あいつの日誌β

働きながら旅しています。

percent encoding された 日本語 url を decode したい

あらすじ

ある日こんな事を言われました。

Python 2.7 で動いてるプログラムがあるんだけど DB に URL エンコードされた文字列が入っているからデコードしてちょ」

やってみる

以下の参考にして unicode を一度 str に変換して urllib.unquote に渡します。

http://d.hatena.ne.jp/itasuke/20080703/p1

import urllib
url = u'http%3a%2f%2fexample%2ecom%2f%e5%95%86%e5%a3%b2%e7%b9%81%e7%9b%9b'
result = urllib.unquote(url.encode('ascii')).decode('utf8')
print isinstance(result, unicode)
print result

実行してみる

True
http://example.com/商売繁盛

成功しました。任務完了...

バグ発生: 文字化け

http://example.com/�����ɐ� みたいな文字になってるとの報告。

原因: エンコードが utf8 以外だった

というわけで以下のように decode する時に encoding を指定しないといけないっぽい

import urllib
url2 = u'http%3a%2f%2fexample%2ecom%2f%8f%a4%94%84%94%c9%90%b7'
print urllib.unquote(url2.encode('ascii')).decode('shiftjis')

なんだけどすでに DB に格納されているのでどの文字コードか判定できない

妥協案

文字コード推測する

# -*- coding: utf-8 -*-
import urllib


def to_unicode(s, encodings=('ascii', 'utf8', 'shiftjis', 'eucjp')):
    if isinstance(s, unicode):
        return s

    for encoding in encodings:
        try:
            return unicode(s, encoding)
        except UnicodeDecodeError:
            pass

    return unicode(s, 'ascii', 'ignore')


url1 = u'http%3a%2f%2fexample%2ecom%2f%e5%95%86%e5%a3%b2%e7%b9%81%e7%9b%9b'  # utf8
url2 = u'http%3a%2f%2fexample%2ecom%2f%8f%a4%94%84%94%c9%90%b7'  # shift-jis
print to_unicode(urllib.unquote(url1.encode()))
print to_unicode(urllib.unquote(url2.encode()))

実行結果

http://example.com/商売繁盛
http://example.com/商売繁盛

とはいえ、これはあくまで percent encoded url で ascii で表現できる文字しか使われていない状態で DB に保存されているケースなので http://exmaple.com/商売繁盛 をそのまま DB に保存した場合は考慮していません。

思ったこと

ユーザーから入力された URL をどのようにして保存するか、というベストプラクティスを私は知らないのですが、みなさんどうやってるんでしょう?

そもそも URL エンコード文字コードに関する規定がないようなので(つまり自由) そうするとユーザーにどの文字コードで URL エンコードしているのかを知らせてもらわないといけないんでしょうかねえ。

今度誰かに会った時に聞いてみよう(酒の肴にする)