銀月の符号

Python 使い見習いの日記・雑記

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 関連は中途半端、自信の無い状態で放置してしまっているからなぁ。いっちょ本腰入れて学ぶ、いい機会かもしれない。