Python爬蟲十六式 - 第三式:Requests的用法

Connor_Zhang發表於2019-01-09

Requests: 讓 HTTP 服務人類

學習一時爽,一直學習一直爽

  Hello,大家好,我是Connor,一個從無到有的技術小白。今天我們繼續來說我們的 Python 爬蟲,上一次我們說到了 urlliburllib3 ,不知道大家看了以後有何感想,今天我們來繼續聊聊 Python爬蟲中的另一個常用庫——requests,相信你今天看了這篇文章以後一定有想要揍我的衝動。

Python爬蟲十六式 - 第三式:Requests的用法

1.request 的簡介

  上一篇文章介紹了Python的網路請求庫 urlliburllib3 的使用方法,那麼,作為同樣是網路請求庫的 Requests ,相較於 urlliburllib3,它有哪些優勢呢?

Requests 唯一的一個非轉基因的 Python HTTP 庫,人類可以安全享用。
警告:非專業使用其他 HTTP 庫會導致危險的副作用,包括:安全缺陷症、冗餘程式碼症、重新發明輪子症、啃文件症、抑鬱、頭疼、甚至死亡。
                                                               ——requests官方文件

  Emmn.... 官方吐槽最為致命,從requests的話中,你就可以看得出它是多麼的偉大了。

  requests 還是少有的被官方推薦的庫,在 urllib.request 的文件中,有這樣的一句話來推薦 requests :

The Requests packageis recommended for a higher-level HTTP client interface.

  urllib 做為Python的標準庫,因為歷史原因,使用的方式可以說是非常的麻煩而複雜的,而且官方文件也十分的簡陋,常常需要去檢視原始碼。與之相反的是,Requests的使用方式非常的簡單、直觀、人性化,讓程式設計師的精力完全從庫的使用中解放出來。

  吹牛皮吹了這麼久,那麼requests庫究竟可以強大到什麼地方呢?Twitter、Spotify、Microsoft、Amazon、Lyft、BuzzFeed、Reddit、NASA、Amazon、Google、Twilio、Mozilla、Heroku、PayPal、NPR、Obama for America、Transifex、Native Instruments、Washington Post、Twitter、SoundCloud、Kippt、Readability、以及若干不願公開身份的聯邦政府機構都在內部使用。

  怎麼樣,是不是有了一種想要打我的衝動了?這麼好的庫居然不早點拿出來說,藏著掖著的...其實 requests 是非常少有的配備有官方中文文件的第三方庫,如果有需要的話你也可以去看看他們的官方文件 點我走起>>>


2.requests 的使用

  百度五分鐘解決問題,群裡吹牛逼倆小時,哈哈哈,貌似今天你百度也要跟著吹牛兩小時了,下面我們說點正經了,看看requests到底應該怎麼使用吧:

2.1 requests 的簡單使用

  如果你想使用request訪問一個頁面的話,那麼你可以直接這樣:

In [1]: import requests
    
In [2]: resp = requests.get('https://www.baidu.com')

In [3]: resp.status_code
Out[3]: 200

In [4]: resp.encoding
Out[4]: 'ISO-8859-1'

In [5]: resp.headers['Date']
Out[5]: 'Mon, 07 Jan 2019 07:30:15 GMT'
    
複製程式碼

  可以看到,無論是發起請求還是響應處理,都是非常直觀明瞭的。requests目前可以滿足所有的請求需求,所以相較 urlliburllib3 而言,大家還是更加喜歡使用 requests 庫來發起訪問。


2.2 requests 的安裝

  requests 的安裝方式與所有的第三方庫的安裝方式都是相同的,你可以直接用 pip 進行安裝

pip install requests
複製程式碼

2.3 requests 的使用

   requests的請求不再像urllib一樣需要去構造各種Request、opener和handler,使用requests構造的方法,並在其中傳入需要的引數即可。

2.3.1 發起請求

  在 reqeuest 模組中,每一種method方法都有對應的封裝好的函式供你使用,比如說,進行GET請求的時候,你可以直接使用 get() 方法:

In [1]: import requests

In [2]: resp = requests.get('https://www.baidu.com')
複製程式碼

  而當你需要進行POST請求的時候,你可以直接使用封裝好了的 post() 方法來進行訪問:

In [3]: resp = requests.post('https://www.baidu.com', 
                             data = {'hello': 'world'})
複製程式碼

   其它的請求方式也都有相對應的請求方法來進行請求,如下:

In [4]: resp = requests.put('http://httpbin.org/put', data = {'key':'value'})
In [5]: resp = requests.delete('http://httpbin.org/delete')
In [6]: resp = requests.head('http://httpbin.org/get')
In [7]: resp = requests.options('http://httpbin.org/get')
複製程式碼

  雖然上述的幾種方法並不常用,但是 requests 官方依舊很人性化的將它們都封裝了進去。


2.3.2 傳遞 URL 引數

  傳遞URL引數的時候再也不用使用 urllib 中的方法來將URL拼接起來了,它和 urllib3 的方式一致,直接將要拼接的 URL 構造成字典,傳遞給 params引數即可,和 urllib3 中的 request 方法中的 field 屬性一致:

In [1]: import requests

In [2]: params = {'searchtype': '1''bookname': 'Detroit'}

In [3]: resp = requests.get('https://httpbin.org/get', params=params)

In [4]: resp.url
Out[4]: 'https://httpbin.org/get?searchtype=1&bookname=Detroit'
複製程式碼

  可以清晰的看到,request 自動幫我們把 params 引數拼接到了一起。當然,有的時候我們也會遇到同名不同值的引數,但是python語法中並不支援鍵名的重複,這個時候我們可以把屬性名相同的引數變成一個列表,request會自動的幫我們把它們拼接到一起:

In [1]: import requests

In [2]: params = {'name': 'Connor', 'others': ['abc', 'def']}

In [3]: resp = requests.get('https://www.httpbin.org/get', params=params)

In [4]: resp.url
Out[4]: 'https://www.httpbin.org/get?name=Connor&others=abc&others=def'
複製程式碼

2.3.3 自定義headers

  毫無疑問,requests 也能夠設定 headers 引數,這是章口就萊的事,下面我們來看看如何設定 headers

import requests
datas = {'name': 'Connor', 'age': '22', 'height': '166'}
headers = {'User-Agent': 'ABCDE'}
resp = requests.post('https://www.httpbin.org/post', headers=headers, data=datas)
print(resp.json()['headers'])

複製程式碼

執行之後的結果如下:

{'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'close', 'Content-Length': '29', 'Content-Type': 'application/x-www-form-urlencoded', 'Host': 'www.httpbin.org', 'User-Agent': 'ABCDE'}
複製程式碼

  可以明顯的看到,我們的 User-Agent 屬性已經變成了之前設定的 ABCDE,還是非常簡單的


2.3.4 自定義Cookies

  reqeusts 還可以幫我們構建cookie,我們不用再建立CookieJar物件了,直接將你的 Cookie傳遞給Cookies引數即可:

In [1]: import requests

In [2]: url = 'https://www.httpbin.org/cookies'

In [3]: cook = {'leijun': 'Are you OK?'}

In [4]: resp = requests.get(url, cookies=cook)

In [5]: resp.text
Out[5]: '{\n  "cookies": {\n    "leijun": "Are you OK?"\n  }\n}\n'
複製程式碼

2.3.5 設定代理

  requests 連cookie都幫我們構建了,當然也少不了代理的構建,當我們需要使用代理的時候需要把構造的字典傳遞給 proxies 引數即可

import requests
url = 'https://www.httpbin.org/ip'
datas = {'name': 'Connor', 'age': '22', 'height': '166'}
headers = {'User-Agent': 'ABCDE'}
proxy = {'http': 'http://110.52.235.90:9999', 'https': 'https://110.52.235.90:9999'}
resp1 = requests.get(url)
resp2 = requests.get(url, proxies=proxy)
print(resp1.text, resp2.text)
複製程式碼

執行之後的結果如下:

{"origin": "221.192.178.130"}
{"origin": "110.52.235.90"}
複製程式碼

2.3.6 重定向

  再網路請求中,我們常常會遇到狀態碼是3開頭的重定向問題,在 requests 中預設是開啟重定向的,即當我們遇到重定向的時候,reqeusts 會自動幫我們繼續訪問,你也可以手動關閉重定向:

In [1]: import requests

In [2]: url = 'http://www.jianshu.com'

In [3]: resp = requests.get(url, allow_redirects=False)

In [4]: resp.status_code
Out[4]: 301
複製程式碼

2.3.7 證照驗證

  有些時候,當我們使用了抓包工具的時候,由於抓包工具提供的證照不是受信任的數字證照頒發機構頒發的,所以會導致證照驗證失敗,這時我們應該關閉證照驗證。

  在請求的時候,我們可以關閉證照驗證,只需要將 verify 屬性設定為 False 即可:

In [1]: import requests

In [2]: resp = requests.get('https://www.baidu.com', verify=False)
d:\software\python\python\lib\site-packages\urllib3\connectionpool.py:847: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
複製程式碼

  這個時候你會發現,關閉驗證後會有一個煩人的847警告,你可以使用以下方式來關閉警告:

from requests.packages.urllib3.exceptions import InsecureRequestWarning
# 禁用安全請求警告
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
複製程式碼

2.3.8 設定超時

  當我們訪問一個網站的時候,如果訪問時間太久,或者響應時間太長,可能對我們的效率有很大的影響,這個時候我們可以設定超時時間,放棄一些資料來提升爬蟲的效率。你只需要為其設定timeout 引數即可:

In [1]: import requests
    
In [2]: requests.get('http://github.com', timeout=0.001)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
requests.exceptions.Timeout: HTTPConnectionPool(host='github.com', port=80): Request timed out. (timeout=0.001)
複製程式碼

2.4 requests的響應

  通過 requests 發起請求得到的物件是 Resopnse 物件,通過這個物件我們可以方便的獲取響應的內容。

2.4.1 響應內容

  在上一式我們講述 urlliburllib3 的時候,我們獲取到的內容是二進位制格式的,這個時候需要我們自己手動的 decode() 將二進位制的bytes資料轉換成我們可以讀懂的字串資料。但是在 requests 中,它的 text 屬性會主動的通過可能的方式幫我們解碼:

In [1]: import requests

In [2]: url = 'https://www.httpbin.org/get'

In [3]: resp = requests.get(url)

In [4]: resp.text
Out[4]: '{\n  "args": {}, \n  "headers": {\n    "Accept": "*/*", \n    "Accept-Encoding": "gzip, deflate", \n    "Connection": "close", \n    "Host": "www.httpbin.org", \n    "User-Agent": "python-requests/2.21.0"\n  }, \n  "origin": "221.192.178.130", \n  "url": "https://www.httpbin.org/get"\n}\n'
複製程式碼

  requests 會主動的幫我們進行解碼,它會通過響應的報頭來猜測網頁的編碼是什麼,然後根據猜測的編碼來解碼網頁的內容。大部分的網頁他都能夠正確的解碼,但也有解碼不正確的時候,如果發現解碼不正確了,我們就需要自己來手動的指定解碼的編碼格式:

import requests
url = 'http://www.quanshuwang.com/'
resp = requests.get(url)
resp.encoding = 'GBK'
print(resp.text)
複製程式碼

  在這裡我給大家推薦一個其他的python自帶的庫,這個庫也是在猜測網頁的編碼格式,但是這個庫的準確率更高一些吧。況且我們每次指定解碼格式就要求我們去看網頁的編碼格式。像新浪微博這樣的網站,它在無序視覺化的頁面上使用的是 GBK 編碼格式,在需要可視的頁面上的編碼格式是 UTF-8,我們不希望每次訪問都手動的去更改它的編碼格式,這個時候就用下面這種方法:

  這個時候,我們可以使用 chardet 中的 detect 方法:

import requests
import chardet

url = 'https://www.httpbin.org/get'
resp = requests.get(url)
resp.encoding = chardet.detect(resp.content)
print(resp.text)
複製程式碼

  如果你需要獲得網頁的原始二進位制資料的時候,那麼使用 content 屬性即可:

In [1]: import requests

In [2]: url = 'https://www.httpbin.org/get'

In [3]: resp = requests.get(url)
   
In [4]: resp.content
Out[4]: b'{\n  "args": {}, \n  "headers": {\n    "Accept": "*/*", \n    "Accept-Encoding": "gzip, deflate", \n    "Connection": "close", \n    "Host": "www.httpbin.org", \n    "User-Agent": "python-requests/2.21.0"\n  }, \n  "origin": "221.192.178.130", \n  "url": "https://www.httpbin.org/get"\n}\n'
複製程式碼

  如果我們訪問之後獲得的資料是JSON格式的,那麼我們可以使用json()方法,直接獲取轉換成字典格式的資料:

In [1]: import requests

In [2]: url = 'https://www.httpbin.org/get'

In [3]: resp = requests.get(url)
    
In [4]: resp.json()
Out[4]:
{'args': {},
 'headers': {'Accept': '*/*',
  'Accept-Encoding': 'gzip, deflate',
  'Connection': 'close',
  'Host': 'www.httpbin.org',
  'User-Agent': 'python-requests/2.21.0'},
 'origin': '221.192.178.130',
 'url': 'https://www.httpbin.org/get'}
複製程式碼

2.4.2 狀態碼

  有時候我們過於頻繁的訪問一個網址之後,或者某些網站對訪問速度有限制的話,那我們應該判斷以下響應的狀態碼,然後再判斷是否需要資料的提取。你可以通過 status_code 屬性來獲取響應的狀態碼:

In [1]: import requests

In [2]: url = 'https://www.httpbin.org/get'

In [3]: resp = requests.get(url)
    
In [4]: resp.status_code
Out[4]: 200
複製程式碼

2.4.3 響應報頭

  當我們進行了訪問之後,如果你想檢視該網頁的響應報頭,那麼你可以通過 headers 屬性來檢視你的響應報頭

In [1]: import requests

In [2]: url = 'https://www.httpbin.org/get'

In [3]: resp = requests.get(url)
    
In [4]: resp.headers
Out[4]: {'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Date': 'Mon, 07 Jan 2019 11:36:04 GMT', 'Content-Type': 'application/json', 'Content-Length': '277', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true', 'Via': '1.1 vegur'}
複製程式碼

2.4.4 伺服器返回的cookies

  你可以通過響應物件的 cookies 屬性來獲取伺服器返回的 cookies

In [1]: import requests

In [2]: url = 'https://www.httpbin.org/get'

In [3]: resp = requests.get(url)
    
In [4]: resp.cookies
Out[4]: <RequestsCookieJar[]>
複製程式碼

  返回的cookies是一個CookieJar物件,如果你想使用這個cookie,你還是需要構建CookieJar物件。cookies 在後續的內容裡我會專門抽出一節課來講這個的,這裡我們掠過,只需要知道通過 cookie 屬性可以獲取從網頁上訪問的 cookies 即可。


2.4.5 url

  你也可以通過 url 屬性來檢視響應的url,你可以通過這種方式來判斷網頁是否進行了重定向等各類操作:

In [1]: import requests

In [2]: url = 'http://www.jianshu.com'

In [3]: resp = requests.get(url)

In [4]: resp.url
Out[4]: 'https://www.jianshu.com'
複製程式碼

2.5 Session物件

2.5.1 Session 的簡單使用

  在 requests 中,實現了 Session 會話功能,當我們使用 Session 的時候,能夠像瀏覽器一樣,在沒有關閉瀏覽器的時候,保持訪問的狀態,這個功能常常用於登陸之後,可以避免我們一次又一次的傳遞 cookies。session 其實非常的簡單易用,它和 requests 物件幾乎沒有任何的差別,requests 的常用方法在 session 這裡依舊適用。下面我們舉一個簡單的例子:

import requests

url = 'https://www.httpbin.org/cookies'
session = requests.session()
resp = sesssion.get('https://www.httpbin.org/get')
print(resp.text)
複製程式碼

  執行結果如下:

{"args": {}, "headers": {"Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Cookie": "eare=https://www.httpbin.org", "Host": "www.httpbin.org", "User-Agent": "python-requests/2.20.0"}, "origin": "221.192.178.173", "url": "https://www.httpbin.org/get"}
複製程式碼

  從上面的執行結果中我們可以看到,session 操作起來還是非常容易上手的,它的方法和requests 的用法幾乎是完全一致的。當然 session 也有自己更為獨有的功能,下面我們一起來看看它還有哪些其它的功能:


2.5.2 updata方法

  既然之前我們說 session 是一個可以大大提高效率的工具,可以保持一些資料,不用頻繁的重複傳參,那我們來試試看:

import requests

url = 'https://www.httpbin.org/cookies'
session = requests.session()
cook = {'eare': 'https://www.httpbin.org'}
resp = session.get(url, cookies=cook)
print(resp.text)
resp = session.get('https://www.httpbin.org/get')
print(resp.text)
複製程式碼

  執行後的結果如下:

{"cookies": {"eare": "https://www.httpbin.org"}}

{"args": {}, "headers": {"Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Host": "www.httpbin.org", "User-Agent": "python-requests/2.20.0"}, "origin": "221.192.178.173", "url":"https://www.httpbin.org/get"}
複製程式碼

  通過上面的結果我們會發現,第二次訪問的時候並沒有維持第一次訪問時設定的 session 啊,這是為什麼呢?難道是session的 doman 有問題?不是的,通過單次訪問傳參的方式來新增引數是不會對下一次訪問起作用的,這個時候我們需要使用一個方法 session.args.updata()

import requests

url = 'https://www.httpbin.org/cookies'
session = requests.session()
cook = {'eare': 'https://www.httpbin.org'}
session.cookies.update(cook)
resp = session.get(url)
print(resp.text)
resp = session.get('https://www.httpbin.org/get')
print(resp.text)
複製程式碼

  這次我們再來看執行結果:

{"cookies": {"eare": "https://www.httpbin.org"}}

{"args": {}, "headers": {"Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Cookie": "eare=https://www.httpbin.org", "Host": "www.httpbin.org", "User-Agent": "python-requests/2.20.0"}, "origin": "221.192.178.173", "url": "https://www.httpbin.org/get"}
複製程式碼

  這一次我們明顯的發現了第二次訪問的時候帶上了第一次設定的 cookie 引數,這樣設定的話我們之後的每次訪問都無需再次傳參了。當然,前提是在同一個域名下的訪問。關於 cookie 的作用域的問題,我在之前的文章:Python 爬蟲十六式 - 第一式:HTTP協議 >>> 中已經寫到過了,如果看不懂的話請參照該文章。

  在上面的內容中,有一點尤為需要注意: session.args.updata() 並不指實際的方法,這只是一類引數的統稱,個人的命名習慣,如有錯誤,歡迎指出! session.args.updata()包括如下的幾種引數:

名稱 作用
headers 設定session的請求頭引數
cookies 設定session的cookies
params 設定session的url拼接引數
proxies 設定session的proxy代理

2.5.3 Session.verify

  session 當然也為我們提供了批量管理驗證的方法,這與 requests 中的管理方式幾乎相同,但和 session 的其它引數的管理相比又有所不同。session.verify 是一個屬性,它是一個 Boolean 型的變數,你只需要將它設定為 False 即可關閉 session 物件的所有證照驗證:

import requests

url = 'https://www.httpbin.org/user-agent'
session = requests.session()
head = {'User-Agent': 'ABCDE'}
session.verify = False
session.headers.update(head)
resp = session.get(url)
print(resp.text)
複製程式碼

  執行結果如下:

py:847: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
{"user-agent": "ABCDE"}
複製程式碼

  嗯,又看到了煩人的 847報錯,想要關閉它的話檢視本文第2.3.7節證照驗證,裡面有如何關閉證照驗證警告的方法,點我直達 >>>


  好啦,到這裡我們就說完 requests 的使用了,不知道你學到了多少東西,但是依舊希望這些東西對你有幫助,最後還是那句話送給大家:

學習一時爽, 一直學習一直爽!

  我是一個從無到有的技術小白,Connor,我們下期再見!


系列文章連線:

Python 爬蟲十六式 - 第一式:HTTP協議 >>>
Python 爬蟲十六式 - 第二式:urllib 與 urllib3 >>>
Python 爬蟲十六式 - 第四式:使用Xpath提取網頁內容 >>>
Python 爬蟲十六式 - 第五式:BeautifulSoup,美味的湯 >>>
Python 爬蟲十六式 - 第六式:JQuery的假兄弟-pyquery >>>
Python 爬蟲十六式 - 第七式:正則的藝術 >>>
Python 爬蟲十六式 - 第八式:例項解析-全書網 >>>

相關文章