urllib2 でリダイレクトを制御する
もうすぐ高校生活!……めんどい。 という心境の azyobuzin です。 Qiita よりブログのほうが書き心地がいいので、結局プログラミング Tips もブログに流してしまっています。
大事な注意:タイトルからわかるとおり、 Python 2.7 でのお話です。 3.x にもすごく興味がありますが、 2.7 で使うコードなので勘弁してください。
今日のお題
img.azyobuzi.net の作り直しで、リダイレクト先をスマートに取得できないかと思い、いろいろ試行錯誤してみました。
現在の実装
現在稼働中の img.azyobuzi.net では、 Dropbox や Tumblr の短縮 URI の展開時に
import httplib conn = httplib.HTTPConnection("db.tt") conn.request("HEAD", "/" + hoge) res = conn.getresponse() uri = res.getheader("location")
というように、生 HTTPConnection を叩いています。これだと URI を直接突っ込めなくて個人的に直感的ではないと感じます。
つまり
使い慣れた urllib2 でなんとかならないのか??
HEAD でリクエストを送信してみる
urlopen は GET と POST しか対応していませんが、このように Request を上書きすることで他のメソッドにも対応できるようです。
import urllib2 request = urllib2.Request('http://localhost:8080') request.get_method = lambda : 'HEAD'How do you send a HEAD HTTP request in Python? - Stack Overflow
とりあえず、リダイレクトに従わなくさせる
リダイレクトに従わなくさせれば、ヘッダー見るだけでリダイレクト先がわかるよね!!!
build_opener の引数に BaseHandler を継承したクラス or インスタンスを突っ込むと、リクエスト前、レスポンス取得時、エラー時の動作を変えることができます。さらに、既存の Handler を継承したクラス or インスタンスを突っ込むと、既存の Handler は除外されます(がんばってソース読んだ)。つまり、 HTTPRedirectHandler を継承して無効化すればめでたく解決なわけです。
そのコードがこちら
使い方はこんな感じ
>>> import urllib2 >>> from module1 import * >>> opener = urllib2.build_opener(DontRedirectHandler) >>> request = urllib2.Request("http://j.mp/Z1AkTI") >>> request.get_method = lambda: "HEAD" >>> response = opener.open(request) >>> response.code 301 >>> response.url 'http://j.mp/Z1AkTI' >>> dict(response.headers) {'content-length': '114', 'set-cookie': '_bit=51618422-002f9-056a6-2e1cf10a;domain=.j.mp;expires=Fri Oct 4 14:35:14 2013;path=/; HttpOnly', 'server': 'nginx', 'connection': 'close', 'location': 'http://www.google.com/', 'cache-control': 'private; max-age=90', 'date': 'Sun, 07 Apr 2013 14:35:14 GMT', 'content-type': 'text/html; charset=utf-8', 'mime-version': '1.0'} >>> response.read() ''
本当は install_opener するべきでしょうけど、全部のコードでリダイレクト無視されちゃうのは嫌なのでやりません。
次回予告
メソッドを保持したままリダイレクトに従ってみたいです。
予定は未定です。
もっと! urllib2 でリダイレクトを制御する - アジョブジ星通信