urllib2を使用してインターネットリソースを取得する方法
- 著者
- マイケル・フォード
序章
関連記事
Pythonを使用したWebリソースのフェッチに関する次の記事も役立つ場合があります。
-
基本認証のチュートリアルとPythonの例。
urllib2 は、URL(Uniform Resource Locator)をフェッチするためのPythonモジュールです。 urlopen 関数の形式で非常にシンプルなインターフェイスを提供します。 これは、さまざまな異なるプロトコルを使用してURLをフェッチすることができます。 また、基本認証、Cookie、プロキシなどの一般的な状況を処理するための少し複雑なインターフェイスも提供します。 これらは、ハンドラーおよびオープナーと呼ばれるオブジェクトによって提供されます。
urllib2は、関連するネットワークを使用して、多くの「URLスキーム」(URLの":"
の前の文字列で識別されます。たとえば、"ftp"
は"ftp://python.org/%22
のURLスキームです)のURLのフェッチをサポートします。プロトコル(例: FTP、HTTP)。 このチュートリアルでは、最も一般的なケースであるHTTPに焦点を当てます。
単純な状況では、 urlopen は非常に使いやすいです。 ただし、HTTP URLを開くときにエラーや重要なケースが発生するとすぐに、ハイパーテキスト転送プロトコルをある程度理解する必要があります。 HTTPへの最も包括的で信頼できる参照は、 RFC 2616 です。 これは技術文書であり、読みやすくすることを目的としたものではありません。 このHOWTOは、 urllib2 の使用方法を説明することを目的としており、HTTPについて十分に詳しく説明しています。 urllib2 ドキュメントを置き換えることを意図したものではありませんが、それらを補足するものです。
URLの取得
urllib2を使用する最も簡単な方法は次のとおりです。
import urllib2
response = urllib2.urlopen('http://python.org/')
html = response.read()
urllib2の多くの使用法は非常に単純です(「http:」URLの代わりに「ftp:」、「file:」などで始まるURLを使用できた可能性があることに注意してください)。 ただし、このチュートリアルの目的は、HTTPに焦点を当てて、より複雑なケースを説明することです。
HTTPは要求と応答に基づいています。クライアントは要求を行い、サーバーは応答を送信します。 urllib2は、これをRequest
オブジェクトとミラーリングします。これは、作成しているHTTPリクエストを表します。 最も単純な形式では、フェッチするURLを指定するRequestオブジェクトを作成します。 このRequestオブジェクトを使用してurlopen
を呼び出すと、要求されたURLの応答オブジェクトが返されます。 この応答はファイルのようなオブジェクトです。つまり、たとえば、応答に対して.read()
を呼び出すことができます。
import urllib2
req = urllib2.Request('http://www.voidspace.org.uk')
response = urllib2.urlopen(req)
the_page = response.read()
urllib2は、すべてのURLスキームを処理するために同じRequestインターフェースを使用することに注意してください。 たとえば、次のようにFTPリクエストを行うことができます。
req = urllib2.Request('ftp://example.com/')
HTTPの場合、Requestオブジェクトで実行できる追加の機能が2つあります。1つは、サーバーに送信するデータを渡すことができることです。 次に、追加情報(「メタデータ」) about データまたはaboutリクエスト自体をサーバーに渡すことができます。この情報はHTTP「ヘッダー」として送信されます。 これらのそれぞれを順番に見ていきましょう。
データ
URLにデータを送信したい場合があります(多くの場合、URLはCGI(Common Gateway Interface)スクリプト 1 または他のWebアプリケーションを参照します)。 HTTPでは、これは POST リクエストと呼ばれるものを使用して行われることがよくあります。 これは、Webで入力したHTMLフォームを送信するときにブラウザが行うことです。 すべてのPOSTがフォームからのものである必要はありません。POSTを使用して、任意のデータを独自のアプリケーションに送信できます。 HTMLフォームの一般的なケースでは、データを標準的な方法でエンコードしてから、data
引数としてRequestオブジェクトに渡す必要があります。 エンコードは、[X43X] のurllib
ライブラリではなくの関数を使用して行われます。
import urllib
import urllib2
url = 'http://www.someserver.com/cgi-bin/register.cgi'
values = {'name' : 'Michael Foord',
'location' : 'Northampton',
'language' : 'Python' }
data = urllib.urlencode(values)
req = urllib2.Request(url, data)
response = urllib2.urlopen(req)
the_page = response.read()
他のエンコーディングが必要になる場合があることに注意してください(例: HTMLフォームからのファイルアップロードの場合-詳細については、 HTML仕様、フォーム送信を参照してください)。
data
引数を渡さない場合、urllib2は GET 要求を使用します。 GETリクエストとPOSTリクエストが異なる1つの方法は、POSTリクエストには「副作用」があることが多いことです。つまり、何らかの方法でシステムの状態を変更します(たとえば、ハンドレッドウェイトの缶詰スパムを配信するようにWebサイトに注文するなど)。あなたのドアに)。 HTTP標準では、POSTは常に副作用を引き起こすことを意図しており、GET要求は決して副作用を引き起こさないことを明確にしていますが、GET要求が副作用を起こすのを妨げるものは何もありません。 、またはPOSTは、副作用がないことを要求します。 データは、URL自体にエンコードすることで、HTTPGETリクエストで渡すこともできます。
これは次のように行われます。
>>> import urllib2
>>> import urllib
>>> data = {}
>>> data['name'] = 'Somebody Here'
>>> data['location'] = 'Northampton'
>>> data['language'] = 'Python'
>>> url_values = urllib.urlencode(data)
>>> print url_values # The order may differ.
name=Somebody+Here&language=Python&location=Northampton
>>> url = 'http://www.example.com/example.cgi'
>>> full_url = url + '?' + url_values
>>> data = urllib2.urlopen(full_url)
?
をURLに追加し、その後にエンコードされた値を追加することで、完全なURLが作成されることに注意してください。
ヘッダー
ここでは、HTTPリクエストにヘッダーを追加する方法を説明するために、特定のHTTPヘッダーについて説明します。
一部のWebサイト 2 は、プログラムによる閲覧を嫌ったり、異なるバージョンを異なるブラウザーに送信したりします 3 。 デフォルトでは、urllib2はそれ自体をPython-urllib/x.y
として識別します(x
およびy
はPythonリリースのメジャーバージョン番号とマイナーバージョン番号です。例: Python-urllib/2.5
)、これはサイトを混乱させるか、単に機能しない可能性があります。 ブラウザがそれ自体を識別する方法は、User-Agent
ヘッダー 4 を介して行われます。 Requestオブジェクトを作成するときに、ヘッダーのディクショナリをに渡すことができます。 次の例では、上記と同じ要求を行いますが、それ自体をInternet Explorer 5 のバージョンとして識別します。
import urllib
import urllib2
url = 'http://www.someserver.com/cgi-bin/register.cgi'
user_agent = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64)'
values = {'name': 'Michael Foord',
'location': 'Northampton',
'language': 'Python' }
headers = {'User-Agent': user_agent}
data = urllib.urlencode(values)
req = urllib2.Request(url, data, headers)
response = urllib2.urlopen(req)
the_page = response.read()
応答には、2つの便利な方法もあります。 infoとgeturl のセクションを参照してください。これは、問題が発生したときに何が起こるかを確認した後に表示されます。
例外の処理
urlopen は、応答を処理できない場合にURLError
を発生させます(ただし、Python APIでは通常どおり、ValueError
、TypeError
などの組み込みの例外があります)。 発生する可能性もあります)。
HTTPError
は、HTTPURLの特定のケースで発生するURLError
のサブクラスです。
URLError
多くの場合、URLErrorは、ネットワーク接続がない(指定されたサーバーへのルートがない)か、指定されたサーバーが存在しないために発生します。 この場合、発生した例外には「reason」属性があります。これは、エラーコードとテキストエラーメッセージを含むタプルです。
例えば
>>> req = urllib2.Request('http://www.pretend_server.org')
>>> try: urllib2.urlopen(req)
... except urllib2.URLError as e:
... print e.reason
...
(4, 'getaddrinfo failed')
HTTPError
サーバーからのすべてのHTTP応答には、数値の「ステータスコード」が含まれています。 ステータスコードは、サーバーが要求を実行できないことを示している場合があります。 デフォルトのハンドラーは、これらの応答の一部を処理します(たとえば、応答が、クライアントに別のURLからドキュメントをフェッチするように要求する「リダイレクト」である場合、urllib2がそれを処理します)。 処理できない場合、urlopenはHTTPError
を発生させます。 一般的なエラーには、「404」(ページが見つかりません)、「403」(リクエストは禁止されています)、「401」(認証が必要)などがあります。
すべてのHTTPエラーコードのリファレンスについては、RFC2616のセクション10を参照してください。
発生したHTTPError
インスタンスには、サーバーから送信されたエラーに対応する整数の「コード」属性があります。
エラーコード
デフォルトのハンドラーはリダイレクト(300の範囲のコード)を処理し、100〜299の範囲のコードは成功を示すため、通常は400〜599の範囲のエラーコードのみが表示されます。
BaseHTTPServer.BaseHTTPRequestHandler.responses
は、RFC2616で使用されるすべての応答コードを表示する応答コードの便利な辞書です。 辞書は便宜上ここに複製されています
# Table mapping response codes to messages; entries have the
# form {code: (shortmessage, longmessage)}.
responses = {
100: ('Continue', 'Request received, please continue'),
101: ('Switching Protocols',
'Switching to new protocol; obey Upgrade header'),
200: ('OK', 'Request fulfilled, document follows'),
201: ('Created', 'Document created, URL follows'),
202: ('Accepted',
'Request accepted, processing continues off-line'),
203: ('Non-Authoritative Information', 'Request fulfilled from cache'),
204: ('No Content', 'Request fulfilled, nothing follows'),
205: ('Reset Content', 'Clear input form for further input.'),
206: ('Partial Content', 'Partial content follows.'),
300: ('Multiple Choices',
'Object has several resources -- see URI list'),
301: ('Moved Permanently', 'Object moved permanently -- see URI list'),
302: ('Found', 'Object moved temporarily -- see URI list'),
303: ('See Other', 'Object moved -- see Method and URL list'),
304: ('Not Modified',
'Document has not changed since given time'),
305: ('Use Proxy',
'You must use proxy specified in Location to access this '
'resource.'),
307: ('Temporary Redirect',
'Object moved temporarily -- see URI list'),
400: ('Bad Request',
'Bad request syntax or unsupported method'),
401: ('Unauthorized',
'No permission -- see authorization schemes'),
402: ('Payment Required',
'No payment -- see charging schemes'),
403: ('Forbidden',
'Request forbidden -- authorization will not help'),
404: ('Not Found', 'Nothing matches the given URI'),
405: ('Method Not Allowed',
'Specified method is invalid for this server.'),
406: ('Not Acceptable', 'URI not available in preferred format.'),
407: ('Proxy Authentication Required', 'You must authenticate with '
'this proxy before proceeding.'),
408: ('Request Timeout', 'Request timed out; try again later.'),
409: ('Conflict', 'Request conflict.'),
410: ('Gone',
'URI no longer exists and has been permanently removed.'),
411: ('Length Required', 'Client must specify Content-Length.'),
412: ('Precondition Failed', 'Precondition in headers is false.'),
413: ('Request Entity Too Large', 'Entity is too large.'),
414: ('Request-URI Too Long', 'URI is too long.'),
415: ('Unsupported Media Type', 'Entity body in unsupported format.'),
416: ('Requested Range Not Satisfiable',
'Cannot satisfy request range.'),
417: ('Expectation Failed',
'Expect condition could not be satisfied.'),
500: ('Internal Server Error', 'Server got itself in trouble'),
501: ('Not Implemented',
'Server does not support this operation'),
502: ('Bad Gateway', 'Invalid responses from another server/proxy.'),
503: ('Service Unavailable',
'The server cannot process the request due to a high load'),
504: ('Gateway Timeout',
'The gateway server did not receive a timely response'),
505: ('HTTP Version Not Supported', 'Cannot fulfill request.'),
}
エラーが発生すると、サーバーはHTTPエラーコードおよびエラーページを返すことで応答します。 返されたページの応答として、HTTPError
インスタンスを使用できます。 これは、コード属性だけでなく、read、geturl、info、メソッドも含まれていることを意味します。
>>> req = urllib2.Request('http://www.python.org/fish.html')
>>> try:
... urllib2.urlopen(req)
... except urllib2.HTTPError as e:
... print e.code
... print e.read()
...
404
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
...
<title>Page Not Found</title>
...
まとめ
したがって、HTTPError
または URLError
の準備をしたい場合は、2つの基本的なアプローチがあります。 私は2番目のアプローチを好みます。
ナンバー1
from urllib2 import Request, urlopen, URLError, HTTPError
req = Request(someurl)
try:
response = urlopen(req)
except HTTPError as e:
print 'The server couldn\'t fulfill the request.'
print 'Error code: ', e.code
except URLError as e:
print 'We failed to reach a server.'
print 'Reason: ', e.reason
else:
# everything is fine
ノート
except HTTPError
が最初に来る必要があります。そうでない場合、except URLError
はもがHTTPError
をキャッチします。
2番
from urllib2 import Request, urlopen, URLError
req = Request(someurl)
try:
response = urlopen(req)
except URLError as e:
if hasattr(e, 'reason'):
print 'We failed to reach a server.'
print 'Reason: ', e.reason
elif hasattr(e, 'code'):
print 'The server couldn\'t fulfill the request.'
print 'Error code: ', e.code
else:
# everything is fine
情報とgeturl
urlopen(またはHTTPError
インスタンス)によって返される応答には、2つの便利なメソッドinfo()
とgeturl()
があります。
geturl -これは、フェッチされたページの実際のURLを返します。 urlopen
(または使用されるオープナーオブジェクト)がリダイレクトに続いている可能性があるため、これは便利です。 フェッチされたページのURLは、要求されたURLと同じでない場合があります。
info -これは、フェッチされたページ、特にサーバーによって送信されたヘッダーを説明する辞書のようなオブジェクトを返します。 現在、httplib.HTTPMessage
インスタンスです。
一般的なヘッダーには、「Content-length」、「Content-type」などがあります。 HTTPヘッダーの便利なリストとその意味と使用法の簡単な説明については、 HTTPヘッダーのクイックリファレンスを参照してください。
オープナーとハンドラー
URLをフェッチするときは、オープナー(おそらく紛らわしい名前の urllib2.OpenerDirector のインスタンス)を使用します。 通常、urlopen
を介してデフォルトのオープナーを使用していますが、カスタムオープナーを作成することもできます。 オープナーはハンドラーを使用します。 すべての「重い持ち上げ」はハンドラーによって行われます。 各ハンドラーは、特定のURLスキーム(http、ftpなど)のURLを開く方法、またはHTTPリダイレクトやHTTPCookieなどのURL開く側面を処理する方法を知っています。
たとえば、Cookieを処理するオープナーを取得したり、リダイレクトを処理しないオープナーを取得したりするために、特定のハンドラーがインストールされたURLをフェッチする場合は、オープナーを作成する必要があります。
オープナーを作成するには、OpenerDirector
をインスタンス化してから、.add_handler(some_handler_instance)
を繰り返し呼び出します。
または、build_opener
を使用することもできます。これは、1回の関数呼び出しでオープナーオブジェクトを作成するための便利な関数です。 build_opener
はデフォルトでいくつかのハンドラーを追加しますが、さらに追加したり、デフォルトのハンドラーをオーバーライドしたりするための簡単な方法を提供します。
プロキシ、認証、およびその他の一般的ではあるがわずかに特殊な状況を処理できる、他の種類のハンドラー。
install_opener
を使用して、opener
オブジェクトを(グローバル)デフォルトオープナーにすることができます。 これは、urlopen
の呼び出しで、インストールしたオープナーが使用されることを意味します。
オープナーオブジェクトにはopen
メソッドがあり、urlopen
関数と同じ方法でURLをフェッチするために直接呼び出すことができます。利便性を除いて、install_opener
を呼び出す必要はありません。 。
基本認証
ハンドラーの作成とインストールを説明するために、HTTPBasicAuthHandler
を使用します。 基本認証の仕組みの説明を含む、この主題の詳細については、基本認証チュートリアルを参照してください。
認証が必要な場合、サーバーは認証を要求するヘッダー(および401エラーコード)を送信します。 これは、認証スキームと「レルム」を指定します。 ヘッダーは次のようになります:WWW-Authenticate: SCHEME realm="REALM"
。
例えば
WWW-Authenticate: Basic realm="cPanel Users"
次に、クライアントは、リクエストのヘッダーとして含まれているレルムの適切な名前とパスワードを使用して、リクエストを再試行する必要があります。 これが「基本認証」です。 このプロセスを簡素化するために、HTTPBasicAuthHandler
のインスタンスと、このハンドラーを使用するためのオープナーを作成できます。
HTTPBasicAuthHandler
は、パスワードマネージャーと呼ばれるオブジェクトを使用して、URLとレルムのパスワードとユーザー名へのマッピングを処理します。 (サーバーから送信された認証ヘッダーから)レルムが何であるかがわかっている場合は、HTTPPasswordMgr
を使用できます。 多くの場合、レルムが何であるかを気にしません。 その場合は、HTTPPasswordMgrWithDefaultRealm
を使用すると便利です。 これにより、URLのデフォルトのユーザー名とパスワードを指定できます。 これは、特定のレルムに代替の組み合わせを提供する必要がない場合に提供されます。 add_password
メソッドのレルム引数としてNone
を提供することにより、これを示します。
トップレベルURLは、認証を必要とする最初のURLです。 .add_password()に渡すURLよりも「深い」URLも一致します。
# create a password manager
password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
# Add the username and password.
# If we knew the realm, we could use it instead of None.
top_level_url = "http://example.com/foo/"
password_mgr.add_password(None, top_level_url, username, password)
handler = urllib2.HTTPBasicAuthHandler(password_mgr)
# create "opener" (OpenerDirector instance)
opener = urllib2.build_opener(handler)
# use the opener to fetch a URL
opener.open(a_url)
# Install the opener.
# Now all calls to urllib2.urlopen use our opener.
urllib2.install_opener(opener)
ノート
上記の例では、HTTPBasicAuthHandler
をbuild_opener
にのみ提供しました。 デフォルトでは、オープナーには通常の状況のハンドラーがあります– ProxyHandler
( http_proxy
環境変数などのプロキシ設定が設定されている場合)、UnknownHandler
、[ X162X] 、HTTPDefaultErrorHandler
、HTTPRedirectHandler
、FTPHandler
、FileHandler
、HTTPErrorProcessor
。
top_level_url
は、実際にはいずれか完全なURL(「http:」スキームコンポーネントとホスト名、およびオプションでポート番号を含む)です。 "http://example.com/%22
または「権限」(つまり ホスト名、オプションでポート番号を含む)例: "example.com"
または"example.com:8080"
(後者の例にはポート番号が含まれています)。 権限が存在する場合、「userinfo」コンポーネントを含めることはできません。たとえば、"joe:[email protected]"
は正しくありません。
プロキシ
urllib2 はプロキシ設定を自動検出し、それらを使用します。 これは、プロキシ設定が検出されたときの通常のハンドラチェーンの一部であるProxyHandler
を介して行われます。 通常はそれは良いことですが、役に立たない場合もあります 6 。 これを行う1つの方法は、プロキシを定義せずに、独自のProxyHandler
をセットアップすることです。 これは、基本認証ハンドラーの設定と同様の手順を使用して行われます。
>>> proxy_support = urllib2.ProxyHandler({})
>>> opener = urllib2.build_opener(proxy_support)
>>> urllib2.install_opener(opener)
ノート
現在、urllib2
は、プロキシを介したhttps
ロケーションのフェッチをサポートしていません。 ただし、これは、レシピ 7 に示されているようにurllib2を拡張することで有効にできます。
ソケットとレイヤー
WebからリソースをフェッチするためのPythonサポートは階層化されています。 urllib2はhttplibライブラリを使用し、httplibライブラリはソケットライブラリを使用します。
Python 2.3以降、タイムアウトする前にソケットが応答を待機する時間を指定できます。 これは、Webページをフェッチする必要があるアプリケーションで役立ちます。 デフォルトでは、ソケットモジュールにはタイムアウトなしがあり、ハングする可能性があります。 現在、ソケットタイムアウトはhttplibまたはurllib2レベルでは公開されていません。 ただし、を使用して、すべてのソケットに対してデフォルトのタイムアウトをグローバルに設定できます。
import socket
import urllib2
# timeout in seconds
timeout = 10
socket.setdefaulttimeout(timeout)
# this call to urllib2.urlopen now uses the default timeout
# we have set in the socket module
req = urllib2.Request('http://www.voidspace.org.uk')
response = urllib2.urlopen(req)
脚注
このドキュメントは、JohnLeeによってレビューおよび改訂されました。
- 1
- CGIプロトコルの概要については、 PythonでのWebアプリケーションの作成を参照してください。
- 2
- たとえばグーグル。
- 3
- ブラウザのスニッフィングは、Webサイトの設計にとって非常に悪い習慣です。Web標準を使用してサイトを構築する方がはるかに賢明です。 残念ながら、多くのサイトはまだ異なるバージョンを異なるブラウザに送信しています。
- 4
- MSIE6のユーザーエージェントは Mozilla / 4.0(互換、MSIE 6.0、Windows NT 5.1、SV1、.NET CLR 1.1.4322)
- 5
- その他のHTTPリクエストヘッダーの詳細については、 HTTPヘッダーのクイックリファレンスを参照してください。
- 6
- 私の場合、職場のインターネットにアクセスするにはプロキシを使用する必要があります。 このプロキシを介して localhost URLをフェッチしようとすると、それらがブロックされます。 IEは、urllib2が取得するプロキシを使用するように設定されています。 ローカルホストサーバーでスクリプトをテストするには、urllib2がプロキシを使用しないようにする必要があります。
- 7
- SSLプロキシ用のurllib2オープナー(CONNECTメソッド): ASPNクックブックレシピ。