深入理解urllib、urllib2及requests
Python 是一種面向對象、解釋型計算機程序設計語言,由Guido van
Rossum于1989年底發明,第一個公開發行版發行于1991年,Python 源代碼同樣遵循 GPL(GNU General Public
License)協議[1] 。Python語法簡潔而清晰,具有豐富和強大的類庫。
urllib and urllib2 區別
urllib和urllib2模塊都做與請求URL相關的操作,但他們提供不同的功能。
urllib2.urlopen accepts
an instance of the Request class or a url, (whereas urllib.urlopen only
accepts a url
中文意思就是:urllib2.urlopen可以接受一個Request對象或者url,(在接受Request對象時候,并以此可以來設置一個URL
的headers),urllib.urlopen只接收一個url
urllib 有urlencode,urllib2沒有,這也是為什么總是urllib,urllib2常會一起使用的原因r?=?Request(url='http://www.mysite.com')
r.add_header('User-Agent',?'awesome?fetcher')
r.add_data(urllib.urlencode({'foo':?'bar'})
response?=?urllib2.urlopen(r)?????#post?method
urllib 模塊
I. urlencode不能直接處理unicode對象,所以如果是unicode,需要先編碼,有unicode轉到utf8,舉例:urllib.urlencode?(u'bl'.encode('utf-8'))
II. 示例import?urllib???????#sohu?手機主頁
url?=?'http://m.sohu.com/?v=3&_once_=000025_v2tov3&_smuid=\
ICvXXapq5EfTpQTVq6Tpz'
resp?=?urllib.urlopen(url)
page?=?resp.read()
f?=?open('./urllib_index.html',?'w')
f.write(page)
print?dir(resp)
結果:['doc',?'init',?'iter',?'module',?'repr',?'close',?'code',?'fileno',?'fp',?'getcode',?'geturl',?'headers',?'info',?'next',?'read',?'readline',?'readlines',?'url']
print?resp.getcode(),?resp.geturl(),?resp.info(),?resp.headers,?resp.url
#resp.url和resp.geturl()結果一樣
III. 編解碼示例 urllib.quote和urllib.urlencode都是編碼,但用法不一樣48?????s?=?urllib.quote('This?is?python')??#編碼?49?????print?'quote:\t'+s????#空格用%20替代?50?????s_un?=?urllib.unquote(s)????#解碼?51?????print?'unquote:\t'+s_un?52?????s_plus?=?urllib.quote_plus('This?is?python')??#編碼?53?????print?'quote_plus:\t'+s_plus????????????#空格用+替代?54?????s_unplus?=?urllib.unquote_plus(s_plus)???????#解碼?55?????print?'s_unplus:\t'+s_unplus?56?????s_dict?=?{'name':?'dkf',?'pass':?'1234'}?57?????s_encode?=?urllib.urlencode(s_dict)????#編碼字典轉換成url參數
58?????print?'s_encode:\t'+s_encode
結果:quote:?This%20is%20python
unquote:???This?is?python
quote_plus:????This+is+python
s_unplus:??This?is?python
s_encode:??name=dkf&pass=1234
IV. urlretrieve() urlretrieve多數適用單純的只下載的功能或者顯示下載的進度等75?????url?=?'http://m.sohu.com/?v=3&_once_=000025_v2tov3&_\
smuid=ICvXXapq5EfTpQTVq6Tpz'
76?????urllib.urlretrieve(url,?'./retrieve_index.html')
#直接把url鏈接網頁內容下載到retrieve_index.html里了,適用于單純的下載的功能。
#urllib.urlretrieve(url,?local_name,?method)
urllib2
I. urllib2模塊定義的函數和類用來獲取URL(主要是HTTP的),他提供一些復雜的接口用于處理:
基本認證,重定向,Cookies等。 II. 常用方法和類 II.1 urllib2.urlopen(url[, data][,
timeout]) #傳url時候,用法同urllib里的urlopen II.1.1
它打開URL網址,url參數可以是一個字符串url或者是一個Request對象。可選的參數timeout,阻塞操作以秒為單位,如嘗試連接(如果沒
有指定,將使用設置的全局默認timeout值)。實際上這僅適用于HTTP,HTTPS和FTP連接。85?????url?=?'http://m.sohu.com/?v=3&_once_=000025_v2tov3&_\
smuid=ICvXXapq5EfTpQTVq6Tpz'86?????resp?=?urllib2.urlopen(url)87?????page?=?resp.read()
II.1.2
urlopen方法也可通過建立了一個Request對象來明確指明想要獲取的url。調用urlopen函數對請求的url返回一個response對
象。這個response類似于一個file對象,所以用.read()函數可以操作這個response對象url?=?'http://m.sohu.com/?v=3&_once_=000025_v2tov3&_smuid\
=ICvXXapq5EfTpQTVq6Tpz'
req?=?urllib2.Request(url)
resp?=?urllib2.urlopen(req)
page?=?resp.read()
II.2 class urllib2.Request(url[, data][, headers][, originreqhost][, unverifiable])
II.2.1 Request類是一個抽象的URL請求。5個參數的說明如下: II.2.1.1
URL——是一個字符串,其中包含一個有效的URL。 II.2.1.2
data——是一個字符串,指定額外的數據發送到服務器,如果沒有data需要發送可以為“None”。目前使用data的HTTP請求是唯一的。當請求
含有data參數時,HTTP的請求為POST,而不是GET。數據應該是緩存在一個標準的application/x-www-form-
urlencoded格式中。urllib.urlencode()函數用映射或2元組,返回一個這種格式的字符串。通俗的說就是如果想向一個URL發送
數據(通常這些數據是代表一些CGI腳本或者其他的web應用)。例如在網上填的form(表單)時,瀏覽器會POST表單的內容,這些數據需要被以標準
的格式編碼(encode),然后作為一個數據參數傳送給Request對象。Encoding是在urlib模塊中完成的,而不是在urlib2中完成
的。下面是個例子: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)???#send?post
response?=?urllib2.urlopen(req)
page?=?response.read()
II.2.1.3
headers——是字典類型,頭字典可以作為參數在request時直接傳入,也可以把每個鍵和值作為參數調用add_header()方法來添加。作
為辨別瀏覽器身份的User-Agent
header是經常被用來惡搞和偽裝的,因為一些HTTP服務只允許某些請求來自常見的瀏覽器而不是腳本,或是針對不同的瀏覽器返回不同的版本。例
如,Mozilla Firefox瀏覽器被識別為“Mozilla/5.0 (X11; U; Linux i686) Gecko/20071127
Firefox/2.0.0.11”。默認情況下,urlib2把自己識別為Python-urllib/x.y(這里的xy是python發行版的主要
或次要的版本號,如在Python
2.6中,urllib2的默認用戶代理字符串是“Python-urllib/2.6。下面的例子和上面的區別就是在請求時加了一個headers,模
仿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()
標準的headers組成是(Content-Length, Content-Type and
Host),只有在Request對象調用urlopen()(上面的例子也屬于這個情況)或者OpenerDirector.open()時加入。兩種
情況的例子如下:
使用headers參數構造Request對象,如上例在生成Request對象時已經初始化header,而下例是Request對象調用
add_header(key, val)方法附加header(Request對象的方法下面再介紹):import?urllib2
req?=?urllib2.Request('http://www.example.com/')
req.add_header('Referer',?'http://www.python.org/')
#http是無狀態的協議,上一次客戶端的請求與下一次客戶端到服務器的請求無關系的,多數省略這一步
r?=?urllib2.urlopen(req)
OpenerDirector為每一個Request自動加上一個User-Agent header,所以第二種方法如下(urllib2.buildopener會返回一個OpenerDirector對象,關于urllib2.buildopener類下面再說):import?urllib2
opener?=?urllib2.build_opener()
opener.addheaders?=?[('User-agent',?'Mozilla/5.0')]
opener.open('http://www.example.com/')
II.3 urllib2.installopener(opener)和urllib2.buildopener([handler, ...])
installopener和buildopener這兩個方法通常都是在一起用,也有時候buildopener單獨使用來得到OpenerDirector對象。
installopener
實例化會得到OpenerDirector
對象用來賦予全局變量opener。如果想用這個opener來調用urlopen,那么就必須實例化得到OpenerDirector;這樣就可以簡單
的調用OpenerDirector.open()來代替urlopen()。
build_opener實例化也會得到
OpenerDirector對象,其中參數handlers可以被BaseHandler或他的子類實例化。子類中可以通過以下實例
化:ProxyHandler (如果檢測代理設置用)掃描代理會用到,很重要這個, UnknownHandler, HTTPHandler,
HTTPDefaultErrorHandler, HTTPRedirectHandler, FTPHandler, FileHandler,
HTTPErrorProcessor。import?urllib2
req?=?urllib2.Request('http://www.python.org/')
opener=urllib2.build_opener()
urllib2.install_opener(opener)
f?=?opener.open(req)
如上使用 urllib2.install_opener()設置 urllib2 的全局
opener。這樣后面的使用會很方便,但不能做更細粒度的控制,比如想在程序中使用兩個不同的 Proxy 設置等。比較好的做法是不使用
install_opener 去更改全局的設置,而只是直接調用 opener的open 方法代替全局的 urlopen 方法。
說到這Opener和Handler之間的操作聽起來有點暈。整理下思路就清楚了。當獲取一個URL時,可以使用一
個opener(一個urllib2.OpenerDirector實例對象,可以由build_opener實例化生成)。正常情況下程
序一直通過urlopen使用默認的opener(也就是說當你使用urlopen方法時,是在隱式的使用默認的opener
對象),但也可以創建自定義的openers(通過操作 器handlers創建的opener實例)。所有的重活和麻煩
都交給這些handlers來做。每一個handler知道如何以一種特定的協議(http,ftp等等)打開url,或
者如何處理打開url發生的HTTP重定向,或者包含的HTTP cookie。創建openers時如果想要安裝特別的han
dlers來實現獲取url(如獲取一個處理cookie的opener,或者一個不處理重定向的opener)的話,先實例
一個OpenerDirector對象,然后多次調用.add_handler(some_handler_instance)來創建一個opene
r。或者,你可以用build_opener,這是一個很方便的創建opener對象的函數,它只有一個函數調用
。build_opener默認會加入許多handlers,它提供了一個快速的方法添加更多東西和使默認的handler 失效。
install_opener如上所述也能用于創建一個opener對象,但是這個對象是(全局)默認的opener。這意味著調用urlopen
將會用到你剛創建的opener。也就是說上面的代碼可以等同于下面這段。這段代碼最終還是使用的默認opener。一般情況下我們用
build_opener為的是生成自定義opener,沒有必要調用install_opener,除非是為了方便。import?urllib2
req?=?urllib2.Request('http://www.python.org/')
opener=urllib2.build_opener()???????#?創建opener對象
urllib2.install_opener(opener)??????#定義全局默認opener
f?=?urllib2.urlopen(req)??????????#urlopen使用默認opener,但是install_opener
#已經把opener設為全局默認了,這里便是使用上面的建立的opener
III. 異常處理http://www.jb51.net/article/63711.htm當我們調用urllib2.urlopen的時候不會總是這么順利,就像瀏覽器打開url時有時也會報 錯,所以就需要我們有應對異常的處理。說到異常,我們先來了解返回的response對象的 幾個常用的方法:
geturl() — 返回檢索的URL資源,這個是返回的真正url,通常是用來鑒定是否重定向的
info() — 返回頁面的原信息就像一個字段的對象, 如headers,它以mimetools.Message實例為格式(可以參考HTTP Headers說明)。
getcode()
— 返回響應的HTTP狀態代碼,運行下面代碼可以得到code=200
當不能處理一個response時,urlopen拋出一個URLError(對于python APIs,內建異常如,ValueError,
TypeError 等也會被拋出。)HTTPError是HTTP
URL在特別的情況下被拋出的URLError的一個子類。下面就詳細說說URLError和HTTPError。
URLError——handlers當運行出現問題時(通常是因為沒有網絡連接也就是沒有路由到指定的服務器,或在指定的服務器不存在)
HTTPError——HTTPError是URLError的子類。每個來自服務器HTTP的response都包含“status
code”. 有時status code不能處理這個request.
默認的處理程序將處理這些異常的responses。例如,urllib2發現response的URL與你請求的URL不同時也就是發生了重定向時,會
自動處理。對于不能處理的請求, urlopen將拋出 - - - HTTPError異常. 典型的錯誤包含‘404’ (沒有找到頁面),
‘403’ (禁止請求),‘401’ (需要驗證)等。它包含2個重要的屬性reason和code。
程序對于重定向時默認處理的
總結
如果只是單純的下載或者顯示下載進度,不對下載后的內容做處理等,比如下載圖片,css,js文件等,可以用urlilb.urlretrieve()
如果是下載的請求需要填寫表單,輸入賬號,密碼等,建議用urllib2.urlopen(urllib2.Request())
在對字典數據編碼時候,用到的是urllib.urlencode()
requests
I. Requests 使用的是
urllib3,繼承了urllib2的所有特性。Requests支持HTTP連接保持和連接池,支持使用cookie保持會話,支持文件上傳,支持自
動確定響應內容的編碼,支持國際化的 URL 和 POST 數據自動編碼。 II. 舉例:import?requests
...
resp?=?requests.get('http://www.mywebsite.com/user')
userdata?=?{"firstname":?"John",?"lastname":?"Doe",?"password":?"jdoe123"}
resp?=?requests.post('http://www.mywebsite.com/user',?params=userdata)
resp?=?requests.put('http://www.mywebsite.com/user/put')
resp?=?requests.delete('http://www.mywebsite.com/user/delete')
resp.json()???#?假如返回的是json數據
resp.text?????#返回的不是text數據
resp.headers['content-type']??#返回text/html;charset=utf-8
f?=?open('request_index.html',?'w')
f.write(page.encode('utf8'))
#test?發現requests抓下來的頁面必須要編碼\
#寫入,(抓下來的是unicode),urllib和urllib2抓下來可以直接寫入,
#因為這兩者抓下來的page是str
III. 其他功能特性國際化域名和?URLs
Keep-Alive?&?連接池
持久的?Cookie?會話
類瀏覽器式的?SSL?加密認證
基本/摘要式的身份認證
優雅的鍵/值?Cookies
自動解壓
Unicode?編碼的響應體
多段文件上傳
連接超時
支持?.netrc
適用于?Python?2.6—3.4線程安全
IV. requests不是python自帶的庫,需要另外安裝 easy_install or pip install
V. requests缺陷:直接使用不能異步調用,速度慢(from others)。官方的urllib可以替代它。
VI. 個人不建議使用requests模塊
更詳細的相關介紹