ichirin2501's diary

いっちりーん。

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に関しましては、素人同然なので言葉だったり、コードが…いやたぶんコードは大丈夫。言葉があやしい