快速入門¶
迫不及待想開始使用 Requests 了嗎?本頁簡要介紹如何開始使用 Requests。
首先,請確認
讓我們開始看一些簡單的範例。
發送請求¶
使用 Requests 發送請求非常簡單。
首先匯入 Requests 模組
>>> import requests
現在,我們嘗試取得一個網頁。以取得 GitHub 的公開時間軸為例
>>> r = requests.get('https://api.github.com/events')
現在,我們有一個名為 r
的 Response
物件。我們可以從這個物件取得所有需要的資訊。
Requests 簡單的 API 表示所有形式的 HTTP 請求都很直觀。例如,以下是如何發送 HTTP POST 請求
>>> r = requests.post('https://httpbin.org/post', data={'key': 'value'})
很棒吧?其他 HTTP 請求類型呢:PUT、DELETE、HEAD 和 OPTIONS?它們也都一樣簡單
>>> r = requests.put('https://httpbin.org/put', data={'key': 'value'})
>>> r = requests.delete('https://httpbin.org/delete')
>>> r = requests.head('https://httpbin.org/get')
>>> r = requests.options('https://httpbin.org/get')
這些都很棒,但這也只是 Requests 功能的開始。
在 URL 中傳遞參數¶
您通常會想在 URL 的查詢字串中發送一些資料。如果您是手動建構 URL,這些資料會以鍵/值對的形式在問號後面的 URL 中給出,例如 httpbin.org/get?key=val
。Requests 允許您使用 params
關鍵字引數,以字串字典的形式提供這些引數。舉例來說,如果您想將 key1=value1
和 key2=value2
傳遞給 httpbin.org/get
,您可以使用以下程式碼
>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.get('https://httpbin.org/get', params=payload)
您可以透過列印 URL 來查看 URL 是否已正確編碼
>>> print(r.url)
https://httpbin.org/get?key2=value2&key1=value1
請注意,任何字典鍵的值為 None
都將不會被加入到 URL 的查詢字串中。
您也可以將項目列表作為值傳遞
>>> payload = {'key1': 'value1', 'key2': ['value2', 'value3']}
>>> r = requests.get('https://httpbin.org/get', params=payload)
>>> print(r.url)
https://httpbin.org/get?key1=value1&key2=value2&key2=value3
回應內容¶
我們可以讀取伺服器回應的內容。再次考慮 GitHub 時間軸
>>> import requests
>>> r = requests.get('https://api.github.com/events')
>>> r.text
'[{"repository":{"open_issues":0,"url":"https://github.com/...
Requests 會自動解碼伺服器的內容。大多數 Unicode 字元集都能無縫解碼。
當您發送請求時,Requests 會根據 HTTP 標頭對回應的編碼做出有根據的猜測。當您存取 r.text
時,會使用 Requests 猜測的文字編碼。您可以使用 r.encoding
屬性來找出 Requests 正在使用的編碼並進行更改
>>> r.encoding
'utf-8'
>>> r.encoding = 'ISO-8859-1'
如果您更改編碼,每當您呼叫 r.text
時,Requests 都會使用 r.encoding
的新值。在任何您可以應用特殊邏輯來計算內容編碼的情況下,您可能會想這樣做。例如,HTML 和 XML 能夠在其主體中指定其編碼。在這種情況下,您應該使用 r.content
來尋找編碼,然後設定 r.encoding
。這將讓您能夠使用具有正確編碼的 r.text
。
如果您需要自訂編碼,Requests 也會使用自訂編碼。如果您已建立自己的編碼並在 codecs
模組中註冊,您可以直接使用編碼解碼器名稱作為 r.encoding
的值,Requests 將為您處理解碼。
二進制回應內容¶
您也可以將回應主體作為位元組存取,用於非文字請求
>>> r.content
b'[{"repository":{"open_issues":0,"url":"https://github.com/...
gzip
和 deflate
傳輸編碼會自動為您解碼。
如果安裝了 Brotli 函式庫(例如 brotli 或 brotlicffi),則 br
傳輸編碼會自動為您解碼。
例如,要從請求傳回的二進制資料建立影像,您可以使用以下程式碼
>>> from PIL import Image
>>> from io import BytesIO
>>> i = Image.open(BytesIO(r.content))
JSON 回應內容¶
還有一個內建的 JSON 解碼器,以防您處理 JSON 資料
>>> import requests
>>> r = requests.get('https://api.github.com/events')
>>> r.json()
[{'repository': {'open_issues': 0, 'url': 'https://github.com/...
如果 JSON 解碼失敗,r.json()
會引發例外。例如,如果回應收到 204 (No Content),或者如果回應包含無效的 JSON,嘗試 r.json()
會引發 requests.exceptions.JSONDecodeError
。requests.exceptions.JSONDecodeError
這個包裝器例外為不同 Python 版本和 JSON 序列化函式庫可能拋出的多個例外提供互操作性。
應該注意的是,呼叫 r.json()
的成功不表示回應的成功。有些伺服器可能會在失敗的回應中傳回 JSON 物件(例如,帶有 HTTP 500 的錯誤詳細資訊)。此類 JSON 將被解碼並傳回。要檢查請求是否成功,請使用 r.raise_for_status()
或檢查 r.status_code
是否為您期望的值。
原始回應內容¶
在極少數情況下,您可能想要從伺服器取得原始的 socket 回應,您可以存取 r.raw
。如果您想這樣做,請確保在您的初始請求中設定 stream=True
。一旦您這樣做了,您就可以執行以下操作
>>> r = requests.get('https://api.github.com/events', stream=True)
>>> r.raw
<urllib3.response.HTTPResponse object at 0x101194810>
>>> r.raw.read(10)
b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'
但總體而言,您應該使用像這樣的模式來儲存正在串流到檔案的內容
with open(filename, 'wb') as fd:
for chunk in r.iter_content(chunk_size=128):
fd.write(chunk)
使用 Response.iter_content
將處理許多您在使用 Response.raw
時原本必須處理的事情。當串流下載時,以上是檢索內容的首選和建議方式。請注意,chunk_size
可以自由調整為更適合您用例的數字。
注意
關於使用 Response.iter_content
與 Response.raw
的一個重要注意事項。Response.iter_content
將自動解碼 gzip
和 deflate
傳輸編碼。Response.raw
是原始位元組串流 – 它不會轉換回應內容。如果您真的需要存取傳回的原始位元組,請使用 Response.raw
。
自訂標頭¶
如果您想在請求中加入 HTTP 標頭,只需將 dict
傳遞給 headers
參數即可。
例如,我們在之前的範例中沒有指定我們的 user-agent
>>> url = 'https://api.github.com/some/endpoint'
>>> headers = {'user-agent': 'my-app/0.0.1'}
>>> r = requests.get(url, headers=headers)
注意:自訂標頭的優先順序低於更具體的資訊來源。例如
如果憑證在
.netrc
中指定,則使用 headers= 設定的 Authorization 標頭將被覆寫,而.netrc
又將被auth=
參數覆寫。Requests 將在 ~/.netrc、~/_netrc 或 NETRC 環境變數指定的路徑中搜尋 netrc 檔案。如果您被重新導向到非主機,Authorization 標頭將被移除。
Proxy-Authorization 標頭將被 URL 中提供的 Proxy 憑證覆寫。
當我們可以確定內容長度時,Content-Length 標頭將被覆寫。
此外,Requests 完全不會根據指定的自訂標頭來更改其行為。標頭只是傳遞到最終請求中。
注意:所有標頭值都必須是 string
、位元組字串或 Unicode。雖然允許,但建議避免傳遞 Unicode 標頭值。
更複雜的 POST 請求¶
通常,您想要發送一些表單編碼的資料 — 很像 HTML 表單。為此,只需將字典傳遞給 data
引數。當發出請求時,您的資料字典將自動進行表單編碼
>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.post('https://httpbin.org/post', data=payload)
>>> print(r.text)
{
...
"form": {
"key2": "value2",
"key1": "value1"
},
...
}
data
引數也可以為每個鍵擁有多個值。這可以透過將 data
設定為元組列表或以列表作為值的字典來完成。當表單有多個元素使用相同的鍵時,這特別有用
>>> payload_tuples = [('key1', 'value1'), ('key1', 'value2')]
>>> r1 = requests.post('https://httpbin.org/post', data=payload_tuples)
>>> payload_dict = {'key1': ['value1', 'value2']}
>>> r2 = requests.post('https://httpbin.org/post', data=payload_dict)
>>> print(r1.text)
{
...
"form": {
"key1": [
"value1",
"value2"
]
},
...
}
>>> r1.text == r2.text
True
有時您可能想要發送未進行表單編碼的資料。如果您傳遞 string
而不是 dict
,則該資料將直接發佈。
例如,GitHub API v3 接受 JSON 編碼的 POST/PATCH 資料
>>> import json
>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some': 'data'}
>>> r = requests.post(url, data=json.dumps(payload))
請注意,上述程式碼將不會加入 Content-Type
標頭(因此特別是它不會將其設定為 application/json
)。
如果您需要設定該標頭,並且不想自己編碼 dict
,您也可以直接使用 json
參數(在版本 2.4.2 中加入)傳遞它,它將自動編碼
>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some': 'data'}
>>> r = requests.post(url, json=payload)
請注意,如果傳遞了 data
或 files
,則會忽略 json
參數。
POST 多部分編碼檔案¶
Requests 使上傳多部分編碼檔案變得簡單
>>> url = 'https://httpbin.org/post'
>>> files = {'file': open('report.xls', 'rb')}
>>> r = requests.post(url, files=files)
>>> r.text
{
...
"files": {
"file": "<censored...binary...data>"
},
...
}
您可以明確設定檔名、content_type 和標頭
>>> url = 'https://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": "<censored...binary...data>"
},
...
}
如果您願意,您可以發送字串以作為檔案接收
>>> url = 'https://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"
},
...
}
如果您要發佈非常大的檔案作為 multipart/form-data
請求,您可能想要串流請求。預設情況下,requests
不支援此功能,但有一個單獨的套件可以做到這一點 - requests-toolbelt
。您應該閱讀toolbelt 的文件,以取得有關如何使用它的更多詳細資訊。
有關在一個請求中發送多個檔案,請參閱進階章節。
警告
強烈建議您以二進制模式開啟檔案。這是因為 Requests 可能會嘗試為您提供 Content-Length
標頭,如果它這樣做,則此值將設定為檔案中的位元組數。如果您以文字模式開啟檔案,可能會發生錯誤。
回應狀態碼¶
我們可以檢查回應狀態碼
>>> r = requests.get('https://httpbin.org/get')
>>> r.status_code
200
Requests 也附帶一個內建的狀態碼查找物件,方便參考
>>> r.status_code == requests.codes.ok
True
如果我們發出了錯誤的請求(4XX 用戶端錯誤或 5XX 伺服器錯誤回應),我們可以透過 Response.raise_for_status()
引發它
>>> bad_r = requests.get('https://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.exceptions.HTTPError: 404 Client Error
但是,由於我們 r
的 status_code
為 200
,當我們呼叫 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 標頭而設計的。根據 RFC 7230,HTTP 標頭名稱不區分大小寫。
因此,我們可以使用任何我們想要的大小寫來存取標頭
>>> r.headers['Content-Type']
'application/json'
>>> r.headers.get('content-type')
'application/json'
它也很特別,因為伺服器可能會多次發送相同的標頭,但值不同,但 requests 會將它們組合起來,以便可以按照 RFC 7230 的規定,在單個映射中的字典中表示它們
接收者可以將具有相同欄位名稱的多個標頭欄位組合到一個 “field-name: field-value” 對中,而不會更改訊息的語義,方法是按順序將每個後續欄位值附加到組合的欄位值,並以逗號分隔。
重新導向和歷史記錄¶
預設情況下,Requests 將對 HEAD 以外的所有動詞執行位置重新導向。
我們可以使用 Response 物件的 history
屬性來追蹤重新導向。
Response.history
列表包含為了完成請求而建立的 Response
物件。列表從最舊的回應到最新的回應排序。
例如,GitHub 將所有 HTTP 請求重新導向到 HTTPS
>>> r = requests.get('http://github.com/')
>>> r.url
'https://github.com/'
>>> r.status_code
200
>>> r.history
[<Response [301]>]
如果您使用 GET、OPTIONS、POST、PUT、PATCH 或 DELETE,您可以使用 allow_redirects
參數停用重新導向處理
>>> 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]>]
逾時¶
您可以使用 timeout
參數告訴 Requests 在給定秒數後停止等待回應。幾乎所有生產程式碼都應該在幾乎所有請求中使用此參數。否則可能會導致您的程式無限期地掛起
>>> requests.get('https://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
不是整個回應下載的時間限制;相反,如果伺服器在 timeout
秒內沒有發出回應,則會引發例外(更準確地說,如果在底層 socket 上 timeout
秒內沒有收到任何位元組)。如果未明確指定逾時,則 requests 不會逾時。
錯誤和例外¶
如果發生網路問題(例如 DNS 失敗、拒絕連線等),Requests 將引發 ConnectionError
例外。
如果 HTTP 請求傳回不成功的狀態碼,Response.raise_for_status()
將引發 HTTPError
。
如果請求逾時,則會引發 Timeout
例外。
如果請求超過設定的最大重新導向次數,則會引發 TooManyRedirects
例外。
Requests 明確引發的所有例外都繼承自 requests.exceptions.RequestException
。
準備好了解更多了嗎?查看進階章節。