Requests 2.18.1文档
發(fā)送請(qǐng)求
使用Requests發(fā)送網(wǎng)絡(luò)請(qǐng)求非常簡(jiǎn)單。
一開(kāi)始要導(dǎo)入Requests模塊:
>>> import requests
然后,嘗試獲取某個(gè)網(wǎng)頁(yè)。本例中,我們來(lái)獲取Github的公共時(shí)間線
>>> r = requests.get('https://api.github.com/events')
現(xiàn)在,我們有一個(gè)名為?r?的 Response?對(duì)象。我們可以從這個(gè)對(duì)象中去獲取所有我們想要的信息。
Requests簡(jiǎn)便的API意味著所有HTTP請(qǐng)求類型都是顯而易見(jiàn)的。例如,你可以這樣發(fā)送一個(gè)HTTP POST請(qǐng)求:
>>> r = requests.post('http://httpbin.org/post', data = {'key': 'value'})
漂亮,對(duì)吧?那么其他HTTP請(qǐng)求類型:PUT, DELETE,HEAD以及OPTIONS又是如何的呢?都是一樣的簡(jiǎn)單:
>>> 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')都很不錯(cuò)吧,但這也僅是Requests的冰山一角呢。
傳遞URL參數(shù)
你也許經(jīng)常想為URL的查詢字符串(query string)傳遞某種數(shù)據(jù)。如果你是手工創(chuàng)建URL,那么數(shù)據(jù)會(huì)以鍵/值對(duì)的形式置于URL中,跟在一個(gè)問(wèn)號(hào)的后面。例如,httpbin.org/get?key=val。Requests允許你使用params關(guān)鍵字參數(shù),以一個(gè)字符串字典來(lái)提供這些參數(shù)。舉例來(lái)說(shuō),如果你想傳遞key1=value1和key2=value2到httpbin.org/get,那么你可以使用如下代碼:
>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.get("http://httpbin.org/get", params=payload)
通過(guò)打印輸出該URL,你能看到URL已被正確解碼:
>>> print(r.url)
http://httpbin.org/get?key2=value2&key1=value1
注意字典里值為None的鍵都不會(huì)被添加到URL的查詢字符串里。
你還可以將一個(gè)列表作為值傳入:
>>> payload = {'key1': 'value1', 'key2': ['value2', 'value3']}
>>> r = requests.get('http://httpbin.org/get', params=payload)
>>> print(r.url)
http://httpbin.org/get?key1=value1&key2=value2&key2=value3
響應(yīng)內(nèi)容
我們能讀取服務(wù)器響應(yīng)的內(nèi)容。再次以Github時(shí)間線為例:
>>> import requests
>>> r = requests.get('https://api.github.com/events')
>>> r.text
u'[{"repository":{"open_issues":0,"url":"https://github.com/...
Requests會(huì)自動(dòng)解碼來(lái)自服務(wù)器的內(nèi)容。大多數(shù)unicode字符集都能被無(wú)縫地解碼。
請(qǐng)求發(fā)出后,Requests會(huì)基于HTTP頭部對(duì)響應(yīng)的編碼作出有根據(jù)的推測(cè)。當(dāng)你訪問(wèn)r.text之時(shí),Requests會(huì)使用其推測(cè)的文本編碼。你可以找出Requests使用了什么編碼,并且能夠使用r.encoding屬性來(lái)改變它:
>>> r.encoding
'utf-8'
>>> r.encoding = 'ISO-8859-1'
如果你改變了編碼,每當(dāng)你訪問(wèn)r.text,Request都將會(huì)使用r.encoding的新值。你可能希望在使用特殊邏輯計(jì)算出文本的編碼的情況下來(lái)修改編碼。比如HTTP和XML自身可以指定編碼。這樣的話,你應(yīng)該使用r.content來(lái)找到編碼,然后設(shè)置r.encoding為相應(yīng)的編碼。這樣就能使用正確的編碼解析r.text了。
在你需要的情況下,Requests也可以使用定制的編碼。如果你創(chuàng)建了自己的編碼,并使用codecs模塊來(lái)進(jìn)行注冊(cè),你就可以輕松地使用這個(gè)編碼器名稱作為r.encoding的值,然后由Requests來(lái)為你處理編碼。
二進(jìn)制響應(yīng)內(nèi)容
你也能以字節(jié)的方法訪問(wèn)請(qǐng)求響應(yīng)體,對(duì)于非文本請(qǐng)求:
>>> r.content
b'[{'repository":{"open_issues":0,"url":"https://github.com/...
Requests會(huì)自動(dòng)為你解碼gzip和deflate傳輸編碼的響應(yīng)數(shù)據(jù)。
例如,以請(qǐng)求返回的二進(jìn)制數(shù)據(jù)創(chuàng)建一張圖片,你可以使用如下代碼:
>>> from PIL import Image
>>> from io import BytesIO
>>> i = Image.open(BytesIO(r.content))
JSON響應(yīng)內(nèi)容
Requests中也有一個(gè)內(nèi)置的JSON解碼器,助你處理JSON數(shù)據(jù):
>>> import requests
>>> r = requests.get('https://api.github.com/events')
>>> r.json()
[{u'repository': {u'open_issues': 0, u'url': 'https://github.com/...
如果JSON解碼失敗,r.json()就會(huì)拋出一個(gè)異常。例如,響應(yīng)內(nèi)容是401(Unauthorized),嘗試訪問(wèn)r.json()將會(huì)拋出ValueError: No JSON object could be decoded異常。
需需要注意的是,成功調(diào)用r.json()并**不**意味著響應(yīng)的成功。有的服務(wù)器會(huì)在失敗的響應(yīng)中包含一個(gè)JSON對(duì)象(比如HTTP 500的錯(cuò)誤細(xì)節(jié))。這種JSON會(huì)被解碼返回。要檢查請(qǐng)求是否成功,請(qǐng)使用r.raise_for_status()或者檢查r.status_code是否和你的期望相同。
原始響應(yīng)內(nèi)容
在罕見(jiàn)的情況下,你可能想獲取來(lái)自服務(wù)器的原始套接字響應(yīng),那么你可以訪問(wèn)r.raw。如果你確實(shí)想這么干,那請(qǐng)你確保在初始請(qǐng)求中設(shè)置了stream=True。具體你可以這么做:
>>> r = requests.get('https://api.github.com/events', stream=True) >>> r.raw <requests.packages.urllib3.response.HTTPResponse object at 0x101194810> >>> r.raw.read(10) '\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'但一般情況下,你應(yīng)該以下面的模式將文本流保存到文件:
with open(filename, 'wb') as fd:
? ? for chunk in r.iter_content(chunk_size):
? ? ? ? fd.write(chunk)
使用Response.iter_content將會(huì)處理大量你直接使用Response.raw不得不處理的。當(dāng)流下載時(shí),上面是優(yōu)先推薦的獲取內(nèi)容方式。Note that chunk_size can be freely adjusted to a number that may better fit your use cases.
定制請(qǐng)求頭
如果你想為請(qǐng)求添加HTTP頭部,只要簡(jiǎn)單地傳遞一個(gè)dict給headers參數(shù)就可以了。
例如,在前一個(gè)示例中我們沒(méi)有指定content-type:
>>> url = 'https://api.github.com/some/endpoint'
>>> headers = {'user-agent': 'my-app/0.0.1'}
>>> r = requests.get(url, headers=headers)
注意:定制header的優(yōu)先級(jí)低于某些特定的信息源,例如:
注意:定制header的優(yōu)先級(jí)低于某些特定的信息源,例如:
- 如果在.netrc中設(shè)置了用戶認(rèn)證信息,使用headers=設(shè)置的授權(quán)就不會(huì)生效。而如果設(shè)置了auth=參數(shù),··.netrc``的設(shè)置就無(wú)效了。
- 如果被重定向到別的主機(jī),授權(quán)header就會(huì)被刪除。
- 代理授權(quán)header會(huì)被URL中提供的代理身份覆蓋掉。
- 在我們能判斷內(nèi)容長(zhǎng)度的情況下,header的Content-Length會(huì)被改寫。
更進(jìn)一步講,Requests不會(huì)基于定制header的具體情況改變自己的行為。只不過(guò)在最后的請(qǐng)求中,所有的header信息都會(huì)被傳遞出去。
注意:所有的header值必須是string、bytestring或者unicode。盡管傳遞unicode header也是允許的,但不建議這樣做。
更加復(fù)雜的POST請(qǐng)求
通常,你想要發(fā)送一些編碼為表單形式的數(shù)據(jù)----非常像一個(gè)HTML表單。要實(shí)現(xiàn)這個(gè),只需簡(jiǎn)單地傳遞一個(gè)字典給data參數(shù)。你的數(shù)據(jù)字典在發(fā)出請(qǐng)求時(shí)會(huì)自動(dòng)編碼為表單形式:
>>> payload = {'key1': 'value1', 'key2': 'value2'} >>> r = requests.post("http://httpbin.org/post", data=payload) >>> print(r.text) {..."form": {"key2": "value2","key1": "value1"},... }你還可以為data參數(shù)傳入一個(gè)元組列表。在表單中多個(gè)元素使用同一key的時(shí)候,這種方法尤其有效:
>>> payload = (('key1', 'value1'), ('key1', 'value2'))
>>> r = requests.port('http://httpbin.org/post', data=payload)
>>> print(r.text)
{
? ...
? "form": {
? ? "key1": [
? ? ? "value1",
? ? ? "value2"
? ? ]
? },
? ...
}
很多時(shí)候你想要發(fā)送的數(shù)據(jù)并非編碼為表單形式的。如果你傳遞一個(gè)string而不是一個(gè)dict,那么數(shù)據(jù)會(huì)被直接發(fā)布出去。
例如,Github API v3接收編碼為JSON的POST/PATCH數(shù)據(jù):
>>> import json
>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some': 'data'}
>>> r = request.post(url, data=json.dumps(payload))
此處除了可以自行對(duì)dict進(jìn)行編碼,你還可以使用json參數(shù)直接傳遞,然后它就會(huì)被自動(dòng)編碼。這是2.4.2版的新功能:
>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some': 'data'}
>>> r = requests.post(url, json=payload)
POST一個(gè)多部分編碼(Multipart-Encoded)的文件
Requests使得上傳多部分編碼文件變得很簡(jiǎn)單:
>>> url = 'http://httpbin.org/post'
>>> files = {'file': open('report.xls', 'rb')}
>>> r = requests.post(url, files=files)
>>> r.text
{
? ...
? "files": {
? ? "file": "<censored...binary...data>"
? },
? ...
}
你可以顯示地設(shè)置文件名,文件類型和請(qǐng)求頭:
>>> url = 'http://httpbin.org/post'
>>>files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})}
>>> r = requests.port(url, files=files)
>>> r.text
{
? ...
? "files": {
? ? "file": "<censored...binary...data>"
? },
? ...
}
如果你想,你也可以發(fā)送作為文件來(lái)接收的字符串:
>>> url = 'http://httpbin.org/post'
>>> files = {'file': ('report.csv', 'some,data,to,send\nanother,row,to,send\n')}
>>> r = requests.post(url, files=files)
>>> r.text
{
? ...
? "files": {
? ? "file": "some,data,to,send\\nanother,row,to,send\\n"
? },
? ...
}
如果你發(fā)送一個(gè)非常大的文件作為multipart/form-data請(qǐng)求,你可能希望將請(qǐng)求做成數(shù)據(jù)流。默認(rèn)下requests不支持,但有個(gè)第三方包requests-toolbelt是支持的。你以閱讀?toolbelt 文檔來(lái)了解使用方法。
在一個(gè)請(qǐng)求中發(fā)送多文件參考?高級(jí)用法?一節(jié)。
警告:
我們強(qiáng)烈建議你用二進(jìn)制模式(binary mode)打開(kāi)文件。這是因?yàn)镽equests可能會(huì)試圖為你提供Content-Length header,在它這樣做的時(shí)候,這個(gè)值會(huì)被設(shè)置為文件的字節(jié)數(shù)(bytes)。如果用文本模式(text mode)打開(kāi)文件。就可能會(huì)發(fā)送錯(cuò)誤。
響應(yīng)狀態(tài)碼
我們可以檢測(cè)響應(yīng)狀態(tài)碼:
>>> r = requests.get('http://httpbin.org/get')
>>> r.status_code
200
為方便引用,Requests還附帶了一個(gè)內(nèi)置的狀態(tài)碼查詢對(duì)象:
>>> r.status_code == requests.codes.ok
True
如果發(fā)送了一個(gè)錯(cuò)誤請(qǐng)求(一個(gè)4XX客戶端錯(cuò)誤,或者5XX服務(wù)器錯(cuò)誤響應(yīng)),我們可以通過(guò)Response.raise_for_status()來(lái)拋出異常:
>>> bad_r = requests.get('http://httpbin.org/status/404')
>>> bad_r.status_code
404
>>> bad_r.raise_for_status()
Traceback (most recent call last):
? File "requests/models.py", line 832, in raise_for_status
? ? raise http_error
Requests.execptions.HTTPError: 404 Client Error
但是,由于我們的例子中r的status_code是200,當(dāng)我們調(diào)用raise_for_status()時(shí),得到的是:
>>> r.raise_for_status()
None
一切都挺和諧哈。
響應(yīng)頭
我們可以查看以一個(gè)Python字典形式展示的服務(wù)器響應(yīng)頭:
>>> r.headers
{
? ? 'content-encoding': 'gzip';
? ? 'transfer-encoding': 'chunked',
? ? 'connection': 'close',
? ? 'server': 'nginx/1.0.4',
? ? 'x-runtime': '148ms',
? ? 'etag': '"e1ca502697e5c9317743dc078f67693f"',
? ? 'content-type': 'application/json'
}
但是這個(gè)字典比較特殊:它是僅為HTTP頭部而生的。根據(jù)?RFC 2616, HTTP 頭部是大小寫不敏感的。
因此,我們可以使用任意大寫形式來(lái)訪問(wèn)這些響應(yīng)頭字段:
>>> r.headers['Content-Type']
'application/json'
>>> r.headers.get('content-type')
'application/json'
它還有一個(gè)特殊點(diǎn),那就是服務(wù)器可以多次接受同一header,每次都使用不同的值。但Requests會(huì)將它們合并,這樣它們就可以用一個(gè)映射來(lái)表示出來(lái),參數(shù)RFC 7230:
A recipient MAY combine multiple header fields with the same field name into one "field-name: field-value" pair, without changing the semantics of the message, by appending each subsequent field value to the combined field value in order, separated by comma.
接收者可以合并多個(gè)相同名稱的header欄位,把他們何為一個(gè)"field-name:field-value"配對(duì),將每個(gè)后續(xù)的欄位值一次追加到合并的欄位值中,用逗號(hào)隔開(kāi)即可,這樣做不會(huì)改變信息的語(yǔ)義。
Cookie
如果某個(gè)響應(yīng)中包含一些cookie,你可以快速訪問(wèn)它們:
>>> url = 'http://example.com/some/cookie/setting/url'
>>> r = requests.get(url)
>>> r.cookies['example_cookie_name']
'example_cookie_value'
要想發(fā)送你的cookies到服務(wù)器,可以使用cookies參數(shù):
>>> url = 'http://httpbin.org/cookies'
>>> cookies = dict(cookies_are='working')
>>> r = requests.get(url, cookies=cookies)
>>> r.text
'{"cookies": {"cookies_are": "working"}}'
Cookie的返回對(duì)象為RequestsCookieJar,它的行為和字典類似,但接口更為完整,適合跨域名跨路徑使用。你還可以把CookieJar傳到Requests中:
>>> jar = requests.cookies.RequestsCookieJar()
>>> jar.set('tasty_cookie', 'yum', domain='httpbin.org', path='/cookies')
>>> jar.set('gross_cookie', 'blech', domain='httpbin.org', path='elsewhere')
>>> url = 'http://httpbin.org/cookies'
>>> r = requests.get(url, cookies=jar)
>>> r.text
'{"cookies": {"tasty_cookie": "yum"}}'
重定向與請(qǐng)求歷史
默認(rèn)情況下,除了HEAD,Requests會(huì)自動(dòng)處理所有重定向。
可以使用響應(yīng)對(duì)象的history方法來(lái)追蹤重定向。
Response.history是一個(gè)Response對(duì)象的列表,為了完成請(qǐng)求而創(chuàng)建了這些對(duì)象。這個(gè)對(duì)象列表按照從最老到最近的請(qǐng)求進(jìn)行排序。
例如,Github將所有的HTTP請(qǐng)求重定向到HTTPS:
>>> r = requests.get('http://github.com')
>>> r.url
'https://github.com/'
>>> r.status_code
200
>>> r.history
[<Response [301]>]
如果你使用的是GET、OPTIONS、POST、PUT、PATH或者DELETE,那么你可以通過(guò)allow_redirects參數(shù)禁用重定向處理:
>>> r = requests.get('http://github.com', allow_redirects=False)
>>> r.status_code
301
>>> r.history
[]
如果你使用了HEAD,你也可以啟用重定向:
>>> r = requests.head('http://github.com', allow_redirects=True)
>>> r.url
'https://github.com/'
>>> r.history
[<Response [301]>]
超時(shí)
你可以告訴requests在經(jīng)過(guò)以timeout參數(shù)設(shè)定的秒數(shù)時(shí)間之后停止等待響應(yīng)。基本上所有的生產(chǎn)代碼都應(yīng)該使用這一參數(shù)。如果不使用,你的程序可能會(huì)永遠(yuǎn)失去響應(yīng):
>>> 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)
注意:
timeout僅對(duì)連接過(guò)程有效,與響應(yīng)體的下載無(wú)關(guān)。timeout并不是整個(gè)下載響應(yīng)的時(shí)間限制,而是如果服務(wù)器在timeout秒內(nèi)沒(méi)有應(yīng)答,將會(huì)引發(fā)一個(gè)異常(更精確地說(shuō),是在timeout秒內(nèi)沒(méi)有從基礎(chǔ)套接字上接收到任何字節(jié)的數(shù)據(jù)時(shí)) If no timeout is specified explicitly, requests do not time out.
錯(cuò)誤與異常
遇到網(wǎng)絡(luò)問(wèn)題(如:DNS查詢失敗、拒絕連接等)時(shí),Requests會(huì)拋出一個(gè)ConnectionError異常。
如果HTTP請(qǐng)求返回了不成功的狀態(tài)碼,Response.raise_for_status()會(huì)拋出一個(gè)HTTPError異常。
若請(qǐng)求超時(shí),則拋出一個(gè)Timeout異常。
若請(qǐng)求超過(guò)了設(shè)定的最大重定向次數(shù),則會(huì)拋出一個(gè)TooManyRedirects異常。
所有Requests顯式拋出的異常都繼承自requests.exceptions.RequestException。
準(zhǔn)備好學(xué)習(xí)更多內(nèi)容了嗎?去?高級(jí)用法?一節(jié)看看吧。
?
?
?
總結(jié)
以上是生活随笔為你收集整理的Requests 2.18.1文档的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python字符串切割:str.spli
- 下一篇: Debian update apache