天天看點

《Python爬蟲開發與項目實戰》——3.2 HTTP請求的Python實作

本節書摘來自華章計算機《python爬蟲開發與項目實戰》一書中的第3章,第3.2節,作者:範傳輝著,更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視

  通過上面的網絡爬蟲結構,我們可以看到讀取url、下載下傳網頁是每一個爬蟲必備而且關鍵的功能,這就需要和http請求打交道。接下來講解python中實作http請求的三種方式:urllib2/urllib、httplib/urllib以及requests。

3.2.1 urllib2/urllib實作

  urllib2和urllib是python中的兩個内置子產品,要實作http功能,實作方式是以urllib2為主,urllib為輔。

  1.?首先實作一個完整的請求與響應模型

  urllib2提供一個基礎函數urlopen,通過向指定的url送出請求來擷取資料。最簡單的形式是:

  其實可以将上面對<code>http://www.zhihu.com</code>的請求響應分為兩步,一步是請求,一步是響應,形式如下:

  上面這兩種形式都是get請求,接下來示範一下post請求,其實大同小異,隻是增加了請求資料,這時候用到了urllib。示例如下:

  但是有時會出現這種情況:即使post請求的資料是對的,但是伺服器拒絕你的通路。這是為什麼呢?問題出在請求中的頭資訊,伺服器會檢驗請求頭,來判斷是否是來自浏覽器的通路,這也是反爬蟲的常用手段。

  2.?請求頭headers處理

  将上面的例子改寫一下,加上請求頭資訊,設定一下請求頭中的user-agent域和referer域資訊。

  也可以這樣寫,使用add_header來添加請求頭資訊,修改如下:

  對有些header要特别留意,伺服器會針對這些header做檢查,例如:

user-agent:有些伺服器或proxy會通過該值來判斷是否是浏覽器發出的請求。

content-type:在使用rest接口時,伺服器會檢查該值,用來确定http body中的内容該怎樣解析。在使用伺服器提供的restful或soap服務時,content-type設定錯誤會導緻伺服器拒絕服務。常見的取值有:application/xml(在xml rpc,如restful/soap調用時使用)、application/json(在json rpc調用時使用)、application/x-www-form-urlencoded(浏覽器送出web表單時使用)。

referer:伺服器有時候會檢查防盜鍊。

  3.?cookie處理

  urllib2對cookie的處理也是自動的,使用cookiejar函數進行cookie的管理。如果需要得到某個cookie項的值,可以這麼做:

《Python爬蟲開發與項目實戰》——3.2 HTTP請求的Python實作

  但是有時候會遇到這種情況,我們不想讓urllib2自動處理,我們想自己添加cookie的内容,可以通過設定請求頭中的cookie域來做:

  4.?timeout設定逾時

  在python2.6之前的版本,urllib2的api并沒有暴露timeout的設定,要設定timeout值,隻能更改socket的全局timeout值。示例如下:

  在python2.6及新的版本中,urlopen函數提供了對timeout的設定,示例如下:

《Python爬蟲開發與項目實戰》——3.2 HTTP請求的Python實作

  5.?擷取http響應碼

  對于200 ok來說,隻要使用urlopen傳回的response對象的getcode()方法就可以得到http的傳回碼。但對其他傳回碼來說,urlopen會抛出異常。這時候,就要檢查異常對象的code屬性了,示例如下:

  6.?重定向

  urllib2預設情況下會針對http 3xx傳回碼自動進行重定向動作。要檢測是否發生了重定向動作,隻要檢查一下response的url和request的url是否一緻就可以了,示例如下:

  如果不想自動重定向,可以自定義httpredirecthandler類,示例如下:

  7.?proxy的設定

  在做爬蟲開發中,必不可少地會用到代理。urllib2預設會使用環境變量http_proxy來設定http proxy。但是我們一般不采用這種方式,而是使用proxyhandler在程式中動态設定代理,示例代碼如下:

《Python爬蟲開發與項目實戰》——3.2 HTTP請求的Python實作

  這裡要注意的一個細節,使用urllib2.install_opener()會設定urllib2的全局opener,之後所有的http通路都會使用這個代理。這樣使用會很友善,但不能做更細粒度的控制,比如想在程式中使用兩個不同的proxy設定,這種場景在爬蟲中很常見。比較好的做法是不使用install_opener去更改全局的設定,而隻是直接調用opener的open方法代替全局的urlopen方法,修改如下:

3.2.2 httplib/urllib實作

  httplib子產品是一個底層基礎子產品,可以看到建立http請求的每一步,但是實作的功能比較少,正常情況下比較少用到。在python爬蟲開發中基本上用不到,是以在此隻是進行一下知識普及。下面介紹一下常用的對象和函數:

建立httpconnection對象:class httplib.httpconnection(host[, port[, strict[, timeout[, source_address]]]])。

發送請求:httpconnection.request(method, url[, body[, headers]])。

獲得響應:httpconnection.getresponse()。

讀取響應資訊:httpresponse.read([amt])。

獲得指定頭資訊:httpresponse.getheader(name[, default])。

獲得響應頭(header, value)元組的清單:httpresponse.getheaders()。

獲得底層socket檔案描述符:httpresponse.fileno()。

獲得頭内容:httpresponse.msg。

獲得頭http版本:httpresponse.version。

獲得傳回狀态碼:httpresponse.status。

獲得傳回說明:httpresponse.reason。

  接下來示範一下get請求和post請求的發送,首先是get請求的示例,如下所示:

《Python爬蟲開發與項目實戰》——3.2 HTTP請求的Python實作
《Python爬蟲開發與項目實戰》——3.2 HTTP請求的Python實作

  post請求的示例如下:

3.2.3 更人性化的requests

  python中requests實作http請求的方式,是本人極力推薦的,也是在python爬蟲開發中最為常用的方式。requests實作http請求非常簡單,操作更加人性化。

  requests庫是第三方子產品,需要額外進行安裝。requests是一個開源庫,使用requests庫需要先進行安裝,一般有兩種安裝方式:

使用pip進行安裝,安裝指令為:pip install requests,不過可能不是最新版。

直接到github上下載下傳requests的源代碼,将源代碼壓縮包進行解壓,然後進入解壓後的檔案夾,運作setup.py檔案即可。

  如何驗證requests子產品安裝是否成功呢?在python的shell中輸入import requests,如果不報錯,則是安裝成功。如圖3-5所示。

《Python爬蟲開發與項目實戰》——3.2 HTTP請求的Python實作

  1.?首先還是實作一個完整的請求與響應模型

  以get請求為例,最簡單的形式如下:

  大家可以看到比urllib2實作方式的代碼量少。接下來示範一下post請求,同樣是非常簡短,更加具有python風格。示例如下:

  http中的其他請求方式也可以用requests來實作,示例如下:

  接着講解一下稍微複雜的方式,就是在網址後面緊跟着“?”,“?”後面還有參數。那麼這樣的get請求該如何發送呢?肯定有人會說,直接将完整的url帶入即可,不過requests還提供了其他方式,示例如下:

《Python爬蟲開發與項目實戰》——3.2 HTTP請求的Python實作
《Python爬蟲開發與項目實戰》——3.2 HTTP請求的Python實作

  通過列印結果,我們看到最終的url變成了:

  2.?響應與編碼

  還是從代碼入手,示例如下:

  其中r.content傳回的是位元組形式,r.text傳回的是文本形式,r.encoding傳回的是根據http頭猜測的網頁編碼格式。

  輸出結果中:“text--&gt;”之後的内容在控制台看到的是亂碼,“encoding--&gt;”之後的内容是iso-8859-1(實際上的編碼格式是utf-8),由于requests猜測編碼錯誤,導緻解析文本出現了亂碼。requests提供了解決方案,可以自行設定編碼格式,r.encoding='utf-8'設定成utf-8之後,“new text--&gt;”的内容就不會出現亂碼。但是這種手動的方式略顯笨拙,下面提供一種更加簡便的方式:chardet,這是一個非常優秀的字元串/檔案編碼檢測子產品。安裝方式如下:

  安裝完成後,使用chardet.detect()傳回字典,其中confidence是檢測精确度,encoding是編碼形式。示例如下:

  直接将chardet探測到的編碼,賦給r.encoding實作解碼,r.text輸出就不會有亂碼了。

  除了上面那種直接擷取全部響應的方式,還有一種流模式,示例如下:

《Python爬蟲開發與項目實戰》——3.2 HTTP請求的Python實作

  設定stream=true标志位,使響應以位元組流方式進行讀取,r.raw.read函數指定讀取的位元組數。

  3.?請求頭headers處理

  requests對headers的處理和urllib2非常相似,在requests的get函數中添加headers參數即可。示例如下:

  4.?響應碼code和響應頭headers處理

  擷取響應碼是使用requests中的status_code字段,擷取響應頭使用requests中的headers字段。示例如下:

  上述程式中,r.headers包含所有的響應頭資訊,可以通過get函數擷取其中的某一個字段,也可以通過字典引用的方式擷取字典值,但是不推薦,因為如果字段中沒有這個字段,第二種方式會抛出異常,第一種方式會傳回none。r.raise_for_status()是用來主動地産生一個異常,當響應碼是4xx或5xx時,raise_for_status()函數會抛出異常,而響應碼為200時,raise_for_status()函數傳回none。

  5.?cookie處理

  如果響應中包含cookie的值,可以如下方式擷取cookie字段的值,示例如下:

《Python爬蟲開發與項目實戰》——3.2 HTTP請求的Python實作

  如果想自定義cookie值發送出去,可以使用以下方式,示例如下:

  還有一種更加進階,且能自動處理cookie的方式,有時候我們不需要關心cookie值是多少,隻是希望每次通路的時候,程式自動把cookie的值帶上,像浏覽器一樣。requests提供了一個session的概念,在連續通路網頁,處理登入跳轉時特别友善,不需要關注具體細節。使用方法示例如下:

  上面的這段程式,其實是正式做python開發中遇到的問題,如果沒有第一步通路登入的頁面,而是直接向登入連結發送post請求,系統會把你當做非法使用者,因為通路登入界面時會配置設定一個cookie,需要将這個cookie在發送post請求時帶上,這種使用session函數處理cookie的方式之後會很常用。

  6.?重定向與曆史資訊

  處理重定向隻是需要設定一下allow_redirects字段即可,例如r=requests.get。将allow_redirects設定為true,則是允許重定向;設定為false,則是禁止重定向。如果是允許重定向,可以通過r.history字段檢視曆史資訊,即通路成功之前的所有請求跳轉資訊。示例如下:

  上面的示例代碼顯示的效果是通路github網址時,會将所有的http請求全部重定向為https。

  7.?逾時設定

  逾時選項是通過參數timeout來進行設定的,示例如下:

  8.?代理設定

  使用代理proxy,你可以為任意請求方法通過設定proxies參數來配置單個請求:

  也可以通過環境變量http_proxy和https_proxy?來配置代理,但是在爬蟲開發中不常用。你的代理需要使用http basic auth,可以使用·<code>http://user:password@host/</code>文法:

繼續閱讀