在爬蟲入門系列(一):快速理解HTTP協(xié)議中介紹了 HTTP 協(xié)議,Python 提供了很多模塊來基于 HTTP 協(xié)議的網(wǎng)絡(luò)編程,urllib、urllib2、urllib3、httplib、httplib2,都是和 HTTP 相關(guān)的模塊,看名字覺得很反人類,更糟糕的是這些模塊在 Python2 與 Python3 中有很大的差異,如果業(yè)務(wù)代碼要同時兼容 2 和 3,寫起來會讓人崩潰。
幸運(yùn)地是,繁榮的 Python 社區(qū)給開發(fā)者帶來了一個非常驚艷的 HTTP 庫 requests,一個真正給人用的HTTP庫。它是 GitHUb 關(guān)注數(shù)最多的 Python 項(xiàng)目之一,requests 的作者是 Kenneth Reitz 大神。
requests 實(shí)現(xiàn)了 HTTP 協(xié)議中絕大部分功能,它提供的功能包括 Keep-Alive、連接池、Cookie持久化、內(nèi)容自動解壓、HTTP代理、SSL認(rèn)證、連接超時、Session等很多特性,最重要的是它同時兼容 python2 和 python3。
快速入門
requests 的安裝可以直接使用 pip 方法:pip install requests
>>> import requests
# GET 請求
>>> response = requests.get("https://foofish.net")
返回的時 Response 對象,Response 對象是 對 HTTP 協(xié)議中服務(wù)端返回給瀏覽器的響應(yīng)數(shù)據(jù)的封裝,響應(yīng)的中的主要元素包括:狀態(tài)碼、原因短語、響應(yīng)首部、響應(yīng)體等等,這些屬性都封裝在Response 對象中。
# 狀態(tài)碼
>>> response.status_code
200
# 原因短語
>>> response.reason
'OK'
# 響應(yīng)首部
>>> for name,value in response.headers.items():
... print("%s:%s" % (name, value))
...
Content-Encoding:gzip
Server:nginx/1.10.2
Date:Thu, 06 Apr 2017 16:28:01 GMT
# 響應(yīng)內(nèi)容
>>> response.content
'<html><body>此處省略一萬字...</body></html>
requests 除了支持 GET 請求外,還支持 HTTP 規(guī)范中的其它所有方法,包括 POST、PUT、DELTET、HEADT、OPTIONS方法。
>>> r = requests.post('http://httpbin.org/post', data = {'key':'value'})
>>> r = requests.put('http://httpbin.org/put', data = {'key':'value'})
>>> r = requests.delete('http://httpbin.org/delete')
>>> r = requests.head('http://httpbin.org/get')
>>> r = requests.options('http://httpbin.org/get')
構(gòu)建請求查詢參數(shù)
很多URL都帶有很長一串參數(shù),我們稱這些參數(shù)為URL的查詢參數(shù),用"?"附加在URL鏈接后面,多個參數(shù)之間用"&"隔開,比如:http://fav.foofish.net/?p=4&s=20 ,現(xiàn)在你可以用字典來構(gòu)建查詢參數(shù):
>>> args = {"p": 4, "s": 20}
>>> response = requests.get("http://fav.foofish.net", params = args)
>>> response.url
'http://fav.foofish.net/?p=4&s=2'
構(gòu)建請求首部 Headers
requests 可以很簡單地指定請求首部字段 Headers,比如有時要指定 User-Agent 偽裝成瀏覽器發(fā)送請求,以此來蒙騙服務(wù)器。直接傳遞一個字典對象給參數(shù) headers 即可。
>>> r = requests.get(url, headers={'user-agent': 'Mozilla/5.0'})
構(gòu)建 POST 請求數(shù)據(jù)
requests 可以非常靈活地構(gòu)建 POST 請求需要的數(shù)據(jù),如果服務(wù)器要求發(fā)送的數(shù)據(jù)是表單數(shù)據(jù),則可以指定關(guān)鍵字參數(shù) data,如果要求傳遞 json 格式字符串參數(shù),則可以使用json關(guān)鍵字參數(shù),參數(shù)的值都可以字典的形式傳過去。
作為表單數(shù)據(jù)傳輸給服務(wù)器
>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.post("http://httpbin.org/post", data=payload)
作為 json 格式的字符串格式傳輸給服務(wù)器
>>> import json
>>> url = 'http://httpbin.org/post'
>>> payload = {'some': 'data'}
>>> r = requests.post(url, json=payload)
Response中的響應(yīng)體
HTTP返回的響應(yīng)消息中很重要的一部分內(nèi)容是響應(yīng)體,響應(yīng)體在 requests 中處理非常靈活,與響應(yīng)體相關(guān)的屬性有:content、text、json()。
content 是 byte 類型,適合直接將內(nèi)容保存到文件系統(tǒng)或者傳輸?shù)骄W(wǎng)絡(luò)中
>>> r = requests.get("https://pic1.zhimg.com/v2-2e92ebadb4a967829dcd7d05908ccab0_b.jpg")
>>> type(r.content)
<class 'bytes'>
# 另存為 test.jpg
>>> with open("test.jpg", "wb") as f:
... f.write(r.content)
text 是 str 類型,比如一個普通的 HTML 頁面,需要對文本進(jìn)一步分析時,使用 text。
>>> r = requests.get("https://foofish.net/understand-http.html")
>>> type(r.text)
<class 'str'>
>>> re.compile('xxx').findall(r.text)
如果使用第三方開放平臺或者API接口爬取數(shù)據(jù)時,返回的內(nèi)容是json格式的數(shù)據(jù)時,那么可以直接使用json()方法返回一個經(jīng)過json.loads()處理后的對象。
>>> r = requests.get('https://www.v2ex.com/api/topics/hot.json')
>>> r.json()
[{'id': 352833, 'title': '在長沙,父母同住...
代理設(shè)置
當(dāng)爬蟲頻繁地對服務(wù)器進(jìn)行抓取內(nèi)容時,很容易被服務(wù)器屏蔽掉,因此要想繼續(xù)順利的進(jìn)行爬取數(shù)據(jù),使用代理是明智的選擇。如果你想爬取墻外的數(shù)據(jù),同樣設(shè)置代理可以解決問題,requests 完美支持代理。
import requests
proxies = {
'http': 'http://10.10.1.10:3128',
'https': 'http://10.10.1.10:1080',
}
requests.get('http://example.org', proxies=proxies)
超時設(shè)置
requests 發(fā)送請求時,默認(rèn)請求下線程一直阻塞,直到有響應(yīng)返回才處理后面的邏輯。如果遇到服務(wù)器沒有響應(yīng)的情況時,問題就變得很嚴(yán)重了,它將導(dǎo)致整個應(yīng)用程序一直處于阻塞狀態(tài)而沒法處理其他請求。
>>> import requests
>>> r = requests.get("http://www.google.coma")
...一直阻塞中
正確的方式的是給每個請求顯示地指定一個超時時間。
>>> r = requests.get("http://www.google.coma", timeout=5)
5秒后報錯
Traceback (most recent call last):
socket.timeout: timed out
Session
在爬蟲入門系列(一):快速理解HTTP協(xié)議中介紹過HTTP協(xié)議是一中無狀態(tài)的協(xié)議,為了維持客戶端與服務(wù)器之間的通信狀態(tài),使用 Cookie 技術(shù)使之保持雙方的通信狀態(tài)。
有些網(wǎng)頁是需要登錄才能進(jìn)行爬蟲操作的,而登錄的原理就是瀏覽器首次通過用戶名密碼登錄之后,服務(wù)器給客戶端發(fā)送一個隨機(jī)的Cookie,下次瀏覽器請求其它頁面時,就把剛才的 cookie 隨著請求一起發(fā)送給服務(wù)器,這樣服務(wù)器就知道該用戶已經(jīng)是登錄用戶。
import requests
# 構(gòu)建會話
session = requests.Session()
# 登錄url
session.post(login_url, data={username, password})
# 登錄后才能訪問的url
r = session.get(home_url)
session.close()
構(gòu)建一個session會話之后,客戶端第一次發(fā)起請求登錄賬戶,服務(wù)器自動把cookie信息保存在session對象中,發(fā)起第二次請求時requests 自動把session中的cookie信息發(fā)送給服務(wù)器,使之保持通信狀態(tài)。
項(xiàng)目實(shí)戰(zhàn)
最后是一個實(shí)戰(zhàn)項(xiàng)目,如何用 requests 實(shí)現(xiàn)知乎自動登錄并給用戶發(fā)私信,我會在下一篇文章中進(jìn)行講解。
延伸閱讀: