How to use Python's Requests library?

WBOY
Release: 2023-05-09 16:16:16
forward
1378 people have browsed it

Quick Start

Send Request

>>> 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
Copy after login

URL Parameters

You can use paramsdictionary parameters to provide query string parameters for the URL, for example, To visit https://httpbin.org/get?key1=value1&key2=value2, you can use the following code:

>>> 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=
Copy after login

It should be noted that if the key value in the dictionary parameter (i.e. URL parameter value is None), the parameter will not be added to the query string of the URL.

If there are duplicate parameters in the URL query string (the parameter name is the same but the parameter value is different), the key value needs to be set to a list composed of parameter values, as follows:

>>> 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
Copy after login

Response Content

Read server response content

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

requests will automatically decode the content from the server. Most unicode character sets are decoded seamlessly.

When you make a request, requests makes an educated guess about the response encoding based on the HTTP headers. When you access r.text, the text encoding guessed by requests will be used. You can use the r.encoding property to find the encoding used by the request and change it:

>>> r.encoding # 输出:utf-8
r.encoding = &#39;ISO-8859-1&#39;
Copy after login

If you change the encoding, whenever r.text is called, requests will use the new r.encoding value. In any case, you can apply special logic to determine the encoding of the content. For example, HTML and XML can specify their encoding in their body. In this case you should use r.content to find the encoding and then set r.encoding. This will allow you to use r.text with the correct encoding.

requests will also use custom encoding when needed. If you have created your own encoding and registered it with the codecs module, you can simply use the codec name as the value of r.encoding and requests will handle the decoding for you.

Binary response content

For non-text requests, the response body can also be accessed in bytes (of course, text requests are also available):

>>> r.content
b&#39;[{"id":"27581220674","type":"IssueCommentEvent","actor":{"id":327807...
Copy after login

requests will be automatically decoded gzip and deflate transfer encoding.

If a Brotil class library like brotli or brotlicffi is installed, Requets will also automatically interface brTransfer encoding

If the Brotli library (such as [Brotli]) automatically interfaces for you Decode br Transfer Encoding (https://pypi.org/project/brotli) or brotliffi installed.

For example, you can use the following code to create an image from the binary data returned by the request:

from PIL import Image
from io import BytesIO
img = Image.open(BytesIO(r.content))
Copy after login

JSON response content

You can use the built-in JSON decoder to process JSON data:

>>> 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;, ...
Copy after login

If JSON decoding fails, r.json() will throw an exception. For example, r.json() will throw requests.exceptions.JSONDecodeError if the response gets a 204 (No Content), or if the response contains invalid JSON. This encapsulated exception may provide interoperability due to multiple exceptions that may be thrown by different python versions and JSON serialization libraries.

It should be noted that the successful call of r.json() does not mean the success of the response. Some servers may return JSON objects in failed responses (for example, error details for HTTP 500). Such JSON will be decoded and returned. To check if the request was successful, use r.raise_for_status() or check r.status_code

The original response content

can be accessed via r.rawAccess the raw socket response returned by the server. If you wish to do this, make sure to set 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;
Copy after login

on the initial request. However, typically, a pattern like this should be used to save the content being streamed to a file. Medium:

with open(filename, &#39;wb&#39;) as fd:
    for chunk in r.iter_content(chunk_size=128):
        fd.write(chunk)
Copy after login

Using Response.iter_content will handle a lot of the things you need to handle when using Resort.raw directly. When streaming downloads, the above are the preferred and recommended methods of retrieving content. Please note that chunk_size can be freely adjusted to a number that is more suitable for your usage scenario.

Note

Important notes about using Response.iter_content and Response.raw. Response.iter_content will automatically decode gzip and deflate transfer encodings. Response.raw is a raw byte stream - it does not transform the response content. If you really need to access the returned bytes, use Response.raw.

Custom request headers

If you want to add HTTP headers to the request, just pass a dict to the headers parameter, e.g. :

>>> 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)
Copy after login

Note: Custom request headers have lower priority than more specific sources of information. For example:

  • If credentials are specified in .netrc, the Authorization request header set by headers= is used will be overwritten, and the credentials will in turn be overwritten by the auth= parameter. The request will search for the netrc file in the path specified by the ~/.netrc, ~/_netrc, or NETRC environment variable.

  • If redirected from the host, the Authorization request header will be removed.

  • Proxy-AuthorizationThe request header will be overridden by the proxy credentials provided in the 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"
}
Copy after login

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
Copy after login

有时,你可能想发送未经表单编码的数据,则需要传入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))
Copy after login

请注意,上述代码不会添加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)
Copy after login

注意,如果提供了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"
}
Copy after login

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

>>> 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"
}
Copy after login

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

>>> 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"
}
Copy after login

如果你将一个非常大的文件作为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
Copy after login

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

>>> r = requests.get(&#39;https://httpbin.org/get&#39;)
>>> r.status_code == requests.codes.ok
True
Copy after login

如果请求出错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
Copy after login

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

>>> r.raise_for_status()
None
Copy after login

响应头

>>> 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;
}
Copy after login

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

>>> r.headers[&#39;Content-Type&#39;]
&#39;application/json&#39;
>>> r.headers.get(&#39;content-type&#39;)
&#39;application/json&#39;
Copy after login

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;
Copy after login

可以使用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;
Copy after login

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;
Copy after login

重定向与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]>]
Copy after login

如果使用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]>]
Copy after login

请求超时

可以使用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)
Copy after login

注意:

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;
>>>
Copy after login

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]>
Copy after login

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

然而,请注意,即使使用会话,方法级参数也不能跨请求保持。本示例将只在发送第一个请求发送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;
Copy after login

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]>
>>>
Copy after login

这将确保在退出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”即可。它将被自动忽略。
Copy after login

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;}
>>>
Copy after login

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)
Copy after login

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)
Copy after login

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)
Copy after login

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)
Copy after login

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;
Copy after login

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

&gt;&gt;&gt; requests.get(&#39;https://github.com&#39;, verify=&#39;/path/to/certfile&#39;)
Copy after login

或者

s = requests.Session()
s.verify = &#39;/path/to/certfile&#39;
Copy after login

注意:

如果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]>
Copy after login

请注意,当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]>
Copy after login

或者:

s = requests.Session()
s.cert = &#39;/path/client.cert&#39;
Copy after login

警告

本地证书的私钥必须为未加密的。当前,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)
Copy after login

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

if int(r.headers.get(&#39;content-length&#39;)) &lt; TOO_LONG:
  content = r.content
  ...
Copy after login

您可以使用 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.
Copy after login

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)
Copy after login

警告

强烈建议以二进制模式打开文件。这是因为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())
Copy after login

对于分块编码请求的响应,最好使用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"/>
Copy after login

为此,只需将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;
Copy after login

事件钩子(Event Hooks)

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

可用的hooks:

  • response:

    请求生成的响应

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

hooks={&#39;response&#39;: print_url}
Copy after login

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

def print_url(r, *args, **kwargs):
    print(r.url)
Copy after login

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

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

def record_hook(r, *args, **kwargs):
    r.hook_called = True
    return r
Copy after login

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

>>> requests.get('https://httpbin.org/', hooks={&#39;response&#39;: print_url})
https://httpbin.org/
Copy after login

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

>>> r = requests.get(&#39;https://httpbin.org/&#39;, hooks={&#39;response&#39;: [print_url, record_hook]})
>>> r.hook_called
True
Copy after login

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

>>> s = requests.Session()
>>> s.hooks[&#39;response&#39;].append(print_url)
>>> s.get(&#39;https://httpbin.org/&#39;)
 https://httpbin.org/
 <Response [200]>
Copy after login

如果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
Copy after login

然后,发送请求

>>> requests.get(&#39;http://pizzabin.org/admin&#39;, auth=PizzaAuth(&#39;kenneth&#39;))
<Response [200]>
Copy after login

流式请求

使用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))
Copy after login

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))
Copy after login

警告

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)
Copy after login

代理

如果你需要使用代理,可在任何请求方法的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)
Copy after login

可选的,可以一次性为整个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;)
Copy after login

警告

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;)
Copy after login

win:

set HTTP_PROXY=http://10.10.1.10:3128
>>> import requests
>>> requests.get(&#39;http://example.org&#39;)
Copy after login

要对代理使用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;}
Copy after login

警告

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

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

proxies = {&#39;http://10.20.1.128&#39;: &#39;http://10.10.1.10:5323&#39;}
Copy after login

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

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

from requests.utils import DEFAULT_CA_BUNDLE_PATH
print(DEFAULT_CA_BUNDLE_PATH)
Copy after login

通过将 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;)
Copy after login

SOCKS

版本2.10.0中新增

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

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

$ python -m pip install requests[socks]
Copy after login

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

proxies = {
    &#39;http&#39;: &#39;socks5://user:pass@host:port&#39;,
    &#39;https&#39;: &#39;socks5://user:pass@host:port&#39;
}
Copy after login

使用 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;
Copy after login

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;}
Copy after login

传输适配器(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())
Copy after login

上述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)
Copy after login

阻塞或非阻塞

有了默认的传输适配器,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)
Copy after login

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

r = requests.get(&#39;https://github.com&#39;, timeout=(3.05, 27))
Copy after login

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

r = requests.get(&#39;https://github.com&#39;, timeout=None)
Copy after login

The above is the detailed content of How to use Python's Requests library?. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:yisu.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template