PythonでURLのクエリ文字列(パラメータ)を取得・作成・変更
Pythonの標準ライブラリurllib.parseモジュールを使うと、URLのクエリ文字列(クエリパラメータ)をパースして取得したり、作成したりできる。
なお、urllib.parseモジュールは、Python2ではurlparseモジュールという名前だった。
サードパーティライブラリRequestsを使って辞書からクエリ文字列(パラメータ)を含むURLを作成することもできる。最終的にURLを開いてデータを取得したい場合はRequestsを使うと楽。以下の記事を参照。
- 関連記事: Python, Requestsの使い方
本記事のサンプルコードでは以下のようにurllib.parseモジュールをインポートしている。標準ライブラリなので追加でインストールする必要はない。
import urllib.parse
URLのクエリ文字列(クエリパラメータ)とは
例えば、Googleで「桜」を画像検索した結果のURLは以下の通り。
?
に続くq=%E6%A1%9C&tbm=isch
の部分をクエリ文字列(クエリパラメータ)という。
key1=value1&key2=value2&...
のように、キーと値を=
で連結した要素が&
で連結されている。
上のGoogleの例のクエリ文字列q=%E6%A1%9C&tbm=isch
では、
q=%E6%A1%9C
は検索ワード(q
)が「桜」(%E6%A1%9C
)であることtbm=isch
は検索の種類(tbm
)が画像検索(isch
)であること
を示している。
「桜」のようなURLで使用できない文字は%E6%A1%9C
のようにURLエンコードされる。
以下で説明する関数では自動的にURLエンコード処理が行われる。
URLからクエリ文字列(パラメータ)を抽出・取得
urllib.parse.urlparse()
urllib.parse.urlparse()
でURLを構成要素に分解(パース)できる。
urllib.parse.urlparse()
は名前付きタプルを返し、そのquery
属性にクエリ文字列が格納されている。
url = 'https://example.com?key1=value1&key2=value2'
print(urllib.parse.urlparse(url))
# ParseResult(scheme='https', netloc='example.com', path='', params='', query='key1=value1&key2=value2', fragment='')
qs = urllib.parse.urlparse(url).query
print(qs)
# key1=value1&key2=value2
print(type(qs))
# <class 'str'>
クエリ文字列(パラメータ)を辞書・リストに変換
辞書に変換: urllib.parse.parse_qs()
urllib.parse.parse_qs()
でクエリ文字列を辞書(dict
)に変換できる。
qs = 'key1=value1&key2=value2%201&key2=value2%2F2'
qs_d = urllib.parse.parse_qs(qs)
print(qs_d)
# {'key1': ['value1'], 'key2': ['value2 1', 'value2/2']}
print(type(qs_d))
# <class 'dict'>
辞書の値はリスト。共通のキーの値はまとめられる。キーが一つだけでも要素数が1個のリストとなるので注意。
上の例の%20
(スペース)や%2F
(スラッシュ/
)のようなURLエンコードされた文字は自動的にURLデコードされる。日本語の文字などもURLデコードされる。
リストに変換: urllib.parse.parse_qsl()
urllib.parse.parse_qsl()
でクエリ文字列をリストに変換できる。
qs = 'key1=value1&key2=value2%201&key2=value2%2F2'
qs_l = urllib.parse.parse_qsl(qs)
print(qs_l)
# [('key1', 'value1'), ('key2', 'value2 1'), ('key2', 'value2/2')]
print(type(qs_l))
# <class 'list'>
リストの要素はクエリ文字列のキーと値のペアのタプル。キーが重複する場合も別の要素として扱われる。
URLエンコードされた文字は自動的にURLデコードされる。
辞書・リストからクエリ文字列(パラメータ)を作成
urllib.parse.urlencode()
urllib.parse.urlencode()
で辞書やリストをクエリ文字列に変換できる。
第一引数には辞書を指定できる。
d = {'key1': 'value=1', 'key2': 'value=2'}
d_qs = urllib.parse.urlencode(d)
print(d_qs)
# key1=value%3D1&key2=value%3D2
print(type(d_qs))
# <class 'str'>
第一引数にはリストも指定可能。リストの要素はクエリ文字列のキーと値のペアのタプル(urllib.parse.parse_qsl()
で取得できる形)である必要がある。
l = [('key1', 'value=1'), ('key2', 'value=2')]
l_qs = urllib.parse.urlencode(l)
print(l_qs)
# key1=value%3D1&key2=value%3D2
print(type(l_qs))
# <class 'str'>
上の例の%3D
(=
)のように、URLで使用できない文字は自動的にURLエンコードされる。
URLエンコード処理は次に説明する引数quote_via
, safe
で細かく設定可能。
引数quote_via, safe
デフォルトでは、urllib.parse.urlencode()
のURLエンコードにはurllib.parse.quote_plus()
が使われる。引数quote_via
を指定することで他の関数、例えばurllib.parse.quote()
を使うことが可能。
urllib.parse.quote_plus()
は空白を+
に変換し、urllib.parse.quote()
は空白を%20
に変換する。
d = {'key1': 'value 1', 'key2': 'value/2'}
print(urllib.parse.urlencode(d))
# key1=value+1&key2=value%2F2
print(urllib.parse.urlencode(d, quote_via=urllib.parse.quote))
# key1=value%201&key2=value%2F2
urllib.parse.urlencode()
の引数safe
は引数quote_via
で指定した関数にそのまま渡される。引数safe
に指定した文字はURLエンコードされない。デフォルトはsafe=''
(空文字列)なので、URLで使用できないすべての文字がURLエンコードされる。
print(urllib.parse.urlencode(d, safe='/'))
# key1=value+1&key2=value/2
print(urllib.parse.urlencode(d, safe='/', quote_via=urllib.parse.quote))
# key1=value%201&key2=value/2
URLエンコードについては以下の記事も参照。
引数doseq
上述のように、urllib.parse.parse_qs()
は値がリストの辞書を返す。これをそのままurllib.parse.urlencode()
に渡すと、リストの括弧[]
などを含んだ文字列に変換されてしまう。
qs = 'key1=value1&key2=value2_1&key2=value2_2'
qs_d = urllib.parse.parse_qs(qs)
print(qs_d)
# {'key1': ['value1'], 'key2': ['value2_1', 'value2_2']}
print(urllib.parse.urlencode(qs_d))
# key1=%5B%27value1%27%5D&key2=%5B%27value2_1%27%2C+%27value2_2%27%5D
引数doseq=True
とすると、括弧を無視してリスト内の要素に対して処理される。
print(urllib.parse.urlencode(qs_d, doseq=True))
# key1=value1&key2=value2_1&key2=value2_2
既存のURLのクエリ文字列(パラメータ)を変更
これまで説明した関数を使って既存のURLのクエリ文字列(パラメータ)を変更する方法を示す。
クエリ文字列(パラメータ)を追加・上書き
キーを指定して新たな要素を追加したり、既存の値を上書きしたりする関数の例は以下の通り。
def update_query(url, key, new_val):
pr = urllib.parse.urlparse(url)
d = urllib.parse.parse_qs(pr.query)
d[key] = new_val
return urllib.parse.urlunparse(pr._replace(query=urllib.parse.urlencode(d, doseq=True)))
処理の流れは以下の通り。
urllib.parse.urlparse()
でURLを構成要素に分解(パース)- 抽出したクエリ文字列を
urllib.parse.parse_qs()
で辞書に変換 - 引数で指定した
key
の値をnew_val
で更新 urllib.parse.urlencode()
でクエリ文字列を作成- 元の名前付きタプルの値を
_replace()
メソッドで置き換え urllib.parse.urlunparse()
でURLを再構築
結果は以下のようになる。
url = 'https://example.com?k1=v1&k2=v2_1&k2=v2_2'
print(update_query(url, 'k1', 'v100'))
# https://example.com?k1=v100&k2=v2_1&k2=v2_2
print(update_query(url, 'k2', 'v2'))
# https://example.com?k1=v1&k2=v2
print(update_query(url, 'k2', ['v2_100', 'v2_200']))
# https://example.com?k1=v1&k2=v2_100&k2=v2_200
print(update_query(url, 'k3', 'v3'))
# https://example.com?k1=v1&k2=v2_1&k2=v2_2&k3=v3
クエリ文字列(パラメータ)を削除
キーを指定して削除する関数の例は以下の通り。辞書のpop()
メソッドで要素を削除する。
def remove_query(url, key):
pr = urllib.parse.urlparse(url)
d = urllib.parse.parse_qs(pr.query)
d.pop(key, None)
return urllib.parse.urlunparse(pr._replace(query=urllib.parse.urlencode(d, doseq=True)))
結果は以下のようになる。存在しないキーを指定した場合は変化なし。
url = 'https://example.com?k1=v1&k2=v2_1&k2=v2_2'
print(remove_query(url, 'k1'))
# https://example.com?k2=v2_1&k2=v2_2
print(remove_query(url, 'k2'))
# https://example.com?k1=v1
print(remove_query(url, 'k3'))
# https://example.com?k1=v1&k2=v2_1&k2=v2_2
クエリ文字列をすべて削除する関数の例は以下の通り。
def remove_all_queries(url):
return urllib.parse.urlunparse(urllib.parse.urlparse(url)._replace(query=None))
url = 'https://example.com?k1=v1&k2=v2_1&k2=v2_2'
print(remove_all_queries(url))
# https://example.com