1. 程式人生 > 實用技巧 >Python網路爬蟲規則之Request庫入門

Python網路爬蟲規則之Request庫入門

  Requests庫是Python的第三方庫,它是目前公認的爬取網頁最好的第三方庫。Requests庫有兩個特點,它很簡單簡單,也很簡潔,甚至用一行程式碼從網頁上獲得相關的資源。Requests庫的更多資訊可以在https://requests.readthedocs.io/en/master/上獲得。

(1).Requests庫的安裝

  在命令列下使用pip命令安裝Requests庫,命令pip install requests。注意:需要將Python目錄和其目錄下的Scripts目錄加到環境變數中。

  安裝完成後,進行一個簡單的測試。啟動Python自帶的IDLE

>>> import requests
>>> r = requests.get("http://www.baidu.com")
>>> r.status_code  #狀態碼
200
>>> r.encoding='utf-8'  #更改編碼為utf-8
>>> r.text  #列印網頁內容
'<!DOCTYPE html>\r\n<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8>
<meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=t
ext/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body
 link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wra
pper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form i
d=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden
 name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hi
dden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=w
d class=s_ipt value maxlength=255 autocomplete=off autofocus></span><span class="bg s_btn_wr"><input type=submi
t id=su value=百度一下 class="bg s_btn"></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com 
name=tj_trnews class=mnav>新聞</a> <a href=http://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href
=http://map.baidu.com name=tj_trmap class=mnav>地圖</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>
視訊</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>貼吧</a> <noscript> <a href=http://www.baidu.
com/bdorz/login.gif?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登入</
a> </noscript> <script>document.write(\'<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u=\'+ encod
eURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ \'" name="tj
_login" class="lb">登入</a>\');</script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="displa
y: block;">更多產品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu
.com>關於百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>©2017 Baidu <a href=http://www.baid
u.com/duty/>使用百度前必讀</a>  <a href=http://jianyi.baidu.com/ class=cp-feedback>意見反饋</a> 京ICP證030173號  <
img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>\r\n'

(2).Requests庫的7個主要方法

方法 說明
requests.request() 構造一個請求,支援以下各個方法的基礎方法
requests.get() 獲取HTML網頁的主要方法,對應於HTTP的GET
requests.head() 獲取HTML網頁頭資訊的方法,對應於HTTP的HEAD
requests.post() 向HTML網頁提交POST請求的方法,對應於HTTP的POST
requests.put() 向HTTP網頁提交PUT請求的方法,對應於HTTP的PUT
requests.patch() 向HTTP網頁提交區域性修改請求,對應於HTTP的PATCH
requests.delete() 向HTTP頁面提交刪除請求,對應於HTTP的DELETE

 1)Requests庫的request()方法

  request()方法是所有方法的基礎方法,它有三個引數method、url和一組控制訪問引數**kwargs。其中,method表示通過request()實現的請求方式,;url指獲取頁面的url連結;**kwargs是13個控制訪問引數。

  method共有7種請求方式,分別是GET/HEAD/POST/PUT/PATCH/delete/OPTIONS。前6種是HTTP協議對應的請求功能,而OPTIONS事實上是向伺服器獲取一些伺服器和客戶端能夠打交道的引數,並不與獲取資源直接相關,因此我們在平時使用中用的比較少。在這7種請求方式中,如果選定了一種,我們可以使用request()直接實現,也可以用Requests庫的對應方法,當然Requests庫的對應方法也是基於request()方法封裝起來的。

  **kwargs共有13個控制訪問引數,均為可選項,使用時必須採用命名方式使用。第一個引數是params,是指能夠增加到url中的字典或位元組序列引數,例項如下:

>>> import requests
>>> kv={'key1':'value1','key2':'value2'}
>>> r=requests.request('GET','http://python123.io/ws',params=kv)
>>> print(r.url)
https://python123.io/ws?key1=value1&key2=value2

  第二個引數data,是指字典、位元組序列或檔案物件,作為Request的內容部分,重點向伺服器提供或提交資源時使用,例項如下:

>>> import requests
>>> kv={'key1':'value1','key2':'value2'}
>>> r=requests.request('POST','http://python123.io/ws',data=kv)

  第三個引數json,是指JSON格式的資料,它也是作為Request的內容部分,可以像伺服器提交,例項如下:

>>> import requests
>>> kv={'key1':'value1'}
>>> r=requests.request('POST','http://python123.io/ws',json=kv)

  第四個引數headers,字典型別,是指向某一個url訪問時所發起的http的頭欄位,簡單說我們可以用這個欄位來定製訪問某一個url的http的協議頭,例項如下:

>>> import requests
>>> hd={'user-agent':'Chrome/10'}
>>> r=requests.request('POST','http://python123.io/ws',headers=hd)

  第五個引數cookies,字典型別或CookieJar,指的是從http協議中解析cookie。

  第六個引數auth,元組型別,它是支援HTTP認證功能。

  第七個引數files,字典型別,顧名思義它是向伺服器傳輸檔案時使用的欄位,例項如下:

>>> import requests
>>> fs={'file':open('data.xls','rb')}
>>> r=requests.request('POST','http://python123.io/ws',files=fs)

  第八個引數timeout,是指設定的超時時間,以秒為單位,例項如下:

>>> import requests
>>> r=requests.request('GET','http://www.baidu.com',timeout=10)

  第九個引數proxies,字典型別,可以為我們爬取網頁設定相關的訪問代理伺服器,可以增加登陸認證,使用這個引數可以有效的隱藏使用者爬取網頁的源IP地址資訊,能夠有效的防止對爬蟲的逆追蹤,例項如下:

>>> import requests
>>> pxs={'http':'http://user:[email protected]:1234','https':'https://10.10.10.1:4321'}
>>> r=requests.request('GET','http://www.baidu.com',proxies=pxs)

  第十個引數allow_redirects,這個引數是一個開關,表示允不允許對URL重定向,預設為True。

  第十一個引數stream,這個引數也是一個開關,表示對獲取的內容是否進行立即下載,預設為True。

  第十二個引數verify,這個引數還是一個開關,表示是否認證SSL證書,預設True。

  第十三個引數cert,是儲存本地SSL證書路徑的欄位。

  理解了request()方法後面的方法就容易理解很多。

 2)Requests庫的get()方法

  get()方法是Requests庫的最常用方法。獲得一個網頁最簡單的一行程式碼就是r=requests.get(url),這裡我們通過給定get()方法和url引數來構造一個向伺服器請求支援的Request物件,這個物件是Requests庫內部生成,另外requests.get()函式返回內容用變數r來表示,這個r是一個包含從伺服器返回的所有相關資源的Response物件。

  requests.get()函式的完整使用方法有三個引數,包括url,params和一組更多的相關引數,具體表現為requests.get(url,params=None,**kwargs)。其中url是獲得頁面的url連結;params指的是在url中增加的額外引數,它可以是字典或位元組流格式,是一個可選引數;**kwargs是12個控制訪問的引數,它也是可選引數,具體可以看request()方法中的解釋。

  如果開啟Requests庫get()方法原始碼,可以看到get()方法實際使用了request()方法來封裝。也就是說Requests庫一共提供了7個常用方法,除了第一個request()方法是基礎方法外,其他的6個方法都是通過呼叫request()方法來實現的。事實上可以認為Requests庫只有一個方法,就是request()方法,但是為了使大家編寫程式更加方便,所以它提供了額外6個方法

  在r=requests.get(url)這一行程式碼中,最重要的兩個物件是Response物件和Request物件,其中獲得網路內容相關的Response物件又是重中之重,它包含了爬蟲返回的全部內容。

>>> import requests
>>> r=requests.get("http://www.baidu.com")
>>> print(r.status_code)  #檢測狀態碼
200
>>> type(r)  #檢測r的型別
<class 'requests.models.Response'>
>>> r.headers  #返回get()請求獲得的頭部資訊
{'Cache-Control': 'private, no-cache, no-store, proxy-revalidate, no-transform', 'Connection': 'keep-alive', 'Content-Encoding': 'gzip',
 'Content-Type': 'text/html', 'Date': 'Tue, 12 May 2020 14:02:02 GMT', 'Last-Modified': 'Mon, 23 Jan 2017 13:27:36 GMT', 'Pragma': 
'no-cache', 'Server': 'bfe/1.0.8.18', 'Set-Cookie': 'BDORZ=27315; max-age=86400; domain=.baidu.com; path=/', 'Transfer-Encoding': 'chunked'}

  Response物件包含了伺服器返回的所有資訊,同時也包含了我們去請求的request()資訊。在Response物件中最常用的有如下五個屬性,這五個屬性是訪問網頁最常用和最必要的屬性。

屬性 說明
r.status_code HTTP請求的返回狀態,200表示連線成功
r.text HTTP響應內容的字串形式,即url對應的網頁內容的字串形式
r.encoding 從HTTP header中猜測的響應內容的編碼方式
r.apparent_encoding 從響應的內容文字中分析出來的編碼方式,是備選編碼方式
r.content HTTP響應內容的二進位制方式,例如圖片

  在使用get()方法獲取網上資源的時候,有一個基本的流程。首先使用r.status_code來檢查返回的Response物件的狀態,如果返回的狀態碼是200,那麼就可以使用r.text,r.encoding,r.apparent_encoding,r.content等屬性去解析返回的內容,如果返回的狀態碼是非200,那麼說明這次url的訪問因為某種原因出錯或產生異常了。

  r.encoding的編碼方式是從HTTP header中的charset欄位獲得,如果HTTP header中有這樣一個欄位,說明訪問的伺服器對資源的編碼是有要求的,而這樣的編碼會獲得回來並存於r.encoding中。但是並不是所有的伺服器對資源編碼都有相關要求,如果HTTP header中不存在charset,則預設將編碼設為ISO-8859-1,這個預設編碼並不能解析中文。所以Requests庫提供了另一個備選編碼,也就是r.apparent_encoding,這個編碼是根據HTTP的內容部分去分析內容中出現文字可能的編碼形式。原則上來說r.apparent_encoding的編碼比r.encoding更加準確,當我們使用r.encoding並不能正確的解碼返回的內容時,我們要用r.apparent_encoding來解碼相關的資訊。

 3)Requests庫的head()方法

  head()方法完整形式是requests.head(url,**kwargs),url是獲得頁面的url連結,**kwargs是13個控制訪問引數。

>>> import requests
>>> r=requests.head('http://httpbin.org/get')
>>> r.headers
{'Date': 'Tue, 04 Aug 2020 12:57:54 GMT', 'Content-Type': 'application/json', 'Content-Length': '305', 'Connection': 'keep-alive',
 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true'}
>>> r.text
''

  requests.head()獲取HTML網頁的頭資訊,可以看到此時的Response物件沒有HTML響應的內容,所以head()方法可以用很少的網路流量來獲取網路資源的概要資訊。

 4)Requests庫的post()方法

  post()方法完整形式是requests.post(url,data=None,json=None,**kwargs),url是更新頁面的url連結,data、json、**kwargs都可以在request()方法中找到,只不過這裡的**kwargs是11個控制訪問引數。

>>> import requests
>>> payload = {'key1': 'value1','key2': 'value2'}
>>> r=requests.post('http://httpbin.org/post',data=payload)
>>> print(r.text)
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "key1": "value1", 
    "key2": "value2"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "23", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.23.0", 
    "X-Amzn-Trace-Id": "Root=1-5f295df4-20801738891519a088c8d7c0"
  }, 
  "json": null, 
  "origin": "157.0.138.63", 
  "url": "http://httpbin.org/post"
}

  當我們想URL資源POST一個字典或鍵值對的時候,那麼鍵值對會預設的被儲存到表單form的欄位下

>>> import requests
>>> r=requests.post('http://httpbin.org/post',data='ABC')
>>> print(r.text)
{
  "args": {}, 
  "data": "ABC", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "3", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.23.0", 
    "X-Amzn-Trace-Id": "Root=1-5f295edb-2286dede6fb188cfadf8063b"
  }, 
  "json": null, 
  "origin": "157.0.138.63", 
  "url": "http://httpbin.org/post"
}

  如果不提交鍵值對,而向URL資源POST一個字串的時候,那麼字串會預設儲存到data欄位。

 5)Requests庫的put()方法

  put()方法完整形式是requests.put(url,data=None,**kwargs),url是更新頁面的url連結,data、**kwargs都可以在request()方法中找到,只不過這裡的**kwargs是12個控制訪問引數。

  put()方法與post()方法類似只是會將原有的欄位覆蓋掉。

>>> import requests
>>> payload={'key1':'value1','key2':'value2'}
>>> r=requests.put('http://httpbin.org/put',data=payload)
>>> print(r.text)
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "key1": "value1", 
    "key2": "value2"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "23", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.23.0", 
    "X-Amzn-Trace-Id": "Root=1-5f296129-77148130765d9350836b3ec0"
  }, 
  "json": null, 
  "origin": "157.0.138.63", 
  "url": "http://httpbin.org/put"
}

 6)Requests庫的patch()方法

  patch()方法完整形式是requests.patch(url,data=None,**kwargs),url是更新頁面的url連結,data、**kwargs都可以在request()方法中找到,只不過這裡的**kwargs是12個控制訪問引數。

 7)Requests庫的delete()方法

  delete()方法完整形式是requests.delete(url,**kwargs),url是刪除頁面的url連結,data、**kwargs都可以在request()方法中找到,只不過這裡的**kwargs是13個控制訪問引數。

(3).爬取網頁的通用程式碼框架

  程式碼框架就是一種將專案開發過程中的共性或通用部分功能進行抽象,提高開發效率,建立更為穩定的程式,並減少開發者重複編寫程式碼的基本架構。進行專案開發時,開發者如果在程式碼框架基礎上進行二次開發,即可大大簡化開發過程,快速實現系統功能。並且能夠幫助初學者建立規範、穩定的專案系統。

  爬取網頁的通用程式碼框架就是一組程式碼,它可以準確的、可靠的爬取網頁的內容。

  我們在用Requests庫訪問網頁的時候,經常用requests.get(url),獲得url的相關內容。但是這樣的語句並不是一定成立的,因為網路連線有風險,所以這樣的語句它的異常處理很重要。Requests庫支援6種如下的常用連線異常:

異常 說明
requests.ConnectionError 網路連線錯誤異常,如DNS查詢失敗、拒絕連線等
requests.HTTPError HTTP錯誤異常
requests.URLRequired URL缺失異常
requests.TooManyRedirects 超過最大重定向次數,產生重定向異常
requests.ConnectTimeout 連線遠端伺服器超時異常
requests.Timeout 請求URL超時,產生超時異常

  Timeout是指發出URL請求到獲得內容整個過程的超時異常,而ConnectTimeout僅指與遠端伺服器連線過程產生的超時異常。

  Response物件返回了所有的網頁內容,它也提供了一個專門與異常打交道的方法r.raise_for_status(),該方法能夠判斷返回的狀態碼,如果狀態碼不是200就會產生一個requests.HTTPError的異常。該方法還被用於爬取網頁的通用程式碼框架中,具體如下:

def getHTMLText(url):  #封裝
    try:
        r=requests.get(url,timeout=30)  #傳送一個url請求
        r.raise_for_status()  #非200狀態碼產生一個HTTPError異常
        r.encoding=r.apparent_encoding
        return r.text
    except:  #處理異常
        return "產生異常"

  通用程式碼框架的最大作用是能夠使得使用者訪問或爬取網頁變得更有效,變得更穩定,變得更可靠。

(4).HTTP協議及Requests庫方法

  為了能夠更好理解Requests的7個主要方法,需要去理解HTTP協議。HTTP協議,英文全稱Hypertext Transfer Protocol,中文全稱超文字傳輸協議,它是一種基於“請求與響應”模式的、無狀態的應用層協議。簡單來說,使用者發起請求,伺服器做相關響應,這就是“請求與響應”的模式。而無狀態指的是第一次請求與第二次請求之間沒有相關的關聯。最後,應用層協議指的是該協議工作在應用層,在TCP協議之上。

  HTTP協議一般採用URL作為定位網路資源的標識,URL格式如下:http://host[:port][path]。每一個URL需要以http://開頭,後面有三個域,host是一個合法的Internet主機域名或IP地址;port指的是埠號,可以省略,預設埠號是80;path指的是請求資源的路徑。由此可以認為,URL是通過HTTP協議存取資源的Internet路徑,一個URL對應一個數據資源。

  HTTP協議對資源有一些操作的功能,最主要的HTTP協議的操作方法一共有6個,而這6個方法對應了Requests庫提供的6個主要函式,具體如下:

方法 說明
GET 請求獲取URL位置的資源
HEAD 請求獲取URL位置資源的頭部資訊
POST 請求向URL位置的資源追加新的資料
PUT 請求向URL位置儲存一個資源,覆蓋原URL位置的資源
PATCH 請求區域性更新URL位置的資源,即改變該處資源的部分內容
DELETE 請求刪除URL位置儲存的資源

  我們可以把網際網路或Internet當成一個雲端,那麼雲端上儲存的所有資源實際上只是使用URL來做相關的描述和標識。如果我們想獲取這個資源,我們可以使用GET或HEAD方法,GET方法獲得全部資源,HEAD方法獲得頭部資訊。如果我們想把自己的資源放到URL對應的位置上,我們可以使用PUT、POST、PATCH方法。如果我們想刪掉這個URL對應的現有資源,我們可以使用DELETE。

  事實上, HTTP協議通過URL對資源做定位,通過這6個常用的方法對資源進行管理,每一次操作都是獨立無狀態的。在HTTP協議的世界裡,網路通道和伺服器都是黑盒子,它能看到的就是URL連結以及對URL的相關操作。

(5).PATCH和PUT的區別

  舉一個例子,假設URL位置有一組資料UserInfo,其中包括UserID,UserName等20個欄位。當用戶修改了UserName而其他不變時,此時採用PATCH僅需要提交UserName的區域性更新請求;但採用PUT則必須將20個欄位一併提交到URL,所有未提交的欄位將被刪除。

  由此可見,PATCH最主要的好處就是節省網路頻寬,當URL對應的資源是很龐大的資源時候,我們只要改其中一個,我們PATCH去修改這一個,而不需要用PUT去重新提交這麼多資源,所以PATCH也是HTTP協議改良後的一個新增指令。