Requests快速上手
迫不及待了嗎?本頁內(nèi)容為如何入門 Requests 提供了很好的指引红氯。其假設你已經(jīng)安裝了 Requests。如果還沒有咕痛,去安裝一節(jié)看看吧痢甘。
首先,確認一下:
Requests?已安裝
Requests?是最新的
讓我們從一些簡單的示例開始吧暇检。
發(fā)送請求
使用 Requests 發(fā)送網(wǎng)絡請求非常簡單产阱。
一開始要導入 Requests 模塊:
>>> import requests
然后,嘗試獲取某個網(wǎng)頁块仆。本例子中构蹬,我們來獲取 Github 的公共時間線:
>>> r = requests.get('https://api.github.com/events')
現(xiàn)在,我們有一個名為?r?的?Response?對象悔据。我們可以從這個對象中獲取所有我們想要的信息庄敛。
Requests 簡便的 API 意味著所有 HTTP 請求類型都是顯而易見的。例如科汗,你可以這樣發(fā)送一個 HTTP POST 請求:
>>> r = requests.post('http://httpbin.org/post',data={'key':'value'})
漂亮藻烤,對吧?那么其他 HTTP 請求類型:PUT头滔,DELETE怖亭,HEAD 以及 OPTIONS 又是如何的呢?都是一樣的簡單:
>>> 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')
都很不錯吧坤检,但這也僅是 Requests 的冰山一角呢兴猩。
傳遞 URL 參數(shù)
你也許經(jīng)常想為 URL 的查詢字符串(query string)傳遞某種數(shù)據(jù)。如果你是手工構建 URL早歇,那么數(shù)據(jù)會以鍵/值對的形式置于 URL 中倾芝,跟在一個問號的后面。例如箭跳,?httpbin.org/get?key=val晨另。 Requests 允許你使用?params?關鍵字參數(shù),以一個字符串字典來提供這些參數(shù)谱姓。舉例來說借尿,如果你想傳遞?key1=value1?和?key2=value2?到?httpbin.org/get?,那么你可以使用如下代碼:
>>> payload = {'key1':'value1','key2':'value2'}
>>> r = requests.get("http://httpbin.org/get",params=payload)
通過打印輸出該 URL屉来,你能看到 URL 已被正確編碼:
>>> print(r.url)http://httpbin.org/get?key2=value2&key1=value1
注意字典里值為?None?的鍵都不會被添加到 URL 的查詢字符串里路翻。
你還可以將一個列表作為值傳入:
>>> 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
響應內(nèi)容
我們能讀取服務器響應的內(nèi)容。再次以 GitHub 時間線為例:
>>> import requests
>>> r = requests.get('https://api.github.com/events')
>>> r.textu'[{"repository":{"open_issues":0,"url":"https://github.com/...
Requests 會自動解碼來自服務器的內(nèi)容奶躯。大多數(shù) unicode 字符集都能被無縫地解碼帚桩。
請求發(fā)出后,Requests 會基于 HTTP 頭部對響應的編碼作出有根據(jù)的推測嘹黔。當你訪問?r.text?之時账嚎,Requests 會使用其推測的文本編碼。你可以找出 Requests 使用了什么編碼儡蔓,并且能夠使用r.encoding?屬性來改變它:
>>> r.encoding'utf-8'
>>> r.encoding = 'ISO-8859-1'
如果你改變了編碼郭蕉,每當你訪問?r.text?,Request 都將會使用?r.encoding?的新值喂江。你可能希望在使用特殊邏輯計算出文本的編碼的情況下來修改編碼召锈。比如 HTTP 和 XML 自身可以指定編碼。這樣的話获询,你應該使用?r.content?來找到編碼涨岁,然后設置?r.encoding?為相應的編碼拐袜。這樣就能使用正確的編碼解析?r.text?了。
在你需要的情況下梢薪,Requests 也可以使用定制的編碼蹬铺。如果你創(chuàng)建了自己的編碼,并使用?codecs模塊進行注冊秉撇,你就可以輕松地使用這個解碼器名稱作為?r.encoding?的值甜攀, 然后由 Requests 來為你處理編碼。
二進制響應內(nèi)容
你也能以字節(jié)的方式訪問請求響應體琐馆,對于非文本請求:
>>> r.contentb'[{"repository":{"open_issues":0,"url":"https://github.com/...
Requests 會自動為你解碼?gzip?和?deflate?傳輸編碼的響應數(shù)據(jù)规阀。
例如,以請求返回的二進制數(shù)據(jù)創(chuàng)建一張圖片瘦麸,你可以使用如下代碼:
>>> from PIL import Image
>>> from io import BytesIO
>>> i = Image.open(BytesIO(r.content))
JSON 響應內(nèi)容
Requests 中也有一個內(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()?就會拋出一個異常瞎暑。例如彤敛,響應內(nèi)容是 401 (Unauthorized),嘗試訪問?r.json()?將會拋出?ValueError:?No?JSON?object?could?be?decoded?異常了赌。
需要注意的是墨榄,成功調(diào)用?r.json()?并**不**意味著響應的成功。有的服務器會在失敗的響應中包含一個 JSON 對象(比如 HTTP 500 的錯誤細節(jié))勿她。這種 JSON 會被解碼返回袄秩。要檢查請求是否成功,請使用?r.raise_for_status()?或者檢查?r.status_code?是否和你的期望相同逢并。
原始響應內(nèi)容
在罕見的情況下之剧,你可能想獲取來自服務器的原始套接字響應,那么你可以訪問?r.raw砍聊。 如果你確實想這么干背稼,那請你確保在初始請求中設置了?stream=True。具體你可以這么做:
>>> r = requests.get('https://api.github.com/events',stream=True)
>>> r.raw
>>> r.raw.read(10)'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'
但一般情況下玻蝌,你應該以下面的模式將文本流保存到文件:
withopen(filename,'wb')asfd:forchunkinr.iter_content(chunk_size):fd.write(chunk)
使用?Response.iter_content?將會處理大量你直接使用?Response.raw?不得不處理的蟹肘。 當流下載時,上面是優(yōu)先推薦的獲取內(nèi)容方式俯树。 Note that?chunk_size?can be freely adjusted to a number that may better fit your use cases.
定制請求頭
如果你想為請求添加 HTTP 頭部帘腹,只要簡單地傳遞一個?dict?給?headers?參數(shù)就可以了。
例如许饿,在前一個示例中我們沒有指定 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)先級低于某些特定的信息源谭确,例如:
如果在?.netrc?中設置了用戶認證信息谬莹,使用?headers=?設置的授權就不會生效剃允。而如果設置了?auth=?參數(shù),``.netrc`` 的設置就無效了秽晚。
如果被重定向到別的主機,授權 header 就會被刪除赊窥。
代理授權 header 會被 URL 中提供的代理身份覆蓋掉爆惧。
在我們能判斷內(nèi)容長度的情況下狸页,header 的 Content-Length 會被改寫锨能。
更進一步講,Requests 不會基于定制 header 的具體情況改變自己的行為芍耘。只不過在最后的請求中址遇,所有的 header 信息都會被傳遞進去。
注意: 所有的 header 值必須是?string斋竞、bytestring 或者 unicode倔约。盡管傳遞 unicode header 也是允許的,但不建議這樣做坝初。
更加復雜的 POST 請求
通常浸剩,你想要發(fā)送一些編碼為表單形式的數(shù)據(jù)——非常像一個 HTML 表單。要實現(xiàn)這個鳄袍,只需簡單地傳遞一個字典給?data?參數(shù)绢要。你的數(shù)據(jù)字典在發(fā)出請求時會自動編碼為表單形式:
>>> payload = {'key1':'value1','key2':'value2'}
>>> r = requests.post("http://httpbin.org/post",data=payload)>>> print(r.text){? ...? "form": {? ? "key2": "value2",? ? "key1": "value1"? },? ...}
你還可以為?data?參數(shù)傳入一個元組列表。在表單中多個元素使用同一 key 的時候拗小,這種方式尤其有效:
>>> payload = (('key1','value1'),('key1','value2'))
>>> r = requests.post('http://httpbin.org/post',data=payload)
>>> print(r.text){? ...? "form": {? ? "key1": [? ? ? "value1",? ? ? "value2"? ? ]? },? ...}
很多時候你想要發(fā)送的數(shù)據(jù)并非編碼為表單形式的重罪。如果你傳遞一個?string?而不是一個?dict,那么數(shù)據(jù)會被直接發(fā)布出去哀九。
例如剿配,Github API v3 接受編碼為 JSON 的 POST/PATCH 數(shù)據(jù):
>>> import json
>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some':'data'}
>>> r = requests.post(url,data=json.dumps(payload))
此處除了可以自行對?dict?進行編碼,你還可以使用?json?參數(shù)直接傳遞阅束,然后它就會被自動編碼呼胚。這是 2.4.2 版的新加功能:
>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some':'data'}
>>> r = requests.post(url,json=payload)
POST一個多部分編碼(Multipart-Encoded)的文件
Requests 使得上傳多部分編碼文件變得很簡單:
>>> url = 'http://httpbin.org/post'
>>> files = {'file':open('report.xls','rb')}
>>> r = requests.post(url,files=files)
>>> r.text{? ...? "files": {? ? "file": ""? },? ...}
你可以顯式地設置文件名,文件類型和請求頭:
>>> url = 'http://httpbin.org/post'
>>> files = {'file':('report.xls',open('report.xls','rb'),'application/vnd.ms-excel',{'Expires':'0'})}
>>> r = requests.post(url,files=files)
>>> r.text{? ...? "files": {? ? "file": ""? },? ...}
如果你想息裸,你也可以發(fā)送作為文件來接收的字符串:
>>> 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ā)送一個非常大的文件作為?multipart/form-data?請求蝇更,你可能希望將請求做成數(shù)據(jù)流。默認下?requests?不支持, 但有個第三方包?requests-toolbelt?是支持的界牡。你可以閱讀?toolbelt 文檔來了解使用方法簿寂。
在一個請求中發(fā)送多文件參考?高級用法?一節(jié)。
警告
我們強烈建議你用二進制模式(binary mode)打開文件宿亡。這是因為 Requests 可能會試圖為你提供?Content-Length?header常遂,在它這樣做的時候,這個值會被設為文件的字節(jié)數(shù)(bytes)挽荠。如果用文本模式(text mode)打開文件克胳,就可能會發(fā)生錯誤平绩。
響應狀態(tài)碼
我們可以檢測響應狀態(tài)碼:
>>> r = requests.get('http://httpbin.org/get')
>>> r.status_code200
為方便引用,Requests還附帶了一個內(nèi)置的狀態(tài)碼查詢對象:
>>> r.status_code==requests.codes.okTrue
如果發(fā)送了一個錯誤請求(一個 4XX 客戶端錯誤漠另,或者 5XX 服務器錯誤響應)捏雌,我們可以通過Response.raise_for_status()?來拋出異常:
>>> bad_r = requests.get('http://httpbin.org/status/404')
>>> bad_r.status_code404
>>> bad_r.raise_for_status()Traceback (most recent call last):File"requests/models.py", line832, inraise_for_statusraisehttp_errorrequests.exceptions.HTTPError:404 Client Error
但是,由于我們的例子中?r?的?status_code?是?200?笆搓,當我們調(diào)用?raise_for_status()?時性湿,得到的是:
>>> r.raise_for_status()None
一切都挺和諧哈。
響應頭
我們可以查看以一個 Python 字典形式展示的服務器響應頭:
>>> r.headers{? ? 'content-encoding': 'gzip',? ? 'transfer-encoding': 'chunked',? ? 'connection': 'close',? ? 'server': 'nginx/1.0.4',? ? 'x-runtime': '148ms',? ? 'etag': '"e1ca502697e5c9317743dc078f67693f"',? ? 'content-type': 'application/json'}
但是這個字典比較特殊:它是僅為 HTTP 頭部而生的满败。根據(jù)?RFC 2616肤频, HTTP 頭部是大小寫不敏感的。
因此算墨,我們可以使用任意大寫形式來訪問這些響應頭字段:
>>> r.headers['Content-Type']'application/json'
>>> r.headers.get('content-type')'application/json'
它還有一個特殊點宵荒,那就是服務器可以多次接受同一 header,每次都使用不同的值净嘀。但 Requests 會將它們合并报咳,這樣它們就可以用一個映射來表示出來,參見?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 a comma.
接收者可以合并多個相同名稱的 header 欄位挖藏,把它們合為一個 "field-name: field-value" 配對暑刃,將每個后續(xù)的欄位值依次追加到合并的欄位值中,用逗號隔開即可熬苍,這樣做不會改變信息的語義稍走。
Cookie
如果某個響應中包含一些 cookie,你可以快速訪問它們:
>>> url = 'http://example.com/some/cookie/setting/url'
>>> r = requests.get(url)
>>> r.cookies['example_cookie_name']'example_cookie_value'
要想發(fā)送你的cookies到服務器柴底,可以使用?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 的返回對象為?RequestsCookieJar婿脸,它的行為和字典類似,但接口更為完整柄驻,適合跨域名跨路徑使用狐树。你還可以把 Cookie Jar 傳到 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"}}'
重定向與請求歷史
默認情況下,除了 HEAD, Requests 會自動處理所有重定向鸿脓。
可以使用響應對象的?history?方法來追蹤重定向抑钟。
Response.history?是一個?Response?對象的列表,為了完成請求而創(chuàng)建了這些對象野哭。這個對象列表按照從最老到最近的請求進行排序在塔。
例如,Github 將所有的 HTTP 請求重定向到 HTTPS:
>>> r = requests.get('http://github.com')
>>> r.url'https://github.com/'
>>> r.status_code200
>>> r.history[]
如果你使用的是GET拨黔、OPTIONS蛔溃、POST、PUT、PATCH 或者 DELETE贺待,那么你可以通過?allow_redirects?參數(shù)禁用重定向處理:
>>> r = requests.get('http://github.com',allow_redirects=False)
>>> r.status_code301
>>> r.history[]
如果你使用了 HEAD徽曲,你也可以啟用重定向:
>>> r=requests.head('http://github.com',allow_redirects=True)
>>> r.url'https://github.com/'
>>> r.history[]
超時
你可以告訴 requests 在經(jīng)過以?timeout?參數(shù)設定的秒數(shù)時間之后停止等待響應◆锶基本上所有的生產(chǎn)代碼都應該使用這一參數(shù)秃臣。如果不使用,你的程序可能會永遠失去響應:
>>> requests.get('http://github.com',timeout=0.001)Traceback (most recent call last):File"", line1, inrequests.exceptions.Timeout:HTTPConnectionPool(host='github.com', port=80): Request timed out. (timeout=0.001)
注意
timeout?僅對連接過程有效哪工,與響應體的下載無關奥此。?timeout?并不是整個下載響應的時間限制,而是如果服務器在?timeout?秒內(nèi)沒有應答正勒,將會引發(fā)一個異常(更精確地說得院,是在timeout?秒內(nèi)沒有從基礎套接字上接收到任何字節(jié)的數(shù)據(jù)時)If no timeout is specified explicitly, requests do not time out.
錯誤與異常
遇到網(wǎng)絡問題(如:DNS 查詢失敗、拒絕連接等)時章贞,Requests 會拋出一個?ConnectionError?異常。
如果 HTTP 請求返回了不成功的狀態(tài)碼非洲,?Response.raise_for_status()?會拋出一個?HTTPError異常鸭限。
若請求超時,則拋出一個?Timeout?異常两踏。
若請求超過了設定的最大重定向次數(shù)败京,則會拋出一個?TooManyRedirects?異常。
所有Requests顯式拋出的異常都繼承自?requests.exceptions.RequestException?梦染。
準備好學習更多內(nèi)容了嗎赡麦?去?高級用法?一節(jié)看看吧。