PythonでURLのクエリ文字列(パラメータ)を取得・作成・変更

Modified: | Tags: Python

Pythonの標準ライブラリurllib.parseモジュールを使うと、URLのクエリ文字列(クエリパラメータ)をパースして取得したり、作成したりできる。

なお、urllib.parseモジュールは、Python2ではurlparseモジュールという名前だった。

サードパーティライブラリRequestsを使って辞書からクエリ文字列(パラメータ)を含むURLを作成することもできる。最終的にURLを開いてデータを取得したい場合は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

関連カテゴリー

関連記事