首頁 > 後端開發 > Python教學 > 如何使用Python的Requests函式庫?

如何使用Python的Requests函式庫?

WBOY
發布: 2023-05-09 16:16:16
轉載
1391 人瀏覽過

快速開始

傳送請求

>>> import requests
>>> r = requests.get('https://api.github.com/events') # GET
>>> r = requests.post('https://httpbin.org/post', data={'key': 'value'}) # POST
>>> r = requests.put('https://httpbin.org/put', data={'key': 'value'}) # PUT
>>> r = requests.delete('https://httpbin.org/delete') # DELETE
>>> r = requests.head('https://httpbin.org/get') # HEAD 
>>> r = requests.options('https://httpbin.org/get') # OPTIONS
登入後複製

URL傳參

#可以使用params字典參數為URL提供查詢字串參數,例如,造訪 https://httpbin.org/get?key1=value1&key2=value2,可使用下列程式碼:

>>> import requests
>>> payload = {'key1': 'value1', 'key2': 'value2', 'key3':'', 'key4':None}
>>> r = requests.get('https://httpbin.org/get', params=payload)
>>> r.url
https://httpbin.org/get?key2=value2&key1=value1&key3=
登入後複製

需要注意的是,如果字典參數中key值(即URL參數的值為None),則該參數不會加入到URL的查詢字串中。

如果URL查詢字串中,存在重複參數(參數名稱相同,參數值不同),則需要將key值設定為由參數值組成的列表,如下:

>>> import requests
>>> payload = {'key1': 'value1', 'key2': ['value2', 'value3']}
>>> r = requests.get('https://httpbin.org/get', params=payload)
>>> r.url
https://httpbin.org/get?key1=value1&key2=value2&key2=value3
登入後複製

回應內容

讀取伺服器回應內容

>>> import requests
>>> r = requests.get('https://api.github.com/events')
>>> r.text
<class &#39;str&#39;> [{"id":"27579847062","type":"PushEvent","actor":{"...
登入後複製

requests 將自動解碼來自伺服器的內容。大多數unicode字元集都是無縫解碼的。

當你發出請求時,requests會根據HTTP頭對回應的編碼進行有依據的猜測。當你造訪r.text時,將使用requests猜測的文字編碼。可以使用r.encoding屬性來尋找請求使用的編碼,並對其進行更改:

>>> r.encoding # 输出:utf-8
r.encoding = &#39;ISO-8859-1&#39;
登入後複製

如果更改編碼,則每當呼叫r.text時, requests都會使用新的r.encoding的值。在任何情況下,你都可以應用特殊邏輯來決定內容的編碼。例如,HTML和XML可以在其正文中指定其編碼。在這種情況下,你應該使用r.content來尋找編碼,然後設定r.encoding。這將允許你使用具有正確編碼的r.text

requests也會在需要時使用自訂編碼。如果你已經創建了自己的編碼並將其註冊到codecs模組,則可以簡單地使用codec名稱作為r.encoding的值,而requests將為你處理解碼。

二元回應內容

對於非文字請求,也可以以位元組的形式存取回應體(當然,文字請求也可以):

>>> r.content
b&#39;[{"id":"27581220674","type":"IssueCommentEvent","actor":{"id":327807...
登入後複製

requests會自動解碼gzipdeflate傳輸編碼。

如果安裝了類似 brotli 或 brotlicffi的Brotil類別庫,Requets也會自動介面br傳輸編碼

如果Brotli庫(如[Brotli])為您自動解碼br傳輸編碼(https://pypi.org/project/brotli)或brotliffi已安裝。

例如,可以使用以下程式碼,從請求傳回的二進位資料建立映像:

from PIL import Image
from io import BytesIO
img = Image.open(BytesIO(r.content))
登入後複製

JSON回應內容

可使用內建的JSON解碼器,處理JSON資料:

>>> import requests
>>> r = requests.get(&#39;https://api.github.com/events&#39;)
>>> r.json() # JSON
[{&#39;id&#39;: &#39;27609416600&#39;, &#39;type&#39;: &#39;PushEvent&#39;, ...
登入後複製

如果JSON解碼失敗,r.json()將會拋出例外。例如,如果回應得到一個204(無內容),或者如果回應包含無效的JSON,則r.json()會拋出requests.exceptions.JSONDecodeError。此封裝的異常可能會因為不同python版本和JSON序列化庫可能引發的多個異常提供互通性。

要注意的是,呼叫r.json()的成功呼叫並不表示回應的成功。一些伺服器可能會在失敗的回應中傳回JSON物件(例如,HTTP 500的錯誤詳細資訊)。這樣的JSON將被解碼並回傳。若要檢查請求是否成功,請使用r.raise_for_status()或檢查r.status_code

原始回應內容

可以透過存取r.raw存取伺服器傳回的原始socket回應。如果希望這樣做,請確保在初始請求中設定 stream=True:

>>> import requests
>>> r = requests.get(&#39;https://api.github.com/events&#39;, stream=True)
>>> r.raw
<urllib3.response.HTTPResponse object at 0x0000018DB1704D30>
>>> r.raw.read(10)
b&#39;\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03&#39;
登入後複製

然而,通常情況下,應該使用類似這樣的模式來保存正在串流傳輸的內容到文件中:

with open(filename, &#39;wb&#39;) as fd:
    for chunk in r.iter_content(chunk_size=128):
        fd.write(chunk)
登入後複製

使用Response.iter_content將處理很多你直接使用Resort.raw時需要處理的事情。當串流下載時,以上是檢索內容的首選和推薦方法。請注意,chunk_size可以自由調整為更適合你使用場景的數字。

注意

關於使用 Response.iter_contentResponse.raw的重要注意事項。 Response.iter_content將自動解碼gzipdeflate傳送編碼。 Response.raw是一個原始位元組流--它不會轉換回應內容。如果確實需要存取傳回的字節,請使用Response.raw

自訂請求頭

如果您想要在請求中新增HTTP頭,只需向headers參數傳遞一個dict即可,例如:

>>> url = &#39;https://api.github.com/some/endpoint&#39;
>>> headers = {&#39;user-agent&#39;: &#39;my-app/0.0.1&#39;}
>>> r = requests.get(url, headers=headers)
登入後複製

注意:自訂請求頭的優先權低於更具體的資訊來源。例如:

  • 如果在.netrc中指定了憑證,則使用headers=設定的Authorization請求頭將會被覆寫,而憑證將被auth=參數覆寫。請求將在~/.netrc~/_netrcNETRC環境變數指定的路徑處中搜尋netrc檔案。

  • 如果從主機重定向,將刪除Authorization請求頭。

  • Proxy-Authorization請求頭將被URL中提供的代理憑證覆蓋。

  • 当我们可以确定内容的长度时,将覆盖Content-Length请求头。

此外,请求根本不会根据指定的自定义请求头更改其行为。请求头仅是简单的传递到最终请求中。

注意:所有请求头值必须是字符串、字节字符串或unicode。虽然允许,但建议避免传递unicode请求头值。

更复杂的POST请求More complicated POST requests

通常,如果发送一些表单编码(form-encoded)的数据--就像一个HTML表单。为此,只需将字典传递给data参数即可。发送请求时,将自动对字典数据进行表单编码:

>>> import requests
>>> payload = {&#39;key1&#39;: &#39;value1&#39;, &#39;key2&#39;: &#39;value2&#39;}
>>> r = requests.post("https://httpbin.org/post", data=payload)
>>> r.text
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "key1": "value1", 
    "key2": "value2"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "23", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.27.1", 
    "X-Amzn-Trace-Id": "Root=1-6409fe3b-0cb4118319f09ab3187402bc"
  }, 
  "json": null, 
  "origin": "183.62.127.25", 
  "url": "https://httpbin.org/post"
}
登入後複製

data参数中,为每个键可以具有多个值。这可以通过将data设置为元组列表或以列表为值的字典来实现。当表单中有多个元素使用相同的键时,这特别有用:

>>> import requests
>>> payload_tuples = [(&#39;key1&#39;, &#39;value1&#39;), (&#39;key1&#39;, &#39;value2&#39;)]
>>> r1 = requests.post(&#39;https://httpbin.org/post&#39;, data=payload_tuples)
>>> payload_dict = {&#39;key1&#39;: [&#39;value1&#39;, &#39;value2&#39;]}
>>> r2 = requests.post(&#39;https://httpbin.org/post&#39;, data=payload_dict)
>>> r1.text
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "key1": [
      "value1", 
      "value2"
    ]
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "23", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.27.1", 
    "X-Amzn-Trace-Id": "Root=1-6409ff49-11b8232a7cc81fc0290ec4c4"
  }, 
  "json": null, 
  "origin": "183.62.127.25", 
  "url": "https://httpbin.org/post"
}
>>> re.text == r2.text
True
登入後複製

有时,你可能想发送未经表单编码的数据,则需要传入string类型的数据,而不是dictstring数据将被直接提交。

例如,GitHub API v3接受JSON编码的POST/PATCH数据:

>>> import requests
>>> import json
>>> url = &#39;https://api.github.com/some/endpoint&#39;
>>> payload = {&#39;some&#39;: &#39;data&#39;}
>>> r = requests.post(url, data=json.dumps(payload))
登入後複製

请注意,上述代码不会添加Content-Type请求头(特别是不会将其设置为application/json)。如果需要设置那个请求头(&#39;Content-Type&#39;: &#39;application/json,发送json请求体),并且不想自己对dict进行编码,你也可以直接使用json参数传递它,它将自动被编码:

>>> url = &#39;https://api.github.com/some/endpoint&#39;
>>> payload = {&#39;some&#39;: &#39;data&#39;}
>>> r = requests.post(url, json=payload)
登入後複製

注意,如果提供了data,或者file参数,json 参数将被自动忽略。

提交Multipart-Encoded文件

Request让上传Multipart编码文件变得简单:

>>> import requests
>>> url = &#39;https://httpbin.org/post&#39;
>>> files = {&#39;file&#39;: open(&#39;report.xls&#39;, &#39;rb&#39;)}
>>> r = requests.post(url, files=files)
>>> r.text
{
  "args": {}, 
  "data": "", 
  "files": {
    "file": "#!/usr/bin/env python\r\n# -*- coding:utf-8 -*-\r\n\r\n#!/usr/bin/env python\r\n# -*- coding:utf-8 -*-\r\n\r\nfrom multiprocessing import Pool\r\nfrom threading import Thread\r\nfrom concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor..."
  }, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "3035", 
    "Content-Type": "multipart/form-data; boundary=9ef4437cb1e14427fcba1c42943509cb", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.27.1", 
    "X-Amzn-Trace-Id": "Root=1-640a03df-1a0a5ce972ce410378cda7a2"
  }, 
  "json": null, 
  "origin": "183.62.127.25", 
  "url": "https://httpbin.org/post"
}
登入後複製

可以显示的设置文件名称,内容类型,请求头:

>>> url = &#39;https://httpbin.org/post&#39;
files = {&#39;file&#39;: (&#39;report.xls&#39;, open(&#39;report.xls&#39;, &#39;rb&#39;), &#39;application/vnd.ms-excel&#39;,  {&#39;Expires&#39;: &#39;0&#39;})}
>>> r = requests.post(url, files=files)
>>> r.text
{
  "args": {}, 
  "data": "", 
  "files": {
    "file": "data:application/vnd.ms-excel;base64,UEsDBBQAAAAAAHy8iFMAAAAAAA...=="
  }, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "9667", 
    "Content-Type": "multipart/form-data; boundary=ff85e1018eb5232f7dcab2b2bc5ffa50", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.27.1", 
    "X-Amzn-Trace-Id": "Root=1-640def51-43cc213e33437a0e60255add"
  }, 
  "json": null, 
  "origin": "183.62.127.25", 
  "url": "https://httpbin.org/post"
}
登入後複製

如果想发送一些字符串,以文件的方式被接收:

>>> url = &#39;https://httpbin.org/post&#39;
>>> files = {&#39;file&#39;: (&#39;report.csv&#39;, &#39;some,data,to,send\nanother,row,to,send\n&#39;)}
>>> r = requests.post(url, files=files)
>>> r.text
{
  "args": {}, 
  "data": "", 
  "files": {
    "file": "some,data,to,send\nanother,row,to,send\n"
  }, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "184", 
    "Content-Type": "multipart/form-data; boundary=2bfe430e025860528e29c893a09f1198", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.27.1", 
    "X-Amzn-Trace-Id": "Root=1-640df132-247947ca699e9da35c588f2d"
  }, 
  "json": null, 
  "origin": "183.62.127.25", 
  "url": "https://httpbin.org/post"
}
登入後複製

如果你将一个非常大的文件作为multipart/form-data请求提交,你可能需要流式传输该请求。默认情况下,requests不支持此功能,但有一个单独的包支持此功能——requests toolbelt。阅读toolbelt文档获取有关如何使用它的详细信息。

要在一个请求中发送多个文件,请参阅高级章节。

警告

强烈建议以二进制模式打开文件。这是因为requests可能会尝试为你提供Content-Length请求头,如果这样做,该请求头值将被设置为文件中的字节数。如果以文本模式打开文件,可能会发生错误。

响应状态码

>>> import requests
>>> r = requests.get(&#39;https://httpbin.org/get&#39;)
>>> r.status_code
200
登入後複製

以便于参考,requests还附带一个内置的状态代码查找对象:

>>> r = requests.get(&#39;https://httpbin.org/get&#39;)
>>> r.status_code == requests.codes.ok
True
登入後複製

如果请求出错4XX客户端错误或5XX服务器错误响应),我们可以使用response.raise_for_status()抛出错误:

>>> import requests
>>> bad_r = requests.get(&#39;https://httpbin.org/status/404&#39;)
>>> bad_r.status_code
404
>>> bad_r.raise_for_status()
Traceback (most recent call last):
  File "D:/codePojects/test.py", line 12, in <module>
    bad_r.raise_for_status()
  File "D:\Program Files (x86)\python36\lib\site-packages\requests\models.py", line 960, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 404 Client Error: NOT FOUND for url: https://httpbin.org/status/404
登入後複製

但是,如果r.status_code200, raise_for_status() 将返回None

>>> r.raise_for_status()
None
登入後複製

响应头

>>> r.headers
{
    &#39;content-encoding&#39;: &#39;gzip&#39;,
    &#39;transfer-encoding&#39;: &#39;chunked&#39;,
    &#39;connection&#39;: &#39;close&#39;,
    &#39;server&#39;: &#39;nginx/1.0.4&#39;,
    &#39;x-runtime&#39;: &#39;148ms&#39;,
    &#39;etag&#39;: &#39;"e1ca502697e5c9317743dc078f67693f"&#39;,
    &#39;content-type&#39;: &#39;application/json&#39;
}
登入後複製

根据RFC 7230, HTTP请求头大小写不敏感,所以,我们可以使用任何大写。因此,我们可以使用任意大小写来访问请求头:

>>> r.headers[&#39;Content-Type&#39;]
&#39;application/json&#39;
>>> r.headers.get(&#39;content-type&#39;)
&#39;application/json&#39;
登入後複製

Cookies

如果响应包含Cookie,可以快速访问它们:

>>> url = &#39;http://example.com/some/cookie/setting/url&#39;
>>> r = requests.get(url)
>>> r.cookies[&#39;example_cookie_name&#39;] # 如果存在名为 example_cookie_name的cookie的话
&#39;example_cookie_value&#39;
登入後複製

可以使用cookies 参数将cookie发送给服务器:

>>> url = &#39;https://httpbin.org/cookies&#39;
>>> cookies = dict(cookies_are=&#39;working&#39;)
>>> r = requests.get(url, cookies=cookies)
>>> r.text
&#39;{\n  "cookies": {\n    "cookies_are": "working"\n  }\n}\n&#39;
登入後複製

Cookies are returned in a RequestsCookieJar, which acts like a dict but also offers a more complete interface, suitable for use over multiple domains or paths. Cookie jars can also be passed in to requests:

返回的Cookie存储在RequestsCookieJar中,其作用类似于dict,同时提供了一个更完整的接口,适合在多个域或路径上使用。Cookie jar也可以传递给请求:

>>> jar = requests.cookies.RequestsCookieJar()
>>> jar.set(&#39;tasty_cookie&#39;, &#39;yum&#39;, domain=&#39;httpbin.org&#39;, path=&#39;/cookies&#39;)
Cookie(version=0, name=&#39;tasty_cookie&#39;, value=&#39;yum&#39;, port=None, port_specified=False, domain=&#39;httpbin.org&#39;, domain_specified=True, domain_initial_dot=False, path=&#39;/cookies&#39;, path_specified=True, secure=False, expires=None, discard=True, comment=None, comment_url=None, rest={&#39;HttpOnly&#39;: None}, rfc2109=False)
>>> jar.set(&#39;gross_cookie&#39;, &#39;blech&#39;, domain=&#39;httpbin.org&#39;, path=&#39;/elsewhere&#39;)
Cookie(version=0, name=&#39;gross_cookie&#39;, value=&#39;blech&#39;, port=None, port_specified=False, domain=&#39;httpbin.org&#39;, domain_specified=True, domain_initial_dot=False, path=&#39;/elsewhere&#39;, path_specified=True, secure=False, expires=None, discard=True, comment=None, comment_url=None, rest={&#39;HttpOnly&#39;: None}, rfc2109=False)
>>> url = &#39;https://httpbin.org/cookies&#39;
>>> r = requests.get(url, cookies=jar)
>>> r.text
&#39;{"cookies": {"tasty_cookie": "yum"}}&#39;
登入後複製

重定向与history

默认情况下,requests将对除HEAD之外的所有请求执行位置重定向(如果需要重定向的话)。

我们可以使用Response对象的history属性来跟踪重定向。

Response.history列表包含为完成请求而创建的Response对象。列表按响应的先后顺序排序。

例如,Gitee将所有HTTP请求重定向到HTTPS:

>>> r = requests.get(&#39;http://gitee.com/&#39;)
>>> r.url
&#39;https://gitee.com/&#39;
>>> r.status_code
200
>>> r.history
[<Response [302]>]
登入後複製

如果使用HEAD,GET, OPTIONSPOSTPUTPATCH 或者DELETE,可以使用 allow_redirects参数禁止重定向:

>>> r = requests.get(&#39;http://gitee.com/&#39;, allow_redirects=False)
>>> r.status_code
302
>>> r.history
[]
>>> r = requests.head(&#39;http://gitee.com/&#39;, allow_redirects=False)
>>> r.url
&#39;http://gitee.com/&#39;
>>> r.status_code
302
>>> r.history
[]
>>> r = requests.head(&#39;http://gitee.com/&#39;, allow_redirects=True)
>>> r.status_code
200
>>> r.url
&#39;https://gitee.com/&#39;
>>> r.history
[<Response [302]>]
登入後複製

请求超时

可以使用timeout参数告诉requests在给定的秒数后停止等待响应。几乎所有的生产代码都应该在几乎所有的请求中使用此参数。否则会导致程序无限期挂起:

>>> requests.get(&#39;https://gitee.com/&#39;, timeout=0.1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  ...
urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host=&#39;gitee.com&#39;, port=443): Read timed out. (read timeout=0.1)
登入後複製

注意:

timeout不是整个响应的下载时间限制;相反,如果服务器在timeout秒内没有发出响应(更准确地说,如果在timeout秒内底层socket没有接收到任何字节数据),则会引发异常。如果未明确指定timeout,则请求不会超时。

错误和异常

如果出现网络问题(例如DNS故障、拒绝连接等),requests将抛出ConnectionError异常。

如果HTTP请求返回了失败的状态代码,Response.raise_for_statu()将抛出HTTPError

如果请求超时,则会抛出Timeout异常。

如果请求超过了配置的最大重定向次数,则会抛出TooManyRedirects异常。

requests显式抛出的所有异常都继承自requests.exceptions.RequestException

高级用法

Session对象

Session对象允许你跨请求保持某些参数,以及Session实例发出的所有请求的cookie,并将使用urllib3的[连接池](https://urllib3.readthedocs.io/en/latest/reference/index.html#module-urllib3.connectionpool)。因此,如果你向同一主机发出多个请求,将复用底层TCP连接,这可能会显著提高性能(请参见HTTP持久连接)。

Session对象具有主要 requests API的所有方法。

让我们在请求之间保持一些cookie:

>>> s = requests.Session()
>>> s.get(&#39;https://httpbin.org/cookies/set/sessioncookie/123456789&#39;)
<Response [200]>
>>> r = s.get(&#39;https://httpbin.org/cookies&#39;)
>>> r.text
&#39;{\n  "cookies": {\n    "sessioncookie": "123456789"\n  }\n}\n&#39;
>>>
登入後複製

Seesion对象还可以用于向请求方法提供默认数据。这是通过向Session对象属性提供数据来实现的:

>>> s = requests.Session()
>>> s.auth = (&#39;user&#39;, &#39;pass&#39;)
>>> s.headers.update({&#39;x-test&#39;: &#39;true&#39;})
# &#39;x-test&#39;和&#39;x-test2&#39;请求头随请求发送了
>>> s.headers.update({&#39;x-test&#39;: &#39;true&#39;})
>>> s.get(&#39;https://httpbin.org/headers&#39;, headers={&#39;x-test2&#39;: &#39;true&#39;})
<Response [200]>
登入後複製

传递给请求方法的任何字典都将与会话级别设置的值合并。方法级别的参数会覆盖会话级别的参数。

然而,请注意,即使使用会话,方法级参数也不能跨请求保持。本示例将只在发送第一个请求发送cookie,而不发送第二个请求

>>> s = requests.Session()
>>> r = s.get(&#39;https://httpbin.org/cookies&#39;, cookies={&#39;from-my&#39;: &#39;browser&#39;})
>>> r.text
&#39;{\n  "cookies": {\n    "from-my": "browser"\n  }\n}\n&#39;
>>> r = s.get(&#39;https://httpbin.org/cookies&#39;)
>>> r.text
&#39;{\n  "cookies": {}\n}\n&#39;
登入後複製

Cookie utility functions to manipulate Session.cookies

如果想手动向Session添加Cookie,那么使用 Cookie utility functions来操作Session.cookies

Session对象也可以用作上下文管理器

>>> with requests.Session() as s:
...     s.get(&#39;https://httpbin.org/cookies/set/sessioncookie/123456789&#39;)
...
<Response [200]>
>>>
登入後複製

这将确保在退出with块后立即关闭会话,即使发生未处理的异常。

Remove a Value From a Dict Parameter
Sometimes you&#39;ll want to omit session-level keys from a dict parameter. To do this, you simply set that key&#39;s value to `None` in the method-level parameter. It will automatically be omitted.
从字典参数中删除值
有时,你需要从dict参数中忽略会话级别的键。为此,只需在方法级参数中将该键的值设置为“None”即可。它将被自动忽略。
登入後複製

Session中包含的所有值都可以直接使用。参见Session API Docs了解更多信息。

请求和响应对象

示例:获取响应头和请求头

>>> r = s.get(&#39;https://httpbin.org&#39;)
>>> r.headers # 获取响应头
{&#39;Date&#39;: &#39;Mon, 13 Mar 2023 15:43:41 GMT&#39;, &#39;Content-Type&#39;: &#39;text/html; charset=utf-8&#39;, &#39;Content-Length&#39;: &#39;9593&#39;, &#39;Connection&#39;: &#39;keep-alive&#39;, &#39;Server&#39;: &#39;gunicorn/19.9.0&#39;, &#39;Access-Control-Allow-Origin&#39;: &#39;*&#39;, &#39;Access-Control-Allow-Credentials&#39;: &#39;true&#39;}
>>> r.request.headers
{&#39;User-Agent&#39;: &#39;python-requests/2.27.1&#39;, &#39;Accept-Encoding&#39;: &#39;gzip, deflate&#39;, &#39;Accept&#39;: &#39;*/*&#39;, &#39;Connection&#39;: &#39;keep-alive&#39;, &#39;Cookie&#39;: &#39;sessioncookie=123456789&#39;}
>>>
登入後複製

Prepared requests

每当收到来自某个API调用或者Session调用的Response对象,request属性实际上是所使用的PreparedRequest。在某些情况下,你可能希望在发送请求之前对请求体或请求头(或其他任何内容)做一些额外的工作。简单的做法如下:

from requests import Request, Session
s = Session()
req = Request(&#39;POST&#39;, url, data=data, headers=headers)
prepped = req.prepare()
# do something with prepped.body
prepped.body = &#39;No, I want exactly this as the body.&#39;
# do something with prepped.headers
del prepped.headers[&#39;Content-Type&#39;]
resp = s.send(prepped,
    stream=stream,
    verify=verify,
    proxies=proxies,
    cert=cert,
    timeout=timeout
)
print(resp.status_code)
登入後複製

However, the above code will lose some of the advantages of having a requests Session object. In particular, Session-level state such as cookies will not get applied to your request. To get a PreparedRequest with that state applied, replace the call to Request.prepare() with a call to Session.prepare_request(), like this:

由于你没有对Request对象执行任何特殊操作,因此您可以立即prepare它并修改PreparedRequest对象。然后将其与发送给requests.*Session.*的其它参数一起发送。

然而,上述代码将失去使用requests Session对象的一些优点。特别是Session级别的状态,比如cookie将不会应用于你的请求。如果需要获取应用了那些状态的 PreparedRequest,替换 Request.prepare() 调用为Session.prepare_request(),像这样:

from requests import Request, Session

s = Session()
req = Request(&#39;GET&#39;,  url, data=data, headers=headers)

prepped = s.prepare_request(req)

# do something with prepped.body
prepped.body = &#39;Seriously, send exactly these bytes.&#39;

# do something with prepped.headers
prepped.headers[&#39;Keep-Dead&#39;] = &#39;parrot&#39;

resp = s.send(prepped,
    stream=stream,
    verify=verify,
    proxies=proxies,
    cert=cert,
    timeout=timeout
)

print(resp.status_code)
登入後複製

When you are using the prepared request flow, keep in mind that it does not take into account the environment. This can cause problems if you are using environment variables to change the behaviour of requests. For example: Self-signed SSL certificates specified in REQUESTS_CA_BUNDLE will not be taken into account. As a result an SSL: CERTIFICATE_VERIFY_FAILED is thrown. You can get around this behaviour by explicitly merging the environment settings into your session:

当你使用prepared request请求时,请记住它没有考虑环境。如果你正使用环境变量来更改请求的行为,这可能会导致问题。例如:在REQUESTS_CA_BUNDLE中指定的自签名SSL证书将不起作用,结果引发了SSL:CERTIFICATE_VERIFY_FAILED。你可以通过将环境设置显式合并到Session中来避免这种行为:

from requests import Request, Session
s = Session()
req = Request(&#39;GET&#39;, url)
prepped = s.prepare_request(req)
# Merge environment settings into session
settings = s.merge_environment_settings(prepped.url, {}, None, None, None)
resp = s.send(prepped, **settings)
print(resp.status_code)
登入後複製

HTTP Basic 验证

>>> from requests.auth import HTTPBasicAuth
>>> auth = HTTPBasicAuth(&#39;your_username&#39;, &#39;your_password&#39;)
>>> r = requests.post(url=&#39;you_target_url&#39;, data=body, auth=auth)
登入後複製

SSL证书验证

requests验证HTTPS请求的SSL证书,就像web浏览器一样。默认情况下,SSL验证已启用,如果无法验证证书,请求将抛出SSLError:

>>> requests.get(&#39;https://requestb.in&#39;)
requests.exceptions.SSLError: hostname &#39;requestb.in&#39; doesn&#39;t match either of &#39;*.herokuapp.com&#39;, &#39;herokuapp.com&#39;
登入後複製

你可以使用verify参数传递拥有受信任CA的证书的CA_BUNDLE文件的路径或者目录:

&gt;&gt;&gt; requests.get(&#39;https://github.com&#39;, verify=&#39;/path/to/certfile&#39;)
登入後複製

或者

s = requests.Session()
s.verify = &#39;/path/to/certfile&#39;
登入後複製

注意:

如果verify设置为目录的路径,则必须使用OpenSSL提供的c_rehash实用程序处理该目录。

还可以通过REQUESTS_CA_BUNDLE环境变量指定此受信任CA列表。如果未设置REQUESTS_CA_BUNDLE,将使用CURL_CA_BUNDLE

如果将verify设置为False,则requests也可以忽略SSL证书验证:

>>> requests.get(&#39;https://kennethreitz.org&#39;, verify=False)
<Response [200]>
登入後複製

请注意,当verify设置为False时,Requests将接受服务器提供的任何TLS证书,并将忽略主机名不匹配,或过期的证书,这将使你的应用程序容易受到中间人(MitM)攻击。在本地开发或测试期间,将verify设置为False可能很有用。

默认情况下,verify设置为True。选项verify仅适用于主机证书。

客户端证书

你还可以将本地证书指定为客户端证书、单个文件(包含私钥和证书)或两个文件路径的元组

>>> requests.get(&#39;https://kennethreitz.org&#39;, cert=(&#39;/path/client.cert&#39;, &#39;/path/client.key&#39;))
<Response [200]>
登入後複製

或者:

s = requests.Session()
s.cert = &#39;/path/client.cert&#39;
登入後複製

警告

本地证书的私钥必须为未加密的。当前,Requests不支持加密的私钥

CA证书

Reuests使用来自certific包中的证书. 这允许用户在不更改Requests版本的情况下更新其受信任的证书。

在2.16版本之前,Requests捆绑了一组其信任的根CA证书,证书来源于Mzillatruststore。每个Request版本只更新一次证书。当未安装certific时,当使用较旧版本的requests时,这会导致证书包非常过时。

为了安全起见,我们建议经常升级certific

Body内容工作流

默认情况下,当你发出一个请求时,将立即下载响应的正文。你可以使用stream参数覆盖此行为并延迟下载响应主体直到访问response.content属性

tarball_url = &#39;https://github.com/psf/requests/tarball/main&#39;
r = requests.get(tarball_url, stream=True)
登入後複製

此时,仅仅响应头被下载,且连接保持打开状态,因此,允许我们有条件的检索内容:

if int(r.headers.get(&#39;content-length&#39;)) &lt; TOO_LONG:
  content = r.content
  ...
登入後複製

您可以使用 Response.iter_content()Response.iter_lines() 方法进一步控制工作流。或者,可以从位于Response.raw的底层的urllib3.HTTPResponse 中读取未编码的主体.

如果在发出请求时将stream设置为True,则requests无法释放连接回连接池,除非读取完所有数据或调用Response.close。这可能导致连接效率低下。如果你发现自己在使用stream=True时部分读取请求体(或根本没有读取它们),则应在with语句中发出请求,以确保连接最终处于关闭状态:

with requests.get(&#39;https://httpbin.org/get&#39;, stream=True) as r:
    # Do things with the response here.
登入後複製

Keep-Alive

多亏了urllib3keep-alive在Session中是100%自动的!你在Session发出的任何请求都将自动重用合适的连接!

注意,只有在读取了所有响应体数据后,才会将连接释放回连接池以供重用;请确保将stream设置为False或读取Response对象的content属性。

流式上传

requests支持流式上传,允许发送大型流或文件,而无需将其读入内存。要流式传输和上传,只需为请求体提供一个类似文件的对象:

with open(&#39;massive-body&#39;, &#39;rb&#39;) as f:
    requests.post(&#39;http://some.url/streamed&#39;, data=f)
登入後複製

警告

强烈建议以二进制模式打开文件。这是因为requests可能会尝试为你提供Content-Length请求头,如果这样做,该请求头值将被设置为文件中的字节数。如果以文本模式打开文件,可能会发生错误。

分块编码(Chunk-Encoded)请求

requests 还支持传出和传入请求的分块传输编码。要发送块编码请求,只需简单的为请求体提供一个生成器(或任何没有长度的迭代器)

def gen():
    yield &#39;hi&#39;
    yield &#39;there&#39;
requests.post(&#39;http://some.url/chunked&#39;, data=gen())
登入後複製

对于分块编码请求的响应,最好使用Response.iter_content()对数据进行迭代。在理想情况下,将在请求上设置stream=True,在这种情况下,可以通过使用值为Nonechunk_size参数调用iter_content来逐块迭代。如果要设置块的最大大小,可以将chunk_size参数设置为任意目标大小整数。

POST 多个分部编码(Multipart-Encoded)文件

你可以在一个请求中发送多个文件。例如,假设你要将图像文件上载到具有多个文件字段“images”的HTML表单:

<input type="file" name="images" multiple="true" required="true"/>
登入後複製

为此,只需将files设置为(form_field_name,file_info)的元组列表:

>>> url = &#39;https://httpbin.org/post&#39;
>>> multiple_files = [
...     (&#39;images&#39;, (&#39;foo.png&#39;, open(&#39;foo.png&#39;, &#39;rb&#39;), &#39;image/png&#39;)),
...     (&#39;images&#39;, (&#39;bar.png&#39;, open(&#39;bar.png&#39;, &#39;rb&#39;), &#39;image/png&#39;))]
>>> r = requests.post(url, files=multiple_files)
>>> r.text
>>> r.text
&#39;{\n  "args": {}, \n  "data": "", \n  "files": {\n    "images": "data:image/png;base64,iVBORw0KGgoAAAAN...=="\n  }, \n  "form": {}, \n  "headers": {\n    "Accept": "*/*", \n    "Accept-Encoding": "gzip, deflate", \n    "Content-Length": "1800", \n    "Content-Type": "multipart/form-data; boundary=771ef90459071106c5f47075cbca2659", \n    "Host": "httpbin.org", \n    "User-Agent": "python-requests/2.27.1", \n    "X-Amzn-Trace-Id": "Root=1-641122ea-10a6271f0fdf488c70cf90e9"\n  }, \n  "json": null, \n  "origin": "183.62.127.25", \n  "url": "https://httpbin.org/post"\n}\n&#39;
登入後複製

事件钩子(Event Hooks)

requests拥有一个hook系统,可用于控制请求过程的部分,或者信号事件处理。

可用的hooks:

  • response:

    请求生成的响应

通过将{hook_name:callback_function}字典传递给hooks请求参数,可以按每个请求分配一个钩子函数:

hooks={&#39;response&#39;: print_url}
登入後複製

callback_function将接收一数据块(a chunk of data)作为其第一个参数。

def print_url(r, *args, **kwargs):
    print(r.url)
登入後複製

回调函数必须处理其自己的异常。任何为处理的异常,都不会以静默方式传递,因此应该由代码调用请求来处理。

如果回调函数返回某个值,则假定它将替换传入的数据。如果函数不返回任何内容,则不产生任何影响

def record_hook(r, *args, **kwargs):
    r.hook_called = True
    return r
登入後複製

让我们在运行时打印一些请求方法参数:

>>> requests.get('https://httpbin.org/', hooks={&#39;response&#39;: print_url})
https://httpbin.org/
登入後複製

可以添加多个钩子到单个请求中,如下,一次调用两个钩子函数:

>>> r = requests.get(&#39;https://httpbin.org/&#39;, hooks={&#39;response&#39;: [print_url, record_hook]})
>>> r.hook_called
True
登入後複製

还可以为Session实例添加钩子,这样添加的任何钩子都将在向会话发出的每个请求中被调用。例如:

>>> s = requests.Session()
>>> s.hooks[&#39;response&#39;].append(print_url)
>>> s.get(&#39;https://httpbin.org/&#39;)
 https://httpbin.org/
 <Response [200]>
登入後複製

如果Session实例可个钩子函数,那么将按钩子的添加顺序调用这些钩子。

自定义身份验证

requests 请求支持自定义身份验证机制。

作为auth参数传递给请求方法的任何可调用对象都有机会在发送请求之前修改请求。

身份验证实现为AuthBase的子类,并且易于定义。requests在requests.auth中提供了两种常见的身份验证方案实现:HTTPBasicAuthHTTPDigestAuth.

假设我们有一个web服务,它只有在X-Pizza请求头设置为密码值时才会响应。这不太可能,暂且还是顺着它:

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[&#39;X-Pizza&#39;] = self.username
        return r
登入後複製

然后,发送请求

>>> requests.get(&#39;http://pizzabin.org/admin&#39;, auth=PizzaAuth(&#39;kenneth&#39;))
<Response [200]>
登入後複製

流式请求

使用Response.iter_lines() ,可以很轻易的迭代流式API,比如 Twitter Streaming API。简单的设置 streamTrue 并且使用iter_lines对响应进行迭代:

import json
import requests
r = requests.get(&#39;https://httpbin.org/stream/20&#39;, stream=True)
for line in r.iter_lines():
    # filter out keep-alive new lines
    if line:
        decoded_line = line.decode(&#39;utf-8&#39;)
        print(json.loads(decoded_line))
登入後複製

decode_unicode=TrueResponse.iter_lines() 或者Response.iter_content()配合使用时,如果服务器未提供编码,则需要提供编码:

r = requests.get(&#39;https://httpbin.org/stream/20&#39;, stream=True)
if r.encoding is None:
    r.encoding = &#39;utf-8&#39;
for line in r.iter_lines(decode_unicode=True):
    if line:
        print(json.loads(line))
登入後複製

警告

iter_lines 不是可重入安全的。多次调用此方法会导致一些接收到的数据丢失。如果需要从多个地方调用它,请使用生成的迭代器对象:

lines = r.iter_lines()
# Save the first line for later or just skip it
first_line = next(lines)
for line in lines:
    print(line)
登入後複製

代理

如果你需要使用代理,可在任何请求方法的proxys参数中为单个请求配置代理

import requests
proxies = {
  &#39;http&#39;: &#39;http://10.10.1.10:3128&#39;,
  &#39;https&#39;: &#39;http://10.10.1.10:1080&#39;,
}
requests.get(&#39;http://example.org&#39;, proxies=proxies)
登入後複製

可选的,可以一次性为整个Session配置代理。

import requests
proxies = {
  &#39;http&#39;: &#39;http://10.10.1.10:3128&#39;,
  &#39;https&#39;: &#39;http://10.10.1.10:1080&#39;,
}
session = requests.Session()
session.proxies.update(proxies)
session.get(&#39;http://example.org&#39;)
登入後複製

警告

session.proxies提供的值可能被环境代理(由urllib.request.getproxys返回的值)覆盖,所以为了确保在环境代理存在的情况下,也使用给定代理,显示为所有单个请求指定proxies参数,如上述一开始所述。

如果没有为请求设置proxies请求参数的情况下,requests会尝试读取由标准环境变量 http_proxy, https_proxy, no_proxyall_proxy定义的代理配置。这些变量名称可大写。所以,可以通过这些变量配置为请求设置代理(请根据实际需要配置):

linux:

$ export HTTP_PROXY="http://10.10.1.10:3128"
$ export HTTPS_PROXY="http://10.10.1.10:1080"
$ export ALL_PROXY="socks5://10.10.1.10:3434"
$ python
>>> import requests
>>> requests.get(&#39;http://example.org&#39;)
登入後複製

win:

set HTTP_PROXY=http://10.10.1.10:3128
>>> import requests
>>> requests.get(&#39;http://example.org&#39;)
登入後複製

要对代理使用HTTP基本身份验证,请在上述任意代理配置入口中使用http://user:password@host/语法:

$ export HTTPS_PROXY="http://user:pass@10.10.1.10:1080"
$ python
>>> proxies = {&#39;http&#39;: &#39;http://user:pass@10.10.1.10:3128/&#39;}
登入後複製

警告

将敏感的用户名和密码信息存储在环境变量或版本控制的文件中会带来安全风险,强烈建议不要这样做。

如果要为特定shema和主机提供代理,请使用scheme://hostnameproxies字典参数的键来设置代理。这将匹配给定scheme和确切主机名的任何请求。

proxies = {&#39;http://10.20.1.128&#39;: &#39;http://10.10.1.10:5323&#39;}
登入後複製

注意,代理URL必须包含schema。

最后需要注意的,为https连接设置代理,通常需要所在本机机器信任代理根证书。默认的,可以通过以下代码查找requests信任的证书列表:

from requests.utils import DEFAULT_CA_BUNDLE_PATH
print(DEFAULT_CA_BUNDLE_PATH)
登入後複製

通过将 REQUESTS_CA_BUNDLE (or CURL_CA_BUNDLE) 环境变量设置为另一个文件路径,可以覆盖此证书路径:

$ export REQUESTS_CA_BUNDLE="/usr/local/myproxy_info/cacert.pem"
$ export https_proxy="http://10.10.1.10:1080"
$ python
>>> import requests
>>> requests.get(&#39;https://example.org&#39;)
登入後複製

SOCKS

版本2.10.0中新增

除了基本的HTTP代理之外,requests还支持使用SOCKS协议的代理。这是一项可选功能,要求在使用前安装其他第三方库

可通过pip获取该功能需要的依赖:

$ python -m pip install requests[socks]
登入後複製

安装依赖后,使用SOCKS代理就同使用HTTP代理一样简单:

proxies = {
    &#39;http&#39;: &#39;socks5://user:pass@host:port&#39;,
    &#39;https&#39;: &#39;socks5://user:pass@host:port&#39;
}
登入後複製

使用 socks5 会导致DNS解析发生在客户端上,而不是代理服务器上。这与curl保持一致,curl使用scheme来决定是在客户端还是代理服务器上进行DNS解析。如果要解析代理服务器上的域,请使用socks5h作为scheme

编码

当收到响应时,并访问 Response.text属性时,requests会猜测用于解码响应体的编码。requests将首先检查HTTP请求头中的编码,如果不存在,则使用charset_normalizer或chardet尝试猜测编码。

如果安装了chardetrequests将使用它,但对于python3来说,chardet不再是强制依赖项。

当安装requests时,没有指定 [use_chardet_on_py3],并且chardet尚未安装时,requests将使用charset normalizer来猜测编码。

requests不会猜测编码的唯一情况是HTTP请求头中不存在显示字符集且Content-Type请求头包含text。在这种情况下,RFC 2616指定默认字符集必须是ISO-8859-1。requests遵循该规范。如果需要不同的编码,您可以手动设置Response.conding属性,或使用原始Response.content

Link请求头

许多HTTP API具有link请求头。它们使API更加自我描述和可发现。

GitHub 在API中将这些用于分页

>>> url = &#39;https://api.github.com/users/kennethreitz/repos?page=1&per_page=10&#39;
>>> r = requests.head(url=url)
>>> r.headers[&#39;link&#39;]
&#39;<https://api.github.com/user/119893/repos?page=2&per_page=10>; rel="next", <https://api.github.com/user/119893/repos?page=5&per_page=10>; rel="last"&#39;
登入後複製

requests 将自动解析这link请求头并且让它们更容易被使用:

>>> r.links["next"]
{&#39;url&#39;: &#39;https://api.github.com/user/119893/repos?page=2&per_page=10&#39;, &#39;rel&#39;: &#39;next&#39;}
>>> r.links["last"]
{&#39;url&#39;: &#39;https://api.github.com/user/119893/repos?page=5&per_page=10&#39;, &#39;rel&#39;: &#39;last&#39;}
登入後複製

传输适配器(Transport Adapters)

从v1.0.0开始,requests 已模块化内部设计。这样做的部分原因是为了实现传输适配器,最初在此处描述. 传输适配器提供了一种机制来定义某个HTTP服务的交互方法。特别是,它们允许你应用每个服务的配置。

requests附带单个传输适配器HTTPAdapter. 此适配器使用功能强大的urllib3提供与HTTP和HTTPS的默认请求交互。当初始化 requests Session 时,其中一个附加到Session 对象表示HTTP,一个表示HTTPS。

户能够创建和使用自己的具备特定功能的传输适配器。一旦创建,传输适配器就可以加载到会话对象,并指示它应该应用于哪些web服务。

>>> s = requests.Session()
>>> s.mount(&#39;https://github.com/&#39;, MyAdapter())
登入後複製

上述mount调用将传输适配器的指定实例注册到URL前缀中。一旦挂载,使用该session发起的,URL以给定前缀开头的任何HTTP请求都将使用给定的传输适配器。

实现传输适配器的许多细节超出了本文档的范围,但是可以看下一个简单SSL使用示例。除此之外,您还可以考虑继承BaseAdapter实现子类适配器。

示例: 指定SSL版本

The requests team has made a specific choice to use whatever SSL version is default in the underlying library (urllib3). Normally this is fine, but from time to time, you might find yourself needing to connect to a service-endpoint that uses a version that isn’t compatible with the default.

You can use Transport Adapters for this by taking most of the existing implementation of HTTPAdapter, and adding a parameter ssl_version that gets passed-through to urllib3. We’ll make a Transport Adapter that instructs the library to use SSLv3:

默认情况下,requests选择使用底层urllib3库中默认的SSL版本。 通常情况下,这是可以的,但有时,您可能会发现自己需要连接到使用与默认版本不兼容的SSL版本的服务端。

为此,可以通过继承HTTPAdapter实现自定义传输适配器,

示例:编写一个适配器,指示库使用SSLv3:

import ssl
from urllib3.poolmanager import PoolManager
from requests.adapters import HTTPAdapter
class Ssl3HttpAdapter(HTTPAdapter):
    """"Transport adapter" that allows us to use SSLv3."""
    def init_poolmanager(self, connections, maxsize, block=False):
        self.poolmanager = PoolManager(
            num_pools=connections, maxsize=maxsize,
            block=block, ssl_version=ssl.PROTOCOL_SSLv3)
登入後複製

阻塞或非阻塞

有了默认的传输适配器,requests就不会提供任何类型的非阻塞IO。Response.content属性将阻塞,直到下载完整个响应为止。如果你需要更大的粒度,则库的流式传输功能(请参阅流式传输请求)允许单次接收较小数量的响应那日。然而,这些调用仍然是阻塞。

如果您关心阻塞IO的使用,那么有很多项目将请求与Python的异步框架结合在一起。一些很好的例子是 requests-threads, grequests, requests-futures, 和httpx.

超时

大多数对外部服务器的请求都应该附加超时,以防服务器没有及时响应。默认情况下,除非显式设置了超时时间,否则requests不会超时。如果没有超时,你的代码可能会挂起几分钟或更长时间。

连接超时是requests等待客户端建立与远程计算机的socke连接的秒数。将连接超时设置为略大于3的倍数是一种很好的做法,因为3秒是默认的TCP数据包重传窗口.

一旦客户端连接到服务器并发送HTTP请求后,读取超时是客户端等待服务器返回响应的秒数(具体来说,这是客户端等待服务器返回字节数据的秒数。在99.9%的情况下,这是服务器返回第一个字节之前的等待时间)。

如果需要为请求设置一个超时时间,可以为timeout参数指定一个具体的时间值:

r = requests.get(&#39;https://github.com&#39;, timeout=5)
登入後複製

该超时时间将同时应用于连接超时和读取超时。如果想为连接超时和读取超时分别设置不同的等待时间,可以指定一个元组:

r = requests.get(&#39;https://github.com&#39;, timeout=(3.05, 27))
登入後複製

如果服务很慢,想让requests一直等待响应直到获取响应,可以指定timeout参数值为None

r = requests.get(&#39;https://github.com&#39;, timeout=None)
登入後複製

以上是如何使用Python的Requests函式庫?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:yisu.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板