Urllib2 總結
介紹
Urllib2是用於獲取URLs(統一資源定位符)的一個Python模塊。它以urlopen函數的形式提供了很是簡單的接口。可以使用各類不一樣的協議來獲取網址。它還提供一個稍微複雜的接口用於處理常見的狀況:如基自己份驗證、cookies、proxies(代理)等。這些是由handlers和openers對象提供。
Urllib2使用相關的網絡協議(FTP,http),支持多種獲取URLs的方案(以URL前面的」: 」定義,如:ftp://python.org),這裏主要講最多見的http。
通常狀況下,使用urllib2是很是簡單的。當打開網頁遇到錯誤或異常時,你須要理解HTTP(超文本傳輸協議),最全面最權威HTTP協議可參考RFC2616。本文旨在說明使用urllib2。
獲取URLs
簡單的使用urllib2以下:
import urllib2
response = urllib2.urlopen('http://python.org')
html = response.read()
print html
能夠看到請求的網頁已經被打印出來了。使用urllib2就是如此簡單。
(可以使用'ftp:'、'file'代替'http:')
HTTP是基於請求和響應---客戶端發出請求和服務器端發送響應。Urllib2 對應Request對象表示你作出HTTP請求,最簡單的形式,建立一個指定要獲取的網址的Request對象。這個Request對象調用urlopen,返回URL請求的Response對象。Response對象是一個相似於文件對象,你能夠在Response中使用 .read()。
import urllib2
req = urllib2.Request('http://python.org')
response = urllib2.urlopen(req)
the_page = response.read()
print the_page
urlib2可使用相同Request接口來處理全部URL方案,例如,你能夠建立一個FTP請求:
req = urllib2.Request('ftp://example.com/')
在HTTP協議中,Request對象有兩個額外的事情能夠作,第一,你能夠經過將數據發送到服務器;第二,你能夠經過數據的額外的信息(metadata)或請求到服務器自己,這個信息是發送HTTP'headers'。
Data
有時你想發送一個數據到URL(一般這個URL指向到CGI(公共網關接口)腳本或其餘Web應用程序)。
在HTTP中,常用POST請求。這個一般是瀏覽器作的,當你在你填寫的網站上提交HTML表單。
不是全部的POSTs都使用表單的形式:你可使用POST傳輸任意數據到本身的應用中。在一般的HTML表單中,這些數據須要以標準的形式進行編碼,而後傳遞到Request對象做爲data參數。編碼是經過使用urllib庫而不是urllib2庫。
import urllib
import urllib2
url = 'http://www.someserver.com/cgi-bin/register.cgi'
values = {}
values['name'] = 'Michael Foord'
values['location'] = 'Northampton'
values['language'] = 'Python'
data = urllib.urlencode(values) #數據進行編碼
req = urllib2.Request(url,data) #做爲data參數傳遞到Request對象中
response = urllib2.urlopen(req)
the_page = response.read()
print the_page
若是你不傳遞data參數,urllib2使用GET請求。GET和POST請求不一樣的是,POST請求常常有反作用:它們在某種程度上改變系統的狀態。
儘管HTTP標準有明確的說明POSTs老是引發反作用,而GET請求從不會引發反作用,沒有什麼能夠防止GET請求沒有反作用,POST請求有反作用。
數據也能夠經過在URL自己中HTTP GET請求來編碼。
>>> import urllib2
>>> import urllib
>>> data = {}
>>> data['name'] = 'Somebody Here'
>>> data['location'] = 'Northampton'
>>> data['language'] = 'Python'
>>> url_values = urllib.urlencode(data)
>>> print url_values
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中,其次是編碼的值。
Headers
在這裏討論一個特定的HTTP頭,說明如何添加頭到你的HTTP請求。
一些網站(google)不喜歡由程序來訪問或不一樣的瀏覽器發送不一樣的版本,默認狀況下,urllib2標識本身爲python urllib / x.y(x.y是Python版本號eg : 2.7),這個可能會混淆網站,或只是簡單的不工做。
瀏覽器標識本身的方式是經過用戶代理(User-Agent)頭。當建立一個Request對象時,你能夠經過一個Headers的字典。下面的例子使相同的請求,但將其標識爲一個版本的Internet Explorer。
import urllib
import urllib2
url = 'http://www.someserver.com/cgi-bin/register.cgi'
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
values = {}
values['name'] = 'Michael Foord'
values['location'] = 'Northampton'
values['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()
print the_page
response還有兩個經常使用的方法(info、geturl)。
Handling Exceptions
當urlopen沒法處理響應時將引發URLError(儘管像往常同樣使用Python APIs,內建的異常如ValueError、TypeError等也能夠引發),在特定的HTTP URLs狀況下,HTTPError 是URLError的子類。
URLError
一般,網絡沒有鏈接或沒有路由到指定的服務器或指定的服務器不存在將引發URLError.在這種情形下,異常引起將有個reason屬性,這是一個包含錯誤代碼和一個文本錯誤信息的元組。
import urllib2
req = urllib2.Request('http://www.pretend_server.org')
try:
urllib2.urlopen(req)
except urllib2.URLError,e:
print e.reason
[Errno 11004] getaddrinfo failed
HTTPError
每一個HTTP響應從服務器包含一個數字狀態碼」,有時,這個狀態碼代表服務器沒法知足請求。默認處理程序將處理一些響應(例如:若是響應是一個「重定向」,則要求客戶端從不一樣的網址提取文檔,urllib2將自行處理);對於那些它不能處理的,urlopen將引發HTTPError。典型的錯誤包括404(沒有找到網頁)、403(禁止請求)、401(須要驗證)。
HTTPError 實例提出的將一個整型的’code ‘屬性,對應服務器發出的錯誤Error Codes。由於默認處理程序處理重定向(代碼在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實例爲頁面上的響應中返回。這個也有code屬性,也有read、geturl、info方法。
import urllib2
req = urllib2.Request('http://www.python.org/fish.html')
try:
urllib2.urlopen(req)
except urllib2.URLError,e:
print e.code
print e.read()
print e.geturl()
print e.info()
Wrapping it up(包裝)
若是你想編寫HTTPError或URLError有兩個基本方法。(更喜歡第二種方法)
Number1
同時處理HTTPError和URLError,應該把HTTPError放在URLError前面。因爲HTTPError是URLError的子類,不然URLError也會捕獲一個HTTPError錯誤。
from urllib2 import Request,urlopen,URLError,HTTPError
req = Request('http://www.python.org/fish.html')
try:
response = urlopen(req)
except HTTPError,e:
print 'The server couldn\'t fulfill the request'
print 'Error code:',e.code
except URLError,e:
print 'We failed to reache a server.'
print 'Reason:',e.reason
else:
#everything is fine
response.getcode()
Number 2
因爲HTTPError是URLError的子類,能夠避免導入HTTPError.改進以下:
from urllib2 import Request,urlopen,URLError
req = Request('http://www.python.org/fish.html')
try:
response = urlopen(req)
except URLError,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
response.getcode()
注意,URLError是內置異常IOError的子類,一樣也可避免導入URLError.在某些狀況下,urllib2也可能會引發scoker.error。
from urllib2 import Request,urlopen
req = Request('http://www.python.org/fish.html')
try:
response = urlopen(req)
except IOError,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
response.getcode()
BadStatusLine and HttpException
由一兩種狀況例外,不繼承IOError引發的異常,一種是由httplib模塊定義的BadStatusLine異常,當請求的頁頁面是空白時會引發這個異常。它沒有繼承IOError,而是從HttpException繼承(在httplib中從新定義,直接從Exception中繼承),可能還有其餘的狀況下,這些例外能夠滲入到用戶urllib2。你能夠將這些異常類型,從httplib中直接抓取,或者用一個"catch-all"異常語句來處理任何發生的錯誤。
info and geturl
以urlopen返回的響應(或HTTPError實例)有兩個有用的信息info和方法geturl。
geturl --- 返回抓取頁面的真正的URL。這是很是有用的,由於urlopen可能跟着一個重定向。獲取的頁面的網址可能與請求的地址不相同。
info --- 返回一個描述頁面抓取的字典對象,特別是服務器發送的頭文件。這是當前的一個httplib.httpmessage實例。典型的heade包括'Content-length', 'Content-type'等。
Openers and Handlers
當使用opener(也許是urllib2.openerdirector的命名實例)獲取一個網頁時,一般咱們經過urlopen已經使用默認的opener, 可是咱們能夠建立自定義openers。openers使用處理程序。全部的「重起」是由處理程序完成的。每一個處理程序知道如何爲一個特定的URL方案(HTTP,FTP等)打開網址,或如何處理網址打開的一個方面,例如,HTTP重定向或者HTTP cookies。
若是你想獲取安裝了特定處理程序的網址,須要建立openers。例如,得到opener處理cookies,或者得到opener不處理重定向。
建立一個opener,實例化一個openerdirector,而後反覆調用.add_handle(some_handler_instance)。或者,你可使用build_opener,這是一個方便的功能,用於建立具備一個單一的函數調用opener對象,.build_opener默認狀況下增長了幾個程序。但提供了一個快速的方法來添加更多或重寫默認的處理程序。
其餘類型的處理程序,你可能但願能夠處理proxies(代理)、authenticarion(身份驗證)和其餘常見狀況。
install_opener默認狀況下能夠打開一個opener對象(全局),這意味着調用urlopen將使用已安裝的opener對象。Opener對象有一個open方法,使用urlopen函數直接抓取urls,不須要調用install_opener,除非爲了方便。
Basic Authentication
說明如何建立和安裝處理程序,咱們將使用httpbasicauthhandler。對於這個問題的更詳細的討論,包括對基本認證如何工做的解釋,看看 Basic Authentication Tutorial.
當須要身份驗證時,服務器發送一個頭(以及401個錯誤代碼)請求身份驗證。
這指定的身份驗證scheme(方案)和'realm'(「領域」)。header是這樣的:WWW-Authenticate:SCHEME realm=「REALM」。
e.g.
Www-authenticate: Basic realm="cPanel Users"
而後,客戶端應以適當的名稱和密碼重試請求,以在請求中包含做爲header的域中的適當name和password。這是「基本的身份驗證」爲了簡化這一過程,咱們能夠建立一個httpbasicauthhandler實例和opener使用此處理程序。
httpbasicauthhandler使用對象稱爲一個密碼管理器,來處理URL和服務器密碼用戶名映射,若是你知道這個領域是什麼(從服務器發送的身份驗證頭),你可使用HTTPPasswordMgr。一般不關心的領域是什麼。
在這種狀況下,它使用很方便httppasswordmgrwithdefaultrealm。
這容許您指定一個默認的用戶名和密碼的網址。
咱們代表這個提供None做爲 add_password方法的域參數。頂級的網址是第一個須要認證的網址。網址「deeper」和URL傳遞給.add_password()也將匹配。
# 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。默認狀況下,openers有正常狀況下的處理程序--ProxyHandler,UnknownHandler,HTTPHandler, HTTPDefaultErrorHandler,HTTPRedirectHandler,FTPHandler,FileHandler,HTTPErrorProcessor.
top_level_url其實是一個完整的URL(包括「http」計劃的組成部分和主機名和可選的端口號)e.g. "http://example.com/" 或者 一個"authority"(i.e.主機名,包括主機名)e.g. "example.com" or "example.com:8080"(後者的例子包括一個端口號)這個 authority
若是存在,必定不能包含「userinfo」部分--例如:joe@password:example.com不正確
proxies
urllib2自動檢測代理服務器設置和使用.這是經過proxyhandler是正常的處理程序鏈的一部分.一般狀況下,這是一件好事,但也有一些時候,它可能不會有幫助。一個辦法是創建咱們本身的proxyhandler,沒有代理的定義。這是使用相似的步驟來設置一個基本的身份驗證處理程序:
proxy_support = urllib2.ProxyHandler({})
opener = urllib2.build_opener(proxy_support)
urllib2.install_open(opener)
注意:
當前的urllib2不支持經過proxy抓取https,這個多是個問題.
Sockets and Layers
Python從網絡獲取資源支持分層,urllib2使用httplib的庫,從而使用Socket庫。在Python2.3你能夠指定多少socket,應該等待超時前的反應。這多是有用的應用程序,必須獲取網頁。默認狀況下,socket模塊沒有超時和能夠掛起。
目前,socket超時不暴露httplib或者urllib2。然而,你能夠在全局範圍內爲全部的socket設置默認超時使用。
import socket
import urllib2
# timeout in seconds
timeout = 10
socket.setdefault timeout(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)