あらすじ
ある日こんな事を言われました。
「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 エンコードしているのかを知らせてもらわないといけないんでしょうかねえ。
今度誰かに会った時に聞いてみよう(酒の肴にする)