urllib2 で HEAD 要求
urllib2 モジュールで http プロトコルを使用する際選べる(選ばれる)メソッドは GET か POST 。では HEAD で十分な時は http モジュールレベルまで戻らなくてはいけないのだろうか? もしくは問答無用で GET して対象サーバーにはごめんなさいで済ませるのか? さすがにこれはない、はず。
コード読んでみた。
urllib2.Request.get_method メソッドは has.data() の真偽値を見て GET か POST を返すようになっている。 AbstractHTTPHandler.do_open メソッドはこの戻り値を第1引数にして、 http_class 変数に収まっているインスタンスの request メソッドを実行する。そして、 http_class 変数の中身は… urllib2.HTTPHandler 経由のアクセスの場合、 httplib.HTTPConnection か httplib.HTTPSConnection になる。これらの request メソッドの第1引数はドキュメントを確認すると HTTP 要求メソッド。つまりこれを HEAD にするべきで…、えっと urllib2.Request.get_method をいじるだけでいいのかな。
つまり、こういうことなのか?
import urllib2 class HeadRequest(urllib2.Request): u""" urllib2 にて HEAD リクエストを行うためのクラス。 元コードを読んでいる途中なので、 __init__ メソッド以外はなるべく 使わないように。 self.data に値が入ってしまったときの動作は未調査。 """ def __init__(self, url, data=None, headers={}, origin_req_host=None, unverifiable=False): urllib2.Request.__init__(self, url, None, headers, origin_req_host, unverifiable) def get_method(self): return "HEAD" def add_data(self, data): pass
>>> import urllib2 >>> request = HeadRequest('http://d.hatena.ne.jp/fgshun/') >>> res = urllib2.urlopen(request) >>> for key, value in res.info().items(): ... print key, value ... (中略) last-modified Tue, 21 Oct 2008 13:37:27 GMT connection close date Sat, 25 Oct 2008 03:19:08 GMT content-type text/html; charset=euc-jp >>> res.read() ''
できたっぽい。 info するとヘッダがとれるけれど read すると空文字。期待通り。これで問題ないのかどうか、もうすこし urllib2, httplib モジュールのコードと戯れてみる。
パケットキャプチャ?
実際になにが行われたかを確認するには Wirshark などをつかってパケットみるのが早い? これは今晩の課題に。
パケットキャプチャ
何が起こるのか見てみた。 urllib2.urlopen した時点で送受信が始まっていることと、上記の HeadRequest でちゃんと HEAD リクエストが飛んでいることを確認。やはり read() した時点で始めて通信開始というわけではなく urlopen した時点で通信が始まっている。しかしデータサイズが大きいものが対象だと read() するまで、すべてをもってくることは無い模様。これらの挙動をよりくわしく知るためには… socket._fileobject クラスまでさかのぼる必要がある? 手ごわいかも。
結論。 info() でヘッダをみるだけのために通常の方法で urllib2.urlopen (つまり GET や POST )しても悲惨な通信量にはならなさそう。とはいえ、ヘッダしかいらないのなら HEAD リクエストするのが筋ではある。
socket プログラミングかぁ
今日、 urllib2 から実装を調べるためさかのぼっていったら httplib の裏側にある socket モジュールに到着した。 socket 関連は中途半端、自信の無い状態で放置してしまっているからなぁ。いっちょ本腰入れて学ぶ、いい機会かもしれない。