參考:http://www./2013/01/13/python-requests/
比urllib好用的requests
Python標(biāo)準(zhǔn)庫里提供了httplib以及urllib、urllib2,但是學(xué)習(xí)了好幾次,都沒有記?。ㄏ碌墓Ψ虿粔颍?。今天崔推薦了一個(gè)requests庫,看了一下樣例,幾乎立即就會(huì)使用了,所以推薦給大家。
看官方是怎么描述這種情況的:
“Python’s standard urllib2 module provides most of the HTTP capabilities you need, but the API is thoroughly broken. It was built for a different time — and a different web. It requires an enormous amount of work (even method overrides) to perform the simplest
of tasks.
Things shouldn’t be this way. Not in Python.”
http://docs./en/latest/
可見urllib2確實(shí)不太容易使用。
常用功能羅列如下,以便查詢。
# 0. 認(rèn)證、狀態(tài)碼、header、編碼、json
>>> r = requests.get('https://api.github.com/user', auth=('user', 'pass'))
>>> r.status_code
200
>>> r.headers['content-type']
'application/json; charset=utf8'
>>> r.encoding
'utf-8'
>>> r.text
u'{"type":"User"...'
>>> r.json()
{u'private_gists': 419, u'total_private_repos': 77, ...} # 1. 發(fā)起請(qǐng)求
import requests
URL="http://www./"
r = requests.get(URL)
r = requests.post(URL)
r = requests.put(URL)
r = requests.delete(URL)
r = requests.head(URL)
r = requests.options(URL) # 2. 通過URL傳遞參數(shù)
>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.get("http:///get", params=payload)
>>> print r.url
u'http:///get?key2=value2&key1=value1' # 3. 返回內(nèi)容
>>> import requests
>>> r = requests.get('https://github.com/timeline.json')
>>> r.text
'[{"repository":{"open_issues":0,"url":"https://github.com/...
>>> r.encoding
'utf-8'
>>> r.encoding = 'ISO-8859-1' # 4. 二進(jìn)制內(nèi)容
You can also access the response body as bytes, for non-text requests:
>>> r.content
b'[{"repository":{"open_issues":0,"url":"https://github.com/...
The gzip and deflate transfer-encodings are automatically decoded for you.
For example, to create an image from binary data returned by a request,
ou can use the following code:
>>> from PIL import Image
>>> from StringIO import StringIO
>>> i = Image.open(StringIO(r.content)) # 5. JSON
>>> import requests
>>> r = requests.get('https://github.com/timeline.json')
>>> r.json()
[{u'repository': {u'open_issues': 0, u'url': 'https://github.com/... # 6. 超時(shí)
>>> requests.get('http://github.com', timeout=0.001) # 7. 自定義header
>>> import json
>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some': 'data'}
>>> headers = {'content-type': 'application/json'}
>>> r = requests.post(url, data=json.dumps(payload), headers=headers)
參考:http://cn./en/latest/user/quickstart.html#id8
發(fā)送請(qǐng)求
使用Requests發(fā)送網(wǎng)絡(luò)請(qǐng)求非常簡單。
一開始要導(dǎo)入Requests模塊:
然后,嘗試獲取某個(gè)網(wǎng)頁。本例子中,我們來獲取Github的公共時(shí)間線
>>> r = requests.get('https://github.com/timeline.json')
現(xiàn)在,我們有一個(gè)名為 r 的 Response 對(duì)象??梢詮倪@個(gè)對(duì)象中獲取所有我們想要的信息。
Requests簡便的API意味著所有HTTP請(qǐng)求類型都是顯而易見的。例如,你可以這樣發(fā)送一個(gè)HTTP POST請(qǐng)求:
>>> r = requests.post("http:///post")
漂亮,對(duì)吧?那么其他HTTP請(qǐng)求類型:PUT, DELETE, HEAD以及OPTIONS又是如何的呢?都是一樣的簡單:
>>> r = requests.put("http:///put")
>>> r = requests.delete("http:///delete")
>>> r = requests.head("http:///get")
>>> r = requests.options("http:///get")
都很不錯(cuò)吧,但這也僅是Requests的冰山一角呢。
為URL傳遞參數(shù)
你也許經(jīng)常想為URL的查詢字符串(query string)傳遞某種數(shù)據(jù)。如果你是手工構(gòu)建URL,那么數(shù)據(jù)會(huì)以鍵/值 對(duì)的形式置于URL中,跟在一個(gè)問號(hào)的后面。例如,/get?key=val 。
Requests允許你使用 params 關(guān)鍵字參數(shù),以一個(gè)字典來提供這些參數(shù)。舉例來說,如果你想傳遞 key1=value1 和 key2=value2 到 /get ,那么你可以使用如下代碼:
>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.get("http:///get", params=payload)
通過打印輸出該URL,你能看到URL已被正確編碼:
>>> print r.url
u'http:///get?key2=value2&key1=value1'
響應(yīng)內(nèi)容
我們能讀取服務(wù)器響應(yīng)的內(nèi)容。再次以Github時(shí)間線為例:
>>> import requests
>>> r = requests.get('https://github.com/timeline.json')
>>> r.text
'[{"repository":{"open_issues":0,"url":"https://github.com/...
Requests會(huì)自動(dòng)解碼來自服務(wù)器的內(nèi)容。大多數(shù)unicode字符集都能被無縫地解碼。
請(qǐng)求發(fā)出后,Requests會(huì)基于HTTP頭部對(duì)響應(yīng)的編碼作出有根據(jù)的推測(cè)。當(dāng)你訪問r.text 之時(shí),Requests會(huì)使用其推測(cè)的文本編碼。你可以找出Requests使用了什么編碼,并且能夠使用 r.encoding 屬性來改變它:
>>> r.encoding
'utf-8'
>>> r.encoding = 'ISO-8859-1'
如果你改變了編碼,每當(dāng)你訪問 r.text ,Request都將會(huì)使用 r.encoding 的新值。
在你需要的情況下,Requests也可以使用定制的編碼。如果你創(chuàng)建了自己的編碼,并使用codecs 模塊進(jìn)行注冊(cè),你就可以輕松地使用這個(gè)解碼器名稱作為 r.encoding 的值,
然后由Requests來為你處理編碼。
二進(jìn)制響應(yīng)內(nèi)容
你也能以字節(jié)的方式訪問請(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 StringIO import StringIO
>>> i = Image.open(StringIO(r.content))
JSON響應(yīng)內(nèi)容
Requests中也有一個(gè)內(nèi)置的JSON解碼器,助你處理JSON數(shù)據(jù):
>>> import requests
>>> r = requests.get('https://github.com/timeline.json')
>>> r.json()
[{u'repository': {u'open_issues': 0, u'url': 'https://github.com/...
如果JSON解碼失敗, r.json 就會(huì)拋出一個(gè)異常。
原始響應(yīng)內(nèi)容
在罕見的情況下你可能想獲取來自服務(wù)器的原始套接字響應(yīng),那么你可以訪問 r.raw 。
如果你確實(shí)想這么干,那請(qǐng)你確保在初始請(qǐng)求中設(shè)置了 stream=True 。具體的你可以這么做:
>>> r = requests.get('https://github.com/timeline.json', 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'
定制請(qǐng)求頭
如果你想為請(qǐng)求添加HTTP頭部,只要簡單地傳遞一個(gè) dict 給 headers 參數(shù)就可以了。
例如,在前一個(gè)示例中我們沒有指定content-type:
>>> import json
>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some': 'data'}
>>> headers = {'content-type': 'application/json'}
>>> r = requests.post(url, data=json.dumps(payload), headers=headers)
更加復(fù)雜的POST請(qǐng)求
通常,你想要發(fā)送一些編碼為表單形式的數(shù)據(jù)—非常像一個(gè)HTML表單。 要實(shí)現(xiàn)這個(gè),只需簡單地傳遞一個(gè)字典給 data 參數(shù)。你的數(shù)據(jù)字典 在發(fā)出請(qǐng)求時(shí)會(huì)自動(dòng)編碼為表單形式:
>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.post("http:///post", data=payload)
>>> print r.text
{
...
"form": {
"key2": "value2",
"key1": "value1"
},
...
}
很多時(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 = requests.post(url, data=json.dumps(payload))
POST一個(gè)多部分編碼(Multipart-Encoded)的文件
Requests使得上傳多部分編碼文件變得很簡單:
>>> url = 'http:///post'
>>> files = {'file': open('report.xls', 'rb')}
>>> r = requests.post(url, files=files)
>>> r.text
{
...
"files": {
"file": "<censored...binary...data>"
},
...
}
你可以顯式地設(shè)置文件名:
>>> url = 'http:///post'
>>> files = {'file': ('report.xls', open('report.xls', 'rb'))}
>>> r = requests.post(url, files=files)
>>> r.text
{
...
"files": {
"file": "<censored...binary...data>"
},
...
}
如果你想,你也可以發(fā)送作為文件來接收的字符串:
>>> url = 'http:///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"
},
...
}
響應(yīng)狀態(tài)碼
我們可以檢測(cè)響應(yīng)狀態(tài)碼:
>>> r = requests.get('http:///get')
>>> r.status_code
200
為方便引用,Requests還附帶了一個(gè)內(nèi)置的狀態(tài)碼查詢對(duì)象:
>>> r.status_code == requests.codes.ok
True
如果發(fā)送了一個(gè)失敗請(qǐng)求(非200響應(yīng)),我們可以通過 Response.raise_for_status() 來拋出異常:
>>> bad_r = requests.get('http:///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.exceptions.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
{
'status': '200 OK',
'content-encoding': 'gzip',
'transfer-encoding': 'chunked',
'connection': 'close',
'server': 'nginx/1.0.4',
'x-runtime': '148ms',
'etag': '"e1ca502697e5c9317743dc078f67693f"',
'content-type': 'application/json; charset=utf-8'
}
但是這個(gè)字典比較特殊:它是僅為HTTP頭部而生的。根據(jù) RFC
2616 , HTTP頭部是大小寫不敏感的。
因此,我們可以使用任意大寫形式來訪問這些響應(yīng)頭字段:
>>> r.headers['Content-Type']
'application/json; charset=utf-8'
>>> r.headers.get('content-type')
'application/json; charset=utf-8'
如果某個(gè)響應(yīng)頭字段不存在,那么它的默認(rèn)值為 None
>>> r.headers['X-Random']
None
Cookies
如果某個(gè)響應(yīng)中包含一些Cookie,你可以快速訪問它們:
>>> url = 'http:///some/cookie/setting/url'
>>> r = requests.get(url)
>>> r.cookies['example_cookie_name']
'example_cookie_value'
要想發(fā)送你的cookies到服務(wù)器,可以使用 cookies 參數(shù):
>>> url = 'http:///cookies'
>>> cookies = dict(cookies_are='working')
>>> r = requests.get(url, cookies=cookies)
>>> r.text
'{"cookies": {"cookies_are": "working"}}'
重定向與請(qǐng)求歷史
使用GET或OPTIONS時(shí),Requests會(huì)自動(dòng)處理位置重定向。
Github將所有的HTTP請(qǐng)求重定向到HTTPS。可以使用響應(yīng)對(duì)象的 history 方法來追蹤重定向。
我們來看看Github做了什么:
>>> r = requests.get('http://github.com')
>>> r.url
'https://github.com/'
>>> r.status_code
200
>>> r.history
[<Response [301]>]
Response.history 是一個(gè):class:Request 對(duì)象的列表,為了完成請(qǐng)求而創(chuàng)建了這些對(duì)象。這個(gè)對(duì)象列表按照從最老到最近的請(qǐng)求進(jìn)行排序。
如果你使用的是GET或OPTIONS,那么你可以通過 allow_redirects 參數(shù)禁用重定向處理:
>>> r = requests.get('http://github.com', allow_redirects=False)
>>> r.status_code
301
>>> r.history
[]
如果你使用的是POST,PUT,PATCH,DELETE或HEAD,你也可以啟用重定向:
>>> r = requests.post('http://github.com', allow_redirects=True)
>>> r.url
'https://github.com/'
>>> r.history
[<Response [301]>]
超時(shí)
你可以告訴requests在經(jīng)過以 timeout 參數(shù)設(shè)定的秒數(shù)時(shí)間之后停止等待響應(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ì)連接過程有效,與響應(yīng)體的下載無關(guān)。
錯(cuò)誤與異常
遇到網(wǎng)絡(luò)問題(如:DNS查詢失敗、拒絕連接等)時(shí),Requests會(huì)拋出一個(gè)ConnectionError 異常。
遇到罕見的無效HTTP響應(yīng)時(shí),Requests則會(huì)拋出一個(gè) HTTPError 異常。
若請(qǐng)求超時(shí),則拋出一個(gè) Timeout 異常。
若請(qǐng)求超過了設(shè)定的最大重定向次數(shù),則會(huì)拋出一個(gè) TooManyRedirects 異常。
所有Requests顯式拋出的異常都繼承自 requests.exceptions.RequestException 。
高級(jí)用法
本篇文檔涵蓋了Requests的一些更加高級(jí)的特性。
會(huì)話對(duì)象
會(huì)話對(duì)象讓你能夠跨請(qǐng)求保持某些參數(shù)。它也會(huì)在同一個(gè)Session實(shí)例發(fā)出的所有請(qǐng)求之間保持cookies。
會(huì)話對(duì)象具有主要的Requests API的所有方法。
我們來跨請(qǐng)求保持一些cookies:
s = requests.Session()
s.get('http:///cookies/set/sessioncookie/123456789')
r = s.get("http:///cookies")
print r.text
# '{"cookies": {"sessioncookie": "123456789"}}'
會(huì)話也可用來為請(qǐng)求方法提供缺省數(shù)據(jù)。這是通過為會(huì)話對(duì)象的屬性提供數(shù)據(jù)來實(shí)現(xiàn)的:
s = requests.Session()
s.auth = ('user', 'pass')
s.headers.update({'x-test': 'true'})
# both 'x-test' and 'x-test2' are sent
s.get('http:///headers', headers={'x-test2': 'true'})
任何你傳遞給請(qǐng)求方法的字典都會(huì)與已設(shè)置會(huì)話層數(shù)據(jù)合并。方法層的參數(shù)覆蓋會(huì)話的參數(shù)。
從字典參數(shù)中移除一個(gè)值
有時(shí)你會(huì)想省略字典參數(shù)中一些會(huì)話層的鍵。要做到這一點(diǎn),你只需簡單地在方法層參數(shù)中將那個(gè)鍵的值設(shè)置為 None ,那個(gè)鍵就會(huì)被自動(dòng)省略掉。
包含在一個(gè)會(huì)話中的所有數(shù)據(jù)你都可以直接使用。學(xué)習(xí)更多細(xì)節(jié)請(qǐng)閱讀 會(huì)話API文檔 。
請(qǐng)求與響應(yīng)對(duì)象
任何時(shí)候調(diào)用requests.*()你都在做兩件主要的事情。其一,你在構(gòu)建一個(gè) Request 對(duì)象, 該對(duì)象將被發(fā)送到某個(gè)服務(wù)器請(qǐng)求或查詢一些資源。其二,一旦 requests 得到一個(gè)從
服務(wù)器返回的響應(yīng)就會(huì)產(chǎn)生一個(gè) Response 對(duì)象。該響應(yīng)對(duì)象包含服務(wù)器返回的所有信息,
也包含你原來創(chuàng)建的 Request 對(duì)象。如下是一個(gè)簡單的請(qǐng)求,從Wikipedia的服務(wù)器得到
一些非常重要的信息:
>>> r = requests.get('http://en./wiki/Monty_Python')
如果想訪問服務(wù)器返回給我們的響應(yīng)頭部信息,可以這樣做:
>>> r.headers
{'content-length': '56170', 'x-content-type-options': 'nosniff', 'x-cache':
'HIT from cp1006.eqiad.wmnet, MISS from cp1010.eqiad.wmnet', 'content-encoding':
'gzip', 'age': '3080', 'content-language': 'en', 'vary': 'Accept-Encoding,Cookie',
'server': 'Apache', 'last-modified': 'Wed, 13 Jun 2012 01:33:50 GMT',
'connection': 'close', 'cache-control': 'private, s-maxage=0, max-age=0,
must-revalidate', 'date': 'Thu, 14 Jun 2012 12:59:39 GMT', 'content-type':
'text/html; charset=UTF-8', 'x-cache-lookup': 'HIT from cp1006.eqiad.wmnet:3128,
MISS from cp1010.eqiad.wmnet:80'}
然而,如果想得到發(fā)送到服務(wù)器的請(qǐng)求的頭部,我們可以簡單地訪問該請(qǐng)求,然后是該請(qǐng)求的頭部:
>>> r.request.headers
{'Accept-Encoding': 'identity, deflate, compress, gzip',
'Accept': '*/*', 'User-Agent': 'python-requests/0.13.1'}
SSL證書驗(yàn)證
Requests可以為HTTPS請(qǐng)求驗(yàn)證SSL證書,就像web瀏覽器一樣。要想檢查某個(gè)主機(jī)的SSL證書,你可以使用 verify 參數(shù):
>>> requests.get('https://', verify=True)
requests.exceptions.SSLError: hostname '' doesn't match either of '*.herokuapp.com', 'herokuapp.com'
在該域名上我沒有設(shè)置SSL,所以失敗了。但Github設(shè)置了SSL:
>>> requests.get('https://github.com', verify=True)
<Response [200]>
對(duì)于私有證書,你也可以傳遞一個(gè)CA_BUNDLE文件的路徑給 verify 。你也可以設(shè)置REQUEST_CA_BUNDLE 環(huán)境變量。
如果你將 verify 設(shè)置為False,Requests也能忽略對(duì)SSL證書的驗(yàn)證。
>>> requests.get('https://', verify=False)
<Response [200]>
默認(rèn)情況下, verify 是設(shè)置為True的。選項(xiàng) verify 僅應(yīng)用于主機(jī)證書。
你也可以指定一個(gè)本地證書用作客戶端證書,可以是單個(gè)文件(包含密鑰和證書)或一個(gè)包含兩個(gè)文件路徑的元組:
>>> requests.get('https://', cert=('/path/server.crt', '/path/key'))
<Response [200]>
如果你指定了一個(gè)錯(cuò)誤路徑或一個(gè)無效的證書:
>>> requests.get('https://', cert='/wrong_path/server.pem')
SSLError: [Errno 336265225] _ssl.c:347: error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib
響應(yīng)體內(nèi)容工作流
默認(rèn)情況下,當(dāng)你進(jìn)行網(wǎng)絡(luò)請(qǐng)求后,響應(yīng)體會(huì)立即被下載。你可以通過 stream 參數(shù)覆蓋這個(gè)行為,推遲下載響應(yīng)體直到訪問 Response.content 屬性:
tarball_url = 'https://github.com/kennethreitz/requests/tarball/master'
r = requests.get(tarball_url, stream=True)
此時(shí)僅有響應(yīng)頭被下載下來了,連接保持打開狀態(tài),因此允許我們根據(jù)條件獲取內(nèi)容:
if int(r.headers['content-length']) < TOO_LONG:
content = r.content
...
你可以進(jìn)一步使用 Response.iter_content 和 Response.iter_lines 方法來控制工作流,或者以 Response.raw 從底層urllib3的 urllib3.HTTPResponse 讀取。
保持活動(dòng)狀態(tài)(持久連接)
好消息 - 歸功于urllib3,同一會(huì)話內(nèi)的持久連接是完全自動(dòng)處理的!同一會(huì)話內(nèi)你發(fā)出的任何請(qǐng)求都會(huì)自動(dòng)復(fù)用恰當(dāng)?shù)倪B接!
注意:只有所有的響應(yīng)體數(shù)據(jù)被讀取完畢連接才會(huì)被釋放為連接池;所以確保將 stream設(shè)置為 False 或讀取 Response 對(duì)象的 content 屬性。
流式上傳
Requests支持流式上傳,這允許你發(fā)送大的數(shù)據(jù)流或文件而無需先把它們讀入內(nèi)存。要使用流式上傳,僅需為你的請(qǐng)求體提供一個(gè)類文件對(duì)象即可:
with open('massive-body') as f:
requests.post('http://some.url/streamed', data=f)
塊編碼請(qǐng)求
對(duì)于出去和進(jìn)來的請(qǐng)求,Requests也支持分塊傳輸編碼。要發(fā)送一個(gè)塊編碼的請(qǐng)求,僅需為你的請(qǐng)求體提供一個(gè)生成器(或任意沒有具體長度(without a length)的迭代器):
def gen():
yield 'hi'
yield 'there'
requests.post('http://some.url/chunked', data=gen())
事件掛鉤
Requests有一個(gè)鉤子系統(tǒng),你可以用來操控部分請(qǐng)求過程,或信號(hào)事件處理。
可用的鉤子:
response:
從一個(gè)請(qǐng)求產(chǎn)生的響應(yīng)
你可以通過傳遞一個(gè) {hook_name: callback_function} 字典給 hooks 請(qǐng)求參數(shù)
為每個(gè)請(qǐng)求分配一個(gè)鉤子函數(shù):
hooks=dict(response=print_url)
callback_function 會(huì)接受一個(gè)數(shù)據(jù)塊作為它的第一個(gè)參數(shù)。
def print_url(r):
print(r.url)
若執(zhí)行你的回調(diào)函數(shù)期間發(fā)生錯(cuò)誤,系統(tǒng)會(huì)給出一個(gè)警告。
若回調(diào)函數(shù)返回一個(gè)值,默認(rèn)以該值替換傳進(jìn)來的數(shù)據(jù)。若函數(shù)未返回任何東西, 也沒有什么其他的影響。
我們來在運(yùn)行期間打印一些請(qǐng)求方法的參數(shù):
>>> requests.get('http://', hooks=dict(response=print_url))
http://
<Response [200]>
自定義身份驗(yàn)證
Requests允許你使用自己指定的身份驗(yàn)證機(jī)制。
任何傳遞給請(qǐng)求方法的 auth 參數(shù)的可調(diào)用對(duì)象,在請(qǐng)求發(fā)出之前都有機(jī)會(huì)修改請(qǐng)求。
自定義的身份驗(yàn)證機(jī)制是作為 requests.auth.AuthBase 的子類來實(shí)現(xiàn)的,也非常容易定義。
Requests在 requests.auth 中提供了兩種常見的的身份驗(yàn)證方案: HTTPBasicAuth 和HTTPDigestAuth 。
假設(shè)我們有一個(gè)web服務(wù),僅在 X-Pizza 頭被設(shè)置為一個(gè)密碼值的情況下才會(huì)有響應(yīng)。雖然這不太可能,
但就以它為例好了
from requests.auth import AuthBase
class PizzaAuth(AuthBase):
"""Attaches HTTP Pizza Authentication to the given Request object."""
def __init__(self, username):
# setup any auth-related data here
self.username = username
def __call__(self, r):
# modify and return the request
r.headers['X-Pizza'] = self.username
return r
然后就可以使用我們的PizzaAuth來進(jìn)行網(wǎng)絡(luò)請(qǐng)求:
>>> requests.get('http:///admin', auth=PizzaAuth('kenneth'))
<Response [200]>
流式請(qǐng)求
使用 requests.Response.iter_lines() 你可以很方便地對(duì)流式API(例如 Twitter的流式API )進(jìn)行迭代。
使用Twitter流式API來追蹤關(guān)鍵字“requests”:
import requests
import json
r = requests.post('https://stream.twitter.com/1/statuses/filter.json',
data={'track': 'requests'}, auth=('username', 'password'), stream=True)
for line in r.iter_lines():
if line: # filter out keep-alive new lines
print json.loads(line)
代理
如果需要使用代理,你可以通過為任意請(qǐng)求方法提供 proxies 參數(shù)來配置單個(gè)請(qǐng)求:
import requests
proxies = {
"http": "http://10.10.1.10:3128",
"https": "http://10.10.1.10:1080",
}
requests.get("http://", proxies=proxies)
你也可以通過環(huán)境變量 HTTP_PROXY 和 HTTPS_PROXY 來配置代理。
$ export HTTP_PROXY="http://10.10.1.10:3128"
$ export HTTPS_PROXY="http://10.10.1.10:1080"
$ python
>>> import requests
>>> requests.get("http://")
若你的代理需要使用HTTP Basic Auth,可以使用 http://user:password@host/ 語法:
proxies = {
"http": "http://user:pass@10.10.1.10:3128/",
}
合規(guī)性
Requests符合所有相關(guān)的規(guī)范和RFC,這樣不會(huì)為用戶造成不必要的困難。但這種對(duì)規(guī)范的考慮 導(dǎo)致一些行為對(duì)于不熟悉相關(guān)規(guī)范的人來說看似有點(diǎn)奇怪。
編碼方式
當(dāng)你收到一個(gè)響應(yīng)時(shí),Requests會(huì)猜測(cè)響應(yīng)的編碼方式,用于在你調(diào)用 Response.text 方法時(shí)
對(duì)響應(yīng)進(jìn)行解碼。Requests首先在HTTP頭部檢測(cè)是否存在指定的編碼方式,如果不存在,則會(huì)使用 charade 來嘗試猜測(cè)編碼方式。
只有當(dāng)HTTP頭部不存在明確指定的字符集,并且 Content-Type 頭部字段包含 text 值之時(shí),
Requests才不去猜測(cè)編碼方式。
在這種情況下, RFC
2616 指定默認(rèn)字符集 必須是 ISO-8859-1 。Requests遵從這一規(guī)范。如果你需要一種不同的編碼方式,你可以手動(dòng)設(shè)置 Response.encoding 屬性,或使用原始的 Response.content 。
HTTP動(dòng)詞
Requests提供了幾乎所有HTTP動(dòng)詞的功能:GET,OPTIONS, HEAD,POST,PUT,PATCH和DELETE。 以下內(nèi)容為使用Requests中的這些動(dòng)詞以及Github API提供了詳細(xì)示例。
我將從最常使用的動(dòng)詞GET開始。HTTP GET是一個(gè)冪等的方法,從給定的URL返回一個(gè)資源。因而, 當(dāng)你試圖從一個(gè)web位置獲取數(shù)據(jù)之時(shí),你應(yīng)該使用這個(gè)動(dòng)詞。一個(gè)使用示例是嘗試從Github上獲取 關(guān)于一個(gè)特定commit的信息。假設(shè)我們想獲取Requests的commit a050faf 的信息。我們可以
這樣去做:
>>> import requests
>>> r = requests.get('https://api.github.com/repos/kennethreitz/requests/git/commits/a050faf084662f3a352dd1a941f2c7c9f886d4ad')
我們應(yīng)該確認(rèn)Github是否正確響應(yīng)。如果正確響應(yīng),我們想弄清響應(yīng)內(nèi)容是什么類型的。像這樣去做:
>>> if (r.status_code == requests.codes.ok):
... print r.headers['content-type']
...
application/json; charset=utf-8
可見,GitHub返回了JSON數(shù)據(jù),非常好,這樣就可以使用 r.json 方法把這個(gè)返回的數(shù)據(jù)解析成Python對(duì)象。
>>> commit_data = r.json()
>>> print commit_data.keys()
[u'committer', u'author', u'url', u'tree', u'sha', u'parents', u'message']
>>> print commit_data[u'committer']
{u'date': u'2012-05-10T11:10:50-07:00', u'email': u'me@', u'name': u'Kenneth Reitz'}
>>> print commit_data[u'message']
makin' history
到目前為止,一切都非常簡單。嗯,我們來研究一下GitHub的API。我們可以去看看文檔, 但如果使用Requests來研究也許會(huì)更有意思一點(diǎn)。我們可以借助Requests的OPTIONS動(dòng)詞來看看我們剛使用過的url 支持哪些HTTP方法。
>>> verbs = requests.options(r.url)
>>> verbs.status_code
500
額,這是怎么回事?毫無幫助嘛!原來GitHub,與許多API提供方一樣,實(shí)際上并未實(shí)現(xiàn)OPTIONS方法。 這是一個(gè)惱人的疏忽,但沒關(guān)系,那我們可以使用枯燥的文檔。然而,如果GitHub正確實(shí)現(xiàn)了OPTIONS, 那么服務(wù)器應(yīng)該在響應(yīng)頭中返回允許用戶使用的HTTP方法,例如
>>> verbs = requests.options('http:///api/cats')
>>> print verbs.headers['allow']
GET,HEAD,POST,OPTIONS
轉(zhuǎn)而去查看文檔,我們看到對(duì)于提交信息,另一個(gè)允許的方法是POST,它會(huì)創(chuàng)建一個(gè)新的提交。 由于我們正在使用Requests代碼庫,我們應(yīng)盡可能避免對(duì)它發(fā)送笨拙的POST。作為替代,我們來 玩玩GitHub的Issue特性。
本篇文檔是回應(yīng)Issue #482而添加的。鑒于該問題已經(jīng)存在,我們就以它為例。先獲取它。
>>> r = requests.get('https://api.github.com/repos/kennethreitz/requests/issues/482')
>>> r.status_code
200
>>> issue = json.loads(r.text)
>>> print issue[u'title']
Feature any http verb in docs
>>> print issue[u'comments']
3
Cool,有3個(gè)評(píng)論。我們來看一下最后一個(gè)評(píng)論。
>>> r = requests.get(r.url + u'/comments')
>>> r.status_code
200
>>> comments = r.json()
>>> print comments[0].keys()
[u'body', u'url', u'created_at', u'updated_at', u'user', u'id']
>>> print comments[2][u'body']
Probably in the "advanced" section
嗯,那看起來似乎是個(gè)愚蠢之處。我們發(fā)表個(gè)評(píng)論來告訴這個(gè)評(píng)論者他自己的愚蠢。那么,這個(gè)評(píng)論者是誰呢?
>>> print comments[2][u'user'][u'login']
kennethreitz
好,我們來告訴這個(gè)叫肯尼思的家伙,這個(gè)例子應(yīng)該放在快速上手指南中。根據(jù)GitHub API文檔, 其方法是POST到該話題。我們來試試看。
>>> body = json.dumps({u"body": u"Sounds great! I'll get right on it!"})
>>> url = u"https://api.github.com/repos/kennethreitz/requests/issues/482/comments"
>>> r = requests.post(url=url, data=body)
>>> r.status_code
404
額,這有點(diǎn)古怪哈??赡芪覀冃枰?yàn)證身份。那就有點(diǎn)糾結(jié)了,對(duì)吧?不對(duì)。Requests簡化了多種身份驗(yàn)證形式的使用, 包括非常常見的Basic Auth。
>>> from requests.auth import HTTPBasicAuth
>>> auth = HTTPBasicAuth('fake@', 'not_a_real_password')
>>> r = requests.post(url=url, data=body, auth=auth)
>>> r.status_code
201
>>> content = r.json()
>>> print content[u'body']
Sounds great! I'll get right on it.
精彩!噢,不!我原本是想說等我一會(huì),因?yàn)槲业萌ノ挂幌挛业呢垺H绻夷軌蚓庉嬤@條評(píng)論那就好了! 幸運(yùn)的是,GitHub允許我們使用另一個(gè)HTTP動(dòng)詞,PATCH,來編輯評(píng)論。我們來試試。
>>> print content[u"id"]
5804413
>>> body = json.dumps({u"body": u"Sounds great! I'll get right on it once I feed my cat."})
>>> url = u"https://api.github.com/repos/kennethreitz/requests/issues/comments/5804413"
>>> r = requests.patch(url=url, data=body, auth=auth)
>>> r.status_code
200
非常好?,F(xiàn)在,我們來折磨一下這個(gè)叫肯尼思的家伙,我決定要讓他急得團(tuán)團(tuán)轉(zhuǎn),也不告訴他是我在搗蛋。 這意味著我想刪除這條評(píng)論。GitHub允許我們使用完全名副其實(shí)的DELETE方法來刪除評(píng)論。我們來清除該評(píng)論。
>>> r = requests.delete(url=url, auth=auth)
>>> r.status_code
204
>>> r.headers['status']
'204 No Content'
很好。不見了。最后一件我想知道的事情是我已經(jīng)使用了多少限額(ratelimit)。查查看,GitHub在響應(yīng)頭部發(fā)送這個(gè)信息, 因此不必下載整個(gè)網(wǎng)頁,我將使用一個(gè)HEAD請(qǐng)求來獲取響應(yīng)頭。
>>> r = requests.head(url=url, auth=auth)
>>> print r.headers
...
'x-ratelimit-remaining': '4995'
'x-ratelimit-limit': '5000'
...
很好。是時(shí)候?qū)憘€(gè)Python程序以各種刺激的方式濫用GitHub的API,還可以使用4995次呢。
響應(yīng)頭鏈接字段
許多HTTP API都有響應(yīng)頭鏈接字段的特性,它們使得API能夠更好地自我描述和自我顯露。
GitHub在API中為 分頁 使用這些特性,例如:
>>> url = 'https://api.github.com/users/kennethreitz/repos?page=1&per_page=10'
>>> r = requests.head(url=url)
>>> r.headers['link']
'<https://api.github.com/users/kennethreitz/repos?page=2&per_page=10>; rel="next", <https://api.github.com/users/kennethreitz/repos?page=6&per_page=10>; rel="last"'
Requests會(huì)自動(dòng)解析這些響應(yīng)頭鏈接字段,并使得它們非常易于使用:
>>> r.links["next"]
{'url': 'https://api.github.com/users/kennethreitz/repos?page=2&per_page=10', 'rel': 'next'}
>>> r.links["last"]
{'url': 'https://api.github.com/users/kennethreitz/repos?page=7&per_page=10', 'rel': 'last'}
|