PythonでURLエンコード・デコード(urllib.parse.quote, unquote)

Modified: | Tags: Python

Pythonの標準ライブラリのurllib.parseモジュールを使うと、文字列のURLエンコード(パーセントエンコード)、および、デコードを行うことができる。日本語を含むURLを処理するのに便利。

urllib.parseモジュールをインポートする。標準ライブラリなので追加のインストールは不要。

import urllib.parse

なお、urllib.parseモジュールはPython2ではurlparseモジュールという名前だった。ここではPython3の場合について以下の内容を説明する。

URLエンコード(パーセントエンコード)とは

URLエンコードは、URLで使用できない日本語(全角文字)やスペースなどの記号を使う際に行われる符号化(エンコード)。%XXの形に変換されるのでパーセントエンコードとも呼ばれる。

例えば、Wikipediaの「日本語」のページは以下のようなURLになっている。

この%E6%97%A5%E6%9C%AC%E8%AA%9Eの部分が「日本語」をURLエンコードした文字列。

%E6%97%A5%E6%9C%AC%E8%AA%9Eを「日本語」に戻すことをURLデコードという。ブラウザのアドレスバーではURLデコードされて表示されている場合が多い。

URLエンコード: urllib.parse.quote()など

urllib.parse.quote()

urllib.parse.quote()でURLエンコードができる。

基本的な使い方

第一引数に文字列(str)を渡すとURLエンコードされた文字列が返される。

s = '日本語'

s_quote = urllib.parse.quote(s)

print(s_quote)
# %E6%97%A5%E6%9C%AC%E8%AA%9E

print(type(s_quote))
# <class 'str'>

バイト列をURLエンコード

第一引数にはバイト列(bytes)を渡すことも可能。

文字列をencode()メソッドでエンコードしたバイト列を例とする。デフォルトではutf-8でエンコードされる。

b = s.encode()

print(b)
# b'\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e'

print(type(b))
# <class 'bytes'>

urllib.parse.quote()の第一引数にバイト列をそのまま渡すと、URLエンコードされた文字列(str)が返される。

print(urllib.parse.quote(b))
# %E6%97%A5%E6%9C%AC%E8%AA%9E

エンコーディングを指定: 引数encoding

第一引数に文字列を指定するとき、引数encodingで非ASCII文字をバイト列にエンコードする際に使われるエンコーディングを指定できる。デフォルトは'utf-8'

文字コードを指定してURLエンコードしたい場合は、この引数encodingを使う。

s_quote_sj = urllib.parse.quote(s, encoding='shift-jis')

print(s_quote_sj)
# %93%FA%96%7B%8C%EA

元の文字列をencode()メソッドで該当のエンコーディングでエンコードしたバイト列を第一引数に指定するのと等価。

b_sj_quote = urllib.parse.quote(s.encode('shift-jis'))

print(b_sj_quote)
# %93%FA%96%7B%8C%EA

print(s_quote_sj == b_sj_quote)
# True

変換しない文字を指定: 引数safe

デフォルトでは半角文字、数字、および-, _, ., ~/は変換されない。

print(urllib.parse.quote('http://x-y_z.com'))
# http%3A//x-y_z.com

引数safeで変換しない文字を指定できる。

デフォルトで/が変換されないのは、引数safeのデフォルト値が'/'だから。空文字列''を指定すると/も変換されるようになる。半角文字、数字、および-, _, ., ~は引数safeによらず常に変換されない。

print(urllib.parse.quote('http://x-y_z.com', safe=''))
# http%3A%2F%2Fx-y_z.com

複数の文字を指定する場合は''の中にすべての文字を記述すればよい。

print(urllib.parse.quote('http://x-y_z.com', safe='/:'))
# http://x-y_z.com

urllib.parse.quote_plus()

urllib.parse.quote_plus()urllib.parse.quote()と共通の引数を持ち、どちらもURLエンコードされた文字列を返す。

違いは空白(スペース)の処理と引数safeのデフォルト値。

urllib.parse.quote()は空白(スペース)を%20に変換し、引数safeのデフォルト値は'/'。一方、urllib.parse.quote_plus()は空白(スペース)を+に変換し、引数safeのデフォルト値は''(空文字列)。

print(urllib.parse.quote('+ /'))
# %2B%20/

print(urllib.parse.quote_plus('+ /'))
# %2B+%2F

print(urllib.parse.quote_plus('+ /', safe='+/'))
# ++/

URLエンコードの活用法

例えば、Wikipedia(日本語版)のURLはhttps://ja.wikipedia.org/wiki/<ページ名>。これはurllib.parse.quote()を使って以下のように作成できる。

page_title = '日本語'

base_ja = 'https://ja.wikipedia.org/wiki/'

print(base_ja + urllib.parse.quote(page_title))
# https://ja.wikipedia.org/wiki/%E6%97%A5%E6%9C%AC%E8%AA%9E

日本語部分(URLエンコードしたい部分)が分割されていないフルのURLの場合は引数safe=':/'とすればよい。デフォルトではコロン:も変換されてしまうので注意。

full_url = 'https://ja.wikipedia.org/wiki/日本語'

print(urllib.parse.quote(full_url, safe=':/'))
# https://ja.wikipedia.org/wiki/%E6%97%A5%E6%9C%AC%E8%AA%9E

WikipediaのURLでは、空白(半角スペース)はアンダースコア_で表されている。例えば「OK コンピューター」のページのURLは以下の通り。

https://ja.wikipedia.org/wiki/OK_%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF%E3%83%BC

urllib.parse.quote()では、空白(半角スペース)は%20に変換される。

print(base_ja + urllib.parse.quote('OK コンピューター'))
# https://ja.wikipedia.org/wiki/OK%20%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF%E3%83%BC

%20を使ったURLでも該当ページにリダイレクトされるので問題はないが、アンダースコアを使ったURLが取得したい場合はreplace()メソッドで空白をアンダースコアに置換しておけばよい。

print(base_ja + urllib.parse.quote('OK コンピューター'.replace(' ', '_')))
# https://ja.wikipedia.org/wiki/OK_%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF%E3%83%BC

なお、英語版でも空白はアンダースコアで表されているが、%20でもリダイレクトされる。

クエリ文字列

例えばGoogleの検索結果のURLは以下のようになっている。

このq=<検索ワード>のような文字列(クエリ文字列)を作成するにはurllib.parse.urlencode()を使うほうが簡単。URLエンコードも処理してくれる。以下の記事を参照。

URLデコード: urllib.parse.unquote()など

urllib.parse.unquote()

urllib.parse.unquote()でURLデコードができる。

基本的な使い方

第一引数に文字列(str)を渡すとURLデコードされた文字列が返される。

print(s_quote)
# %E6%97%A5%E6%9C%AC%E8%AA%9E

print(urllib.parse.unquote(s_quote))
# 日本語

エンコーディングを指定: 引数encoding

URLデコードされたバイト列から文字列にデコードするときに使われるエンコーディングを引数encodingに指定する。デフォルトは'utf-8'

元の文字列をバイト列にエンコードするときにutf-8以外のエンコーディングを使っていた場合は、引数encodingを正しく指定しないと文字化けする。

print(s_quote_sj)
# %93%FA%96%7B%8C%EA

print(urllib.parse.unquote(s_quote_sj))
# ���{��

print(urllib.parse.unquote(s_quote_sj, 'shift-jis'))
# 日本語

urllib.parse.unquote_plus()

urllib.parse.unquote_plus()+を空白に置き換える。それ以外はurllib.parse.unquote()と同じ。

print(urllib.parse.unquote('a+b'))
# a+b

print(urllib.parse.unquote_plus('a+b'))
# a b

urllib.parse.unquote_to_bytes()

URLデコードされたバイト列はurllib.parse.unquote_to_bytes()で取得できる。

このバイト列をdecode()メソッドでデコードするとurllib.parse.unquote()が返す文字列となる。

print(s_quote)
# %E6%97%A5%E6%9C%AC%E8%AA%9E

b_unquote = urllib.parse.unquote_to_bytes(s_quote)

print(b_unquote)
# b'\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e'

print(b_unquote.decode())
# 日本語

utf-8以外のエンコーディングの場合はdecode()の引数でエンコーディングを指定する。

print(s_quote_sj)
# %93%FA%96%7B%8C%EA

b_unquote_sj = urllib.parse.unquote_to_bytes(s_quote_sj)

print(b_unquote_sj)
# b'\x93\xfa\x96{\x8c\xea'

print(b_unquote_sj.decode('shift-jis'))
# 日本語

関連カテゴリー

関連記事