快速入門

迫不及待想開始使用 Requests 了嗎?本頁簡要介紹如何開始使用 Requests。

首先,請確認

讓我們開始看一些簡單的範例。

發送請求

使用 Requests 發送請求非常簡單。

首先匯入 Requests 模組

>>> import requests

現在,我們嘗試取得一個網頁。以取得 GitHub 的公開時間軸為例

>>> r = requests.get('https://api.github.com/events')

現在,我們有一個名為 rResponse 物件。我們可以從這個物件取得所有需要的資訊。

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=value1key2=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/...

gzipdeflate 傳輸編碼會自動為您解碼。

如果安裝了 Brotli 函式庫(例如 brotlibrotlicffi),則 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.JSONDecodeErrorrequests.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_contentResponse.raw 的一個重要注意事項。Response.iter_content 將自動解碼 gzipdeflate 傳輸編碼。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~/_netrcNETRC 環境變數指定的路徑中搜尋 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)

請注意,如果傳遞了 datafiles,則會忽略 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

但是,由於我們 rstatus_code200,當我們呼叫 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” 對中,而不會更改訊息的語義,方法是按順序將每個後續欄位值附加到組合的欄位值,並以逗號分隔。

Cookie

如果回應包含一些 Cookie,您可以快速存取它們

>>> url = 'http://example.com/some/cookie/setting/url'
>>> r = requests.get(url)

>>> r.cookies['example_cookie_name']
'example_cookie_value'

要將您自己的 Cookie 發送到伺服器,您可以使用 cookies 參數

>>> url = 'https://httpbin.org/cookies'
>>> cookies = dict(cookies_are='working')

>>> r = requests.get(url, cookies=cookies)
>>> r.text
'{"cookies": {"cookies_are": "working"}}'

Cookie 會在 RequestsCookieJar 中傳回,它的作用類似於 dict,但也提供了更完整的介面,適用於跨多個網域或路徑使用。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 = 'https://httpbin.org/cookies'
>>> r = requests.get(url, cookies=jar)
>>> r.text
'{"cookies": {"tasty_cookie": "yum"}}'

重新導向和歷史記錄

預設情況下,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


準備好了解更多了嗎?查看進階章節。