簡介:
urllib2是python的一個獲取url(Uniform Resource Locators,統一資源定址器)的模塊。它用urlopen函數的形式提供了一個很是簡潔的接口。這使得用各類各樣的協議獲取url成爲可能。它同時 也提供了一個稍微複雜的接口來處理常見的情況-如基本的認證,cookies,代理,等等。這些都是由叫作opener和handler的對象來處理的。html
如下是獲取url最簡單的方式:python
import urllib2
response = urllib2.urlopen('http://python.org/')
html = response.read()web
許多urlib2的使用都是如此簡單(注意咱們原本也能夠用一個以」ftp:」"file:」等開頭的url取代」HTTP」開頭的url).然 而,這篇教程的目的是解釋關於HTTP更復雜的情形。HTTP建基於請求和迴應(requests &responses )-客戶端製造請求服務器返回迴應。urlib2用代 表了你正在請求的HTTP request的Request對象反映了這些。用它最簡單的形式,你創建了一個Request對象來明確指明你想要獲取的url。調用urlopen函 數對請求的url返回一個respons對象。這個respons是一個像file的對象,這意味着你能用.read()函數操做這個respon對象:瀏覽器
import urllib2服務器
req = urllib2.Request('http://www.voidspace.org.uk')
response = urllib2.urlopen(req)
the_page = response.read()cookie
注意urlib2利用了一樣的Request接口來處理全部的url協議。例如,你能夠像這樣請求一個ftpRequest:網絡
req = urllib2.Request('ftp://example.com/')socket
對於HTTP,Request對象容許你作兩件額外的事:第一,你能夠向服務器發送數據。第二,你能夠向服務器發送額外的信息(metadata),這些信息能夠是關於數據自己的,或者是關於這個請求自己的–這些信息被看成HTTP頭髮送。讓咱們依次看一下這些。函數
數據:
有時你想向一個URL發送數據(一般這些數據是表明一些CGI腳本或者其餘的web應用)。對於HTTP,這一般叫作一個Post。當你發送一個你 在網上填的form(表單)時,這一般是你的瀏覽器所作的。並非全部的Post請求都來自HTML表單,這些數據須要被以標準的方式encode,而後 做爲一個數據參數傳送給Request對象。Encoding是在urlib中完成的,而不是在urlib2中完成的。fetch
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()
若是你不傳送數據參數,urlib2使用了一個GET請求。一個GET請求和POST請求的不一樣之處在於POST請求一般具備邊界效應:它們以某種 方式改變系統的狀態。(例如,經過網頁設置一條指令運送一英擔罐裝牛肉到你家。)雖然HTTP標準清楚的說明Post常常產生邊界效應,而get從不產生 邊界效應,但沒有什麼能阻止一個get請求產生邊界效應,或一個Post請求沒有任何邊界效應。數據也能被url本身加密(Encoding)而後經過一 個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.open(full_url)
頭:
咱們將會在這裏討論一個特殊的HTTP頭,來闡釋怎麼向你的HTTP請求中加入頭。
有一些網站不但願被某些程序瀏覽或者針對不一樣的瀏覽器返回不一樣的版本。默認狀況下,urlib2把本身識別爲Python-urllib/x.y(這裏的 xy是python發行版的主要或次要的版本號,如, Python-urllib/2.5),這些也許會混淆站點,或者徹底不工做。瀏覽器區別自身的方式是經過User-Agent頭。當你創建一個 Request對象時,你能夠加入一個頭字典。接下來的這個例子和上面的請求同樣,不過它把本身定義爲IE的一個版本。
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 = {'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()
Respons一樣有兩種有用的方法。當咱們出差錯以後,看一下關於info and geturl的部分。
異常處理:
不能處理一個respons時,urlopen拋出一個urlerror(雖然像日常同樣對於python APIs,內建異常如,ValueError, TypeError 等也會被拋出。)
HTTPerror是HTTP URL在特別的狀況下被拋出的URLError的一個子類。
urlerror:
一般,urlerror被拋出是由於沒有網絡鏈接(沒有至特定服務器的鏈接)或者特定的服務器不存在。在這種狀況下,含有reason屬性的異常將被拋出,以一種包含錯誤代碼和文本錯誤信息的tuple形式。
e.g.
>>> req = urllib2.Request('http://www.pretend_server.org')
>>> try: urllib2.urlopen(req)
>>> except URLError, e:
>>> print e.reason
>>>
(4, 'getaddrinfo failed')
當一個錯誤被拋出的時候,服務器返回一個HTTP錯誤代碼和一個錯誤頁。你可使用返回的HTTP錯誤示例。這意味着它不但具備code屬性,並且 同時具備read,geturl,和info,methods屬性。>>> req = urllib2.Request('http://www.python.org/fish.html')>>> try:>>> urllib2.urlopen(req)>>> except URLError, e:>>> print e.code>>> print e.read()>>>404...... etc
容錯:
若是你準備處理HTTP錯誤和URL錯誤這裏有兩種基本的方法,我更傾向於後一種:
1.
from urllib2 import Request, urlopen, URLError, HTTPError
req = Request(someurl)
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 reach a server.'
print 'Reason: ', e.reason
else:
# everything is fine
注意:HTTP錯誤異常必須在前面,不然URL錯誤也會捕獲一個HTTP錯誤。
2
from urllib2 import Request, urlopen, URLError
req = Request(someurl)
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
注意:URL錯誤是IO錯誤異常的一個子類。這意味着你能避免引入(import)URL錯誤而使用:
from urllib2 import Request, urlopen
req = Request(someurl)
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
極少數環境下,urllib2可以拋出socket.error.
INFO and GETURL
urlopen返回的response(或者HTTP錯誤實例)有兩個有用的方法:info和geturl。
geturl–它返回被獲取網頁的真正的url。這是頗有用的,由於urlopen(或使用的opener對象)也許會伴隨一個重定向。
獲取的網頁url也許和要求的網頁url不同。
info–它返回一個像字典的對象來描述獲取的網頁,尤爲是服務器發送的頭。它如今通常是httplib.HTTPMessage的一個實例。
典型的頭包含'Content-length', 'Content-type', 等等。看一下Quick Reference to HTTP Headers中,HTTP頭列表,還有
關於他們簡單的解釋和使用方法。
Openers 和Handlers
當你獲取一個URL時,你使用一個opener(一個可能以一個比較迷糊名字命名的實例–urllib2.OpenerDirector)。正常狀況下
咱們一直使用默認的opener,經過urlopen,但你也能夠建立自定義的openers。opener使用操做器(handlers)。全部的重活都交給這些handlers來作。每個handler知道
怎麼打開url以一種獨特的url協議(http,ftp等等),或者怎麼處理打開url的某些方面,如,HTTP重定向,或者HTTP cookie。
你將會建立openers若是你想要用安裝特別的handlers獲取url,例如,獲取一個處理cookie的opener,或者一個不處理重定向的opener。
枚舉一個OpenerDirector,而後屢次調用.add_handler(some_handler_instance)來建立一個opener。
或者,你能夠用build_opener,這是一個很方便的建立opener對象的函數,它只有一個函數調用。build_opener默認會加入許多
handlers,可是提供了一個快速的方法添加更多東西和/或使默認的handler失效。
其餘你想要的handlers可以處理代理,authentication和其餘日常可是又有些特殊的狀況。
install_opener能被用於建立一個opener對象,(全局)默認的opener。這意味着調用urlopen將會用到你剛安裝的opener。
opener對象有一個open方法,它能夠被直接調用來獲取url以一種和urlopen函數一樣的方式:沒有必要調用install_opener,除非是爲了方便。
Basic Authentication:(基本驗證)
爲了解釋建立和安裝一個handler,咱們將會使用 HTTPBasicAuthHandler。更多關於這個東西的內容和詳細討論—包括一個 Basic Authentication如何工做的解說–參見 Basic Authentication Tutorial.
當須要Authentication的時候,服務器發送一個頭(同時還有401代碼)請求Authentication。它詳細指明瞭一個Authentication和一個域。這個頭看起來像:
Www-authenticate: SCHEME realm=」REALM」.
e.g.
Www-authenticate: Basic realm=」cPanel Users」
客戶端而後就會用包含在頭中的正確的賬戶和密碼從新請求這個域。這是」基本驗證」。爲了簡化這個過程,咱們能夠建立一個
HTTPBasicAuthHandler和opener的實例來使用這個handler。
HTTPBasicAuthHandler用一個叫作密碼管理的對象來處理url和用戶名和密碼的域的映射。若是你知道域是什麼(從服務器發送的authentication
頭中),那你就可使用一個HTTPPasswordMgr。多數狀況下人們不在意域是什麼。那樣使用HTTPPasswordMgrWithDefaultRealm就很方便。它
容許你爲一個url具體指定用戶名和密碼。這將會在你沒有爲一個特殊的域提供一個可供選擇的密碼鎖時提供給你。咱們經過提供None做爲add_password方法域的參數指出
這一點。
最高級別的url是須要authentication的第一個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)
注意:在以上的示例中咱們只給build_opener提供了HTTPBasicAuthHandler。默認opener有對普通狀況的操做器 (handlers)- ProxyHandler, UnknownHandler, HTTPHandler, HTTPDefaultErrorHandler, HTTPRedirectHandler, FTPHandler, FileHandler, HTTPErrorProcessor.
高級別url其實是一個完整的url(包括http:協議組件和主機名可選的端口號),如」http://example.com」或者是一個受權(一樣,主機名,可選的端口號)
如」"example.com」 或 「example.com:8080″(後一個示例包含了一個端口號)。受權,若是被呈現,必定不能包含用戶信息-如」oe@password:example.com」
是不正確的、
代理:
urllib2將會自動檢測你的代理設置並使用它們。這是經過 ProxyHandler實現的,它是操做器鏈的一部分。正常狀況下,這是個好東西,可是也有它不那麼有用的偶然狀況。
一個作這些的方法是安裝咱們本身的ProxyHandler,不用任何定義任何代理。使用一個和創建Basic Authentication操做器類似的步驟能夠實現:
>>> proxy_support = urllib2.ProxyHandler({})
>>> opener = urllib2.build_opener(proxy_support)
>>> urllib2.install_opener(opener)
注意:
目前urllib2不支持經過代理獲取HTTPs位置。這是一個問題。
sockets和layers
python支持獲取層疊的網頁的源碼。urllib2使用httplib library,而httplib library反過來使用socket library。
對於python2.3你能夠指明一個socket應該在超時以前等待response多久。這在這些不得不獲取網頁的應用中頗有用。默認socket模塊沒有超時並且可以掛起。
目前,socket超時在urllib2或者httplib水平中不可見。然而,你能夠全局地爲全部socket設置默認的超時。
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)