Python9分で読めます

PythonでURLエンコードする方法(urllib.parse 完全ガイド)

PythonのURLエンコードでは、文字列のパーセントエンコーディングに urllib.parse.quote() を、辞書をクエリ文字列にエンコードするのに urllib.parse.urlencode() を使います。本ガイドでは quote()、unquote()、urlencode()、parse_qs() を実践的な例とともに解説します。

quote() によるURLエンコード

urllib.parse.quote() 関数は、文字列をパーセントエンコードするためのPythonの中心的なツールです。URLで安全に使えない文字を、対応するパーセントエンコード表現に変換します。デフォルトではスラッシュ(/)は安全な文字として扱われますが、この挙動はカスタマイズできます。

from urllib.parse import quote

# 基本的なエンコード
print(quote('hello world'))
# 出力: hello%20world

# 特殊文字のエンコード
print(quote('price=10&qty=2'))
# 出力: price%3D10%26qty%3D2

# デフォルトでは / はエンコードされない
print(quote('path/to/file'))
# 出力: path/to/file

# スラッシュもエンコードするには safe='' を指定する
print(quote('path/to/file', safe=''))
# 出力: path%2Fto%2Ffile

# Unicode文字のエンコード
print(quote('cafe'))
# 出力: caf%C3%A9

# 追加で安全とみなす文字を指定する
print(quote('key=value&foo=bar', safe='=&'))
# 出力: key=value&foo=bar

何をエンコードするかを制御する鍵となるのが safe パラメータです。デフォルトは safe='/' です。英数字と _.-~ 以外のすべてをエンコードしたい場合は safe='' を指定します。これはJavaScriptの encodeURIComponent() と同等です。

また quote_plus() もあり、quote() と同じように動作しますが、スペースを %20 ではなく + にエンコードします。これはHTMLフォームデータ(application/x-www-form-urlencoded)で使われる形式です。

from urllib.parse import quote_plus

print(quote_plus('hello world'))
# 出力: hello+world

print(quote_plus('key=value&name=John Doe'))
# 出力: key%3Dvalue%26name%3DJohn+Doe

unquote() によるURLデコード

urllib.parse.unquote() 関数はパーセントエンコーディングを元に戻し、%XX のシーケンスを本来の文字へ変換します。さらに + 記号をスペースに変換する unquote_plus() もあります。

from urllib.parse import unquote, unquote_plus

# 基本的なデコード
print(unquote('hello%20world'))
# 出力: hello world

print(unquote('caf%C3%A9'))
# 出力: cafe(アクセント付き)

# unquote は + をスペースに変換しない
print(unquote('hello+world'))
# 出力: hello+world

# unquote_plus は + をスペースに変換する
print(unquote_plus('hello+world'))
# 出力: hello world

# URL全体のデコード
url = 'https://example.com/search?q=C%2B%2B%20programming'
print(unquote(url))
# 出力: https://example.com/search?q=C++ programming

フォームデータをデコードするときは、HTMLフォームがスペースを + としてエンコードするため、必ず unquote_plus() を使いましょう。スペースが %20 としてエンコードされる一般的なURLのデコードには unquote() を使います。

urlencode() によるクエリ文字列のエンコード

urllib.parse.urlencode() 関数は、辞書やタプルのリストを受け取り、正しく整形されたクエリ文字列に変換します。Pythonでクエリ文字列を組み立てる最も手軽な方法です。

from urllib.parse import urlencode

# 辞書からクエリ文字列へ
params = {
    'q': 'python programming',
    'page': 1,
    'lang': 'en'
}
print(urlencode(params))
# 出力: q=python+programming&page=1&lang=en

# タプルのリスト(順序を保持し、キーの重複を許容する)
params = [
    ('tag', 'python'),
    ('tag', 'web'),
    ('sort', 'date')
]
print(urlencode(params))
# 出力: tag=python&tag=web&sort=date

# リスト値には doseq=True を使う
params = {
    'tag': ['python', 'web', 'api'],
    'sort': 'date'
}
print(urlencode(params, doseq=True))
# 出力: tag=python&tag=web&tag=api&sort=date

# quote_via でスペースのエンコード方法を制御する
from urllib.parse import quote
params = {'q': 'hello world'}
print(urlencode(params, quote_via=quote))
# 出力: q=hello%20world  (+ の代わりに %20 を使う)

urlencode() は内部でデフォルトとして quote_plus() を使うため、スペースは + になります。スペースに %20 が必要な場合は、上記のように quote_via=quote を渡します。

parse_qs() によるクエリ文字列の解析

urllib.parse.parse_qs() 関数は、クエリ文字列を辞書へと解析し直します。クエリパラメータは複数の値を持てるため、辞書の各値はリストになります。タプルのリストを返す parse_qsl() もあります。

from urllib.parse import parse_qs, parse_qsl

# クエリ文字列を辞書へ解析する
qs = 'q=python+programming&page=1&lang=en'
result = parse_qs(qs)
print(result)
# 出力: {'q': ['python programming'], 'page': ['1'], 'lang': ['en']}

# 注意: 値は常にリストになる
print(result['q'][0])  # 'python programming'

# 同じキーに対する複数の値を扱う
qs = 'tag=python&tag=web&tag=api'
result = parse_qs(qs)
print(result)
# 出力: {'tag': ['python', 'web', 'api']}

# parse_qsl はタプルのリストを返す
result = parse_qsl(qs)
print(result)
# 出力: [('tag', 'python'), ('tag', 'web'), ('tag', 'api')]

# 空の値を保持する(デフォルトでは省略される)
qs = 'name=John&email=&age=30'
print(parse_qs(qs, keep_blank_values=True))
# 出力: {'name': ['John'], 'email': [''], 'age': ['30']}

urlparse による完全なURLのエンコード

完全なURLを扱うときは、Pythonの urlparse()urlunparse() 関数を使うと、URLを安全に分解・再構築できます。これは、URLの構造を壊すことなく特定の部分だけを変更したい場合に特に便利です。

from urllib.parse import urlparse, urlunparse, urlencode, quote

# URLを各コンポーネントに分解する
url = 'https://example.com/search?q=hello&page=1#results'
parsed = urlparse(url)
print(parsed.scheme)    # 'https'
print(parsed.netloc)    # 'example.com'
print(parsed.path)      # '/search'
print(parsed.query)     # 'q=hello&page=1'
print(parsed.fragment)  # 'results'

# 各コンポーネントからURLを組み立てる
from urllib.parse import ParseResult
new_url = urlunparse(ParseResult(
    scheme='https',
    netloc='api.example.com',
    path='/v2/search',
    params='',
    query=urlencode({'q': 'python & java', 'limit': 10}),
    fragment=''
))
print(new_url)
# 出力: https://api.example.com/v2/search?q=python+%26+java&limit=10

# 特殊文字を含むパスセグメントを安全に追加する
base = 'https://example.com/files/'
filename = 'my report (final).pdf'
safe_url = base + quote(filename, safe='')
print(safe_url)
# 出力: https://example.com/files/my%20report%20%28final%29.pdf

現代的なPythonコードでは、requests ライブラリの利用を検討してください。パラメータを辞書として渡すと、URLエンコードを自動的に処理してくれます。httpx ライブラリも同様の自動エンコード機能を備えています。

import requests

# requests はエンコードを自動的に処理する
response = requests.get(
    'https://api.example.com/search',
    params={
        'q': 'python & java',
        'page': 1,
        'sort': 'relevance'
    }
)
print(response.url)
# https://api.example.com/search?q=python+%26+java&page=1&sort=relevance

関連記事

無料ツールを試す