新urllib.py
[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)