{{category Python}} !!! Python 3のurllib !! urllib.request Python 3のurllibでは、オブジェクトやメソッドが定義されている名前空間がurllibではなくて、urllib.request以下になった。 import urllib.request opener = urllib.request.build_opener() response = opener.open("http://www.foobar.com") print(response.read()) urllib.request.build_opener([handlers...,])は、urllib.request.OpenerDirectorのインスタンスを返す。 これは、さまざまなHTTPクライアント機能を持ち、openメソッドを持ったオブジェクトである(機能は引数にハンドラオブジェクトを渡して作成する)。 urllib.request.OpenerDirector.open(url, [data, timeout])は、urllib2.urlopen(url, [data, timeout])と同じ引数を取る。キーワード引数でも指定可能。 したがって、違和感なく使える。 urllib.request.OpenerDirector.open(...)が返すのは、http.client.HTTPResponseオブジェクト。 これはファイルハンドラのようにしてコンテンツを読み出せる。 !! Request / Response URL Openerはデフォルトのものが用意されている。 もっと直感的にHTTPのRequest / Responseを実現するには、Requestオブジェクトを使う。 import urllib.request url_request = urllib.request.Request(url="http://www.yahoo.co.jp") http_response = urllib.request.urlopen(url_request) binary_contents = http_response.read() POSTはこんな感じ。 import urllib.request PostHeaders = {"Content-Type" : "application/octet-stream"} post_data_handle = open("C:\post.jpg", "rb") post_request = urllib.request.Request(url="http://post.intranet.com/image", data=post_data_handle.read(), method="POST", headers=PostHeaders) post_response = urllib.request.urlopen(post_request) !! charsetを得ようとする場合 import urllib.request content_charset = "" url_request = urllib.request.Request(url="http://www.yahoo.co.jp") http_response = urllib.request.urlopen(url_request) content_charset = http_response.info().get_content_charset() if content_charset: print(http_response.read().decode(content_charset)) http.client.HTTPResponse.info()が返すのは、http.client.HTTPMessageオブジェクト。これを使って、戻ってきたHTTPヘッダ内の情報を取り出せる。 http.client.HTTPMessage.get_charsets()はcharsetを表す文字列のリストを返す。HTTPの場合は、取得したページのcharsetとして0番目の値だけを参照すればよい、とのこと(メール等のマルチパートMIMEに対応できるように、リストで返す仕様にしてあるらしい)。 charsetが無い場合には[None](唯一の値がNoneであるリスト)が帰ってくるので注意。リストの要素が文字列だという前提でコーディングしていると、例外が発生する。 !! proxyを使いたい場合 ハンドラオブジェクトを作って渡す。 カスタムのハンドラオブジェクトを使いたい場合には、build_openerを使って、カスタムのURL Openerを作成する必要がある。 import urllib.request opener = urllib.request.build_opener(urllib.request.ProxyHandler({"http": "192.168.1.16:8080"})) response = opener.open("http://www.foobar.com", timeout=10) こりゃ簡単だ。 !!! 2.6向けの旧記事 最近の urlopen では、タイムアウト値を設定できるようになったようだ。 少なくとも 2.6 の urllib2 ではできるようになっている。 urlopen(url[, data][, timeout]) dataは、リクエスト以外に追加でサーバーに送りたい文字列がある場合、とのこと。 timeoutは秒数で指定する。 dataは普通指定しない。その場合はNoneでいいので、以下のようになる。 import urllib2 result = urllib2.urlopen("http://www.yahoo.co.jp", None, 30)