python :: urllib2 でhttps通信したときのめも
軽くめも、相変わらずの語尾の不安定さ。
以下の動作は全て python2.7 で行いました。
自動でCookie処理をさせる
デフォルトではCookie処理が自動でされないらしい
例えば以下のようにCookie用のhandlerを追加することで解決できる
import urllib2 import cookielib opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookielib.CookieJar())) urllib2.install_opener(opener)
build_openerを探る
build_openerは OpenerDirector のインスタンスです。
build_openerに引数として渡したhandlerというものが何なのかよくわかりませんでした。
Cookie処理のhandlerが設定されてないから追加する、というのはわかったけど
じゃあ、python2.7でデフォルトのhandlerは何が設定されているのか?
urllib2.pyのソースの build_opener を見てみると、
opener = OpenerDirector() default_classes = [ProxyHandler, UnknownHandler, HTTPHandler, HTTPDefaultErrorHandler, HTTPRedirectHandler, FTPHandler, FileHandler, HTTPErrorProcessor] if hasattr(httplib, 'HTTPS'): default_classes.append(HTTPSHandler)
となっています。
プログラム動作中で、現在設定されてるhandlerを調べたいときは
print opener.handlers
とかで出力することが出来ます。
build_openerが返すのは OpenerDirector のインスタンスなので、
urllib2.pyのソースから引用
class OpenerDirector: def __init__(self): client_version = "Python-urllib/%s" % __version__ self.addheaders = [('User-agent', client_version)] # manage the individual handlers self.handlers = [] self.handle_open = {} self.handle_error = {} self.process_response = {} self.process_request = {}
これらのメンバ変数(言葉合ってる?)も使えます。
User-agentにはデフォルトでpythonのバージョンが格納されるようです。
install_openerを探る
これで出来るよって言われても、内部で何がどうなってるかわからないと気持ち悪いのでソースを見てみた。
_opener = None def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT): global _opener if _opener is None: _opener = build_opener() return _opener.open(url, data, timeout) def install_opener(opener): global _opener _opener = opener
ソースコードがドキュメントである。
グローバルに宣言されてる _opener に引数で渡した OpenerDirectorインスタンスが代入されてる
urllib2.urlopenと、OpenerDirector.openが同じというのもよくわかる
urllib2を使ってhttps通信してるパケットのヘッダが見たい
今までSSLとかに触れたことがなくて wireshark だけで済ませてたのだけど、暗号化されててわからない。
ということで、hatenaのログインページを題材に確かめてみます。
#!/usr/bin/python # -*- coding:utf-8 -*- import urllib import urllib2 import cookielib # http is urllib2.HTTPHandler(debuglevel=1) opener = urllib2.build_opener(urllib2.HTTPSHandler(debuglevel=1), urllib2.HTTPCookieProcessor(cookielib.CookieJar())) urllib2.install_opener(opener) # login url = 'https://www.hatena.ne.jp/login' login_post = {'name':'ichirin2501', 'password':'******'} param = urllib.urlencode(login_post) header = { 'User-Agent' : 'Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.205 Safari/534.16' } req = urllib2.Request(url, param, header) res = urllib2.urlopen(req)
https通信は urllib2.HTTP2Handler を使う必要があります
通常のhttp通信には、 urllib2.HTTPHandler を指定します
出力
※一部は伏せ字 [ichirin@ichirin python]$ python test.py send: 'POST /login HTTP/1.1\r\nAccept-Encoding: identity\r\nContent-Length: 42\r\nHost: www.hatena.ne.jp\r\nContent-Type: application/x-www-form-urlencoded\r\nConnection: close\r\nUser-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.205 Safari/534.16\r\n\r\npassword=*******&name=ichirin2501' reply: 'HTTP/1.1 200 OK\r\n' header: Date: Wed, 27 Apr 2011 17:07:18 GMT header: Server: Apache/2.2.3 (CentOS) header: Cache-Control: no-cache header: Pragma: no-cache header: Vary: Accept-Language,Accept-Encoding,User-Agent header: P3p: CP="OTI CUR OUR BUS STA" header: X-Framework: Ridge/0.1 header: X-Ridge-Dispatch: Hatena::WWW::Engine::Login#default header: X-Runtime: 51ms header: X-View-Runtime: 13ms header: X-Welcome: ichirin2501 header: Content-Length: 3295 header: Content-Type: text/html; charset=utf-8 header: Set-Cookie: b=******; path=/; expires=Tue, 22-Apr-31 17:07:18 GMT; domain=.hatena.ne.jp header: Set-Cookie: rk=******; domain=.hatena.ne.jp; path=/ header: Connection: close [ichirin@ichirin python]$
Responceの出力形式は良いけれど、Requestの出力はなんとかならんのか…
一応、Request,Responceともに出力されています。
最初は気持ち悪かったけど、内部の仕組みが解ってくると非常に使いやすい
上記のデバッグ出力はリダイレクトの処理も出力することも確認
pythonに関しましては、素人同然なので言葉だったり、コードが…いやたぶんコードは大丈夫。言葉があやしい