requests
????在 urllib 的應(yīng)用中盗忱,有一些不方便的地方刀崖,比如處理網(wǎng)頁(yè)驗(yàn)證和 Cookies 時(shí)尘惧,需要寫(xiě) Opener 和 Handler 來(lái)處理。為了更加方便地實(shí)現(xiàn)這些操作辆苔,需要用到更強(qiáng)大的庫(kù):requests。
1. 基本用法
1.1. 簡(jiǎn)單示例
????urllib 庫(kù)中的 urlopen() 方法以 GET 方式請(qǐng)求網(wǎng)頁(yè)扼劈,而 requests 中相應(yīng)的就是 get() 方法驻啤。
import requests
r = requests.get('https://www.baidu.com/')
print(type(r))
# <class 'requests.models.Response'>
print(r.status_code)
# 200
# 響應(yīng)體的類(lèi)型
print(type(r.text))
# <class 'str'>
# 響應(yīng)體的內(nèi)容
print(r.text)
# Cookies的類(lèi)型
print(r.cookies)
# <RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
1.2. GET請(qǐng)求
????HTTP中最常見(jiàn)的請(qǐng)求之一就是GET請(qǐng)求。構(gòu)建一個(gè)最簡(jiǎn)單的GET請(qǐng)求荐吵,鏈接為 http://httpbin.org/get骑冗,該網(wǎng)站會(huì)判斷請(qǐng)求方式,如果是GET請(qǐng)求的話(huà)就返回相應(yīng)的請(qǐng)求信息先煎。
1.2.1. 基本實(shí)例
import requests
r = requests.get('http://httpbin.org/get')
print(r.text)
"""
運(yùn)行結(jié)果:
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.20.1"
},
"origin": "123.160.224.118",
"url": "http://httpbin.org/get"
}
"""
????如果要在GET請(qǐng)求中附加額外的信息贼涩,一般用字典來(lái)存儲(chǔ):
import requests
data = {
'name': 'germey',
'age': 22
}
r = requests.get('http://httpbin.org/get', params=data)
print(r.text)
"""
{
"args": {
"age": "22",
"name": "germey"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.20.1"
},
"origin": "123.160.224.118",
"url": "http://httpbin.org/get?name=germey&age=22"
}
"""
????網(wǎng)頁(yè)的返回類(lèi)型實(shí)際上是 str 類(lèi)型,但是它很特殊薯蝎,是JSON格式的遥倦。如果想直接解析返回結(jié)果,得到一個(gè)字典格式的話(huà)占锯,可以直接調(diào)用 json() 方法袒哥。
import requests
r = requests.get('http://httpbin.org/get')
print(type(r.text))
print(r.json())
print(type(r.json()))
"""
運(yùn)行結(jié)果:
<class 'str'>
{'args': {}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'close', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.20.1'}, 'origin': '123.160.224.118', 'url': 'http://httpbin.org/get'}
<class 'dict'>
"""
????調(diào)用 json() 方法可以將返回結(jié)果是JSON格式的字符串轉(zhuǎn)化為字典,但是在返回結(jié)果不是JSON格式時(shí)會(huì)出現(xiàn)解析錯(cuò)誤烟央,拋出 json.decoder.JSONDecoderError 異常统诺。
1.2.2. 抓取網(wǎng)頁(yè)
????以“知乎”的“發(fā)現(xiàn)”頁(yè)面為例:
import requests
import re
headers = {
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36\
(KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'
}
r = requests.get('https://www.zhihu.com/explore', headers=headers)
pattern = re.compile('explore-feed.*?question_link.*?>(.*?)</a>', re.S)
titles = re.findall(pattern, r.text)
print(titles)
"""
運(yùn)行結(jié)果:
['\n歷史上有那些長(zhǎng)期流傳的,錯(cuò)誤的地理記載疑俭,后來(lái)是如何被推翻的粮呢?\n',
'\n人生應(yīng)該活成什么樣子,該以什么樣的方式活著?\n',
'\nfgo里面的從者和禮裝卡面有哪些不仔細(xì)看看不出來(lái)的小彩蛋啄寡?\n',
'\n如何看待新浪娛樂(lè)的對(duì)話(huà)特輯《聽(tīng)老高鹿晗說(shuō)》豪硅?\n',
'\n如何評(píng)價(jià)《群星(Stellaris)》新DLC《巨型企業(yè)(Megacorp)》?\n',
'\n有哪些句子是真正寫(xiě)到你的心里去了挺物?\n',
'\n經(jīng)過(guò)此次馬思純回懟風(fēng)波后懒浮,楊紫后續(xù)的資源是否更差?\n',
'\n你有過(guò)哪些像段子的親身經(jīng)歷识藤?\n',
'\n如何看待網(wǎng)傳朱一龍抽煙并隨地扔煙頭砚著?\n',
'\n怎么評(píng)價(jià)防彈續(xù)約7年?\n']
"""
????User-Agent 字段信息是瀏覽器標(biāo)識(shí)信息,不加會(huì)被禁止抓取痴昧。
1.2.3. 抓取二進(jìn)制數(shù)據(jù)
????以 GitHub 的站點(diǎn)圖標(biāo)為例:
import requests
r = requests.get('https://github.com/favicon.ico')
print(r.text)
print(r.content)
結(jié)果如下:
r.text 部分結(jié)果:???????t??????????????????????????????
r.content 部分結(jié)果:
b'\x00\x00\x01\x00\x02\x00\x10\x10\x00\x00\x01\x00
圖片是二進(jìn)制數(shù)據(jù)稽穆,前者在打印時(shí)轉(zhuǎn)化為 str 類(lèi)型,圖片直接轉(zhuǎn)化為字符串赶撰,會(huì)出現(xiàn)亂碼
將提取的圖片保存下來(lái)
import requests
r = requests.get('https://github.com/favicon.ico')
with open('favicon.ico', 'wb') as f:
f.write(r.content)
????使用 open() 方法舌镶,第一個(gè)參數(shù)是文件名稱(chēng),第二個(gè)參數(shù)代表以二進(jìn)制形式打開(kāi)豪娜,可以向文件中寫(xiě)入二進(jìn)制數(shù)據(jù)餐胀。運(yùn)行結(jié)束會(huì)在文佳家中產(chǎn)生名為 favicon.ico 的圖片文件。
????同樣瘤载,音頻和視頻文件也可以用這種方法獲取否灾。
1.2.4. 添加 headers
????與 urllib.request 一樣,requests 也通過(guò) headers 來(lái)傳遞頭消息鸣奔。例如前面的“知乎”例子中坟冲,如果不傳遞 headers,就不能正常請(qǐng)求:
import requests
r = requests.get('https://www.zhihu.com/explore')
print(r.text)
"""
運(yùn)行結(jié)果:
<html>
<head><title>400 Bad Request</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<hr><center>openresty</center>
</body>
</html>
"""
1.3. POST請(qǐng)求
????請(qǐng)求 http://httpbin.org/post溃蔫,該網(wǎng)站判斷是POST請(qǐng)求后把相關(guān)信息返回健提,其中 form 部分就是提交的數(shù)據(jù),證明POST請(qǐng)求成功伟叛。
import requests
data = {'name': 'germey', 'age': '22'}
r = requests.post('http://httpbin.org/post', data=data)
print(r.text)
"""
{
"args": {},
"data": "",
"files": {},
"form": {
"age": "22",
"name": "germey"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Content-Length": "18",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.20.1"
},
"json": null,
"origin": "123.160.224.118",
"url": "http://httpbin.org/post"
}
"""
1.4. 響應(yīng)
????發(fā)送請(qǐng)求后私痹,可以使用 text 和 content 得到的響應(yīng)內(nèi)容,此外還有很多屬性和方法可以用來(lái)獲取其他信息统刮,如狀態(tài)碼紊遵、響應(yīng)頭、Cookies 等侥蒙。
import requests
r = requests.get('http://www.reibang.com')
# 輸出狀態(tài)碼
print(type(r.status_code), r.status_code)
# 輸出響應(yīng)頭
print(type(r.headers), r.headers)
# 輸出Cookies
print(type(r.cookies), r.cookies)
# 輸出URL
print(type(r.url), r.url)
# 輸出請(qǐng)求歷史
print(type(r.history), r.history)
<class 'int'> 403
<class 'requests.structures.CaseInsensitiveDict'> {'Date': 'Sat, 24 Nov 2018 09:26:34 GMT', 'Content-Type': 'text/html', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'Server': 'Tengine', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload', 'Content-Encoding': 'gzip', 'X-Via': '1.1 PSzjwzdx11at80:10 (Cdn Cache Server V2.0), 1.1 PShbycdx6yh51:11 (Cdn Cache Server V2.0)'}
<class 'requests.cookies.RequestsCookieJar'> <RequestsCookieJar[]>
<class 'str'> http://www.reibang.com/
<class 'list'> []
headers 和 cookies 兩個(gè)屬性得到的結(jié)果分別是CaseInsensitiveDict 和 RequestsCookieJar 類(lèi)型
????狀態(tài)碼常用來(lái)判斷請(qǐng)求是否成功暗膜,而 requests 還提供了一個(gè)內(nèi)置的狀態(tài)碼查詢(xún)對(duì)象 requests.Codes,示例如下:
import requests
r = requests.get('http://www.reibang.com')
exit() if not r.status_code == requests.codes.ok else print('Request Successfully')
????通過(guò)比較返回碼和內(nèi)置的成功的返回碼鞭衩,來(lái)保證請(qǐng)求得到了正常響應(yīng)学搜,輸出成功請(qǐng)求的消息娃善,否則程序終止,用 requests.codes.ok 得到的成功的狀態(tài)碼是 200瑞佩。
一些返回碼和相應(yīng)的查詢(xún)條件:
????如果想判斷結(jié)果是不是 404 狀態(tài)炬丸,可以用 requests.codes.not_foend 來(lái)對(duì)比瘫寝。
2. 高級(jí)用法
2.1. 文件上傳
import requests
files = {'file': open('favicon.ico', 'rb')}
r = requests.post('http://httpbin.org/post', files=files)
print(r.text)
????上傳的部分會(huì)在一個(gè)單獨(dú)的 files 字段里標(biāo)識(shí)出來(lái)。
2.2. Cookies
import requests
r = requests.get('https://www.baidu.com')
print(r.cookies)
for key, value in r.cookies.items():
print(key + '=' + value)
????可以直接用 Cookies 維持登錄狀態(tài)稠炬,登錄后將 Headers 中的 Cookie 內(nèi)容復(fù)制下來(lái)焕阿,替換成自己的 Cookie, 將其設(shè)置到 Headers 里首启,然后發(fā)送請(qǐng)求捣鲸。
2.3. 會(huì)話(huà)維持
import requests
requests.get('http://httpbin.org/cookies/set/number/123456789')
r = requests.get('http://httpbin.org/cookies')
print(r.text)
運(yùn)行結(jié)果為:
{
"cookies": {}
}
不能獲取到設(shè)置的 Cookies。
import requests
s = requests.Session()
s.get('http://httpbin.org/cookies/set/number/123456789')
r = s.get('http://httpbin.org/cookies')
print(r.text)
運(yùn)行結(jié)果為:
{
"cookies": {
"number": "123456789"
}
}
Yes闽坡,獲取成功!
2.4. SSL 證書(shū)驗(yàn)證
????requests 還提供了證書(shū)驗(yàn)證的功能愁溜。當(dāng)發(fā)送 HTTP 請(qǐng)求的時(shí)候疾嗅,它會(huì)檢查 SSL 證書(shū),我們可以使用 verify 參數(shù)控制是否檢查此證書(shū).其實(shí)如果不加 verify 參數(shù)的話(huà)冕象,默認(rèn)是 True 代承,會(huì)自動(dòng)驗(yàn)證。
import requests
response = requests.get('https://www.12306.cn')
print(response.status_code)
"""
requests.exceptions.SSLError: ("bad handshske: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",)
"""
????這里提示一個(gè)錯(cuò)誤 SSLError 渐扮,表示證書(shū)驗(yàn)證錯(cuò)誤论悴。所以,如果請(qǐng)求一個(gè) HTTPS 站點(diǎn)墓律,但是證書(shū)驗(yàn)證錯(cuò)誤的頁(yè)面時(shí)膀估,就會(huì)報(bào)這樣的錯(cuò)誤,那么如何避免這個(gè)錯(cuò)誤呢耻讽?很簡(jiǎn)單察纯,把 verify 參數(shù)設(shè)置為 False 即可,相關(guān)代碼如下:
import requests
response = requests.get('https://www.12306.cn', verify=False)
print(response.status_code)
????也可以通過(guò)指定一個(gè)本地證書(shū)用作客戶(hù)端證書(shū)针肥,可以是單個(gè)文件(包含秘鑰和證書(shū))或一個(gè)包含兩個(gè)文件路徑的元組:
import requests
response = requests.get('https://www.12306.cn', cert=('path/server.crt', 'path/key'))
print(response.status_code)
????代碼是演示示例饼记,需要的有 crt 和 key 文件,并且指定它們的路徑慰枕。注意:本地私有證書(shū)的 key 必須是解密狀態(tài)具则,加密狀態(tài)的 key 是不支持的。
2.5. 代理設(shè)置
????對(duì)于某些網(wǎng)站具帮,在測(cè)試的時(shí)候請(qǐng)求幾次博肋,能正常獲取內(nèi)容低斋。但是一旦開(kāi)始大規(guī)模爬取,對(duì)于大規(guī)模且頻繁的請(qǐng)求束昵,網(wǎng)站可能會(huì)彈出驗(yàn)證碼拔稳,或者跳轉(zhuǎn)到登錄認(rèn)證頁(yè)面 更甚者可能會(huì)直接封禁客戶(hù)端的 IP,導(dǎo)致一定時(shí)間段內(nèi)無(wú)法訪(fǎng)問(wèn)锹雏。
????那么巴比,為了防止這種情況發(fā)生,需要設(shè)置代理來(lái)解決這個(gè)問(wèn)題礁遵,這就需要用到 proxies 參數(shù)轻绞,可以用這樣的方式設(shè)置:
import requests
proxies = {
"http": "http://10.10.1.10:3128",
"https": "https://10.10.1.10:1080"
}
requests.get("https://www.taobao.com", proxies=proxies)
# 如果代理需要使用 HTTP Basic Auth ,可以使用類(lèi)似 http://user:password@host:port 這樣的語(yǔ)法來(lái)設(shè)置代理
# requests.get("https://www.taobao.com", proxies=proxies)
2.6. 超時(shí)設(shè)置
????在本機(jī)網(wǎng)絡(luò)狀況不好或者服務(wù)器網(wǎng)絡(luò)響應(yīng)太慢甚至無(wú)響應(yīng)時(shí)佣耐,我們可能會(huì)等待特別久的時(shí)間才能收到響應(yīng)政勃,甚至到最后收不到響應(yīng)而報(bào)錯(cuò)。為了防止服務(wù)器不能及時(shí)響應(yīng)兼砖,應(yīng)該設(shè)置一個(gè)超時(shí)時(shí)間奸远,即超過(guò)了這個(gè)時(shí)間還沒(méi)有得到響應(yīng),那就報(bào)錯(cuò)讽挟,這需要用到 timeout 參數(shù)懒叛。這個(gè)時(shí)間的計(jì)算是發(fā)出請(qǐng)求到服務(wù)器返回響應(yīng)的時(shí)間。示例如下:
import requests
r = requests.get("https://www.taobao.com", timeout=1)
print(r.status_code)
????超時(shí)時(shí)間設(shè)置為1秒耽梅,1秒內(nèi)沒(méi)有響應(yīng)就拋出異常薛窥。實(shí)際上請(qǐng)求分為兩個(gè)階段:連接(connect)和讀取(read)眼姐,上面設(shè)置的 timeout 是兩者用時(shí)的總和诅迷。
其他設(shè)置方式:
如果需要分開(kāi)指定,可以傳入一個(gè)元組:
r = requests.get('https://www.taobao.com', timeout=(5, 11, 30))
如果想永久等待众旗,timeout 默認(rèn)值為 None 罢杉,可以直接設(shè)置為 None:
r = requests.get("https://www.taobao.com", timeout=None)
也可以直接不加參數(shù):
r = requests.get("https://www.taobao.com")
2.7. 身份認(rèn)證
????requests 自帶的身份認(rèn)證功能:
import requests
from requests.auth import HTTPBasicAuth
r = requests.get('http://localhost:5000', auth=HTTPBasicAuth('username', 'password'))
print(r.status_code)
????如果用戶(hù)名和密碼正確,請(qǐng)求時(shí)會(huì)自動(dòng)認(rèn)證成功贡歧,返回 200 狀態(tài)碼屑那;如果認(rèn)證失敗,則返回 401 狀態(tài)碼艘款。
????還有一種簡(jiǎn)單的寫(xiě)法持际,不用傳 HTTPBasicAuth 類(lèi)參數(shù),直接傳一個(gè)元組哗咆,會(huì)默認(rèn)使用 HTTPBasicAuth 這個(gè)類(lèi)來(lái)認(rèn)證:
import requests
r = requests.get('http://localhost:5000', auth=('username', 'password'))
print(r.status_code)
????requests 還提供了其他認(rèn)證方式蜘欲,如 OAuth 認(rèn)證,不過(guò)需要安裝 oauth 包晌柬,安裝命令如下:
pip install requests.oauthlib
import requests
from requests_oauthlib import OAuth1
url = 'https://api.twitter.com/1.1/account/verify_credentials.json'
auth = OAuth1('YOUR_APP_KEY', 'YOUR_APP_SECRET', 'USER_OAUTH_TOKEN', 'USER_OAUTH_TOKEN_SECRET')
requests.get(url, auth=auth)
更多詳細(xì)功能請(qǐng)參考 requests_oauthlib 官方文檔 https://requests-oauthlib.readthedocs.org/