高级用法

https://farm5.staticflickr.com/4263/35163665790_d182d84f5e_k_d.jpg

本文档涵盖了一些更高级的请求功能。

会话对象

会话对象允许您在请求之间保留某些参数。它还跨会话实例发出的所有请求保持cookie,并将使用 urllib3connection pooling . 因此,如果您向同一个主机发出多个请求,则底层TCP连接将被重用,这可能导致性能显著提高(请参见 HTTP persistent connection

会话对象具有主请求API的所有方法。

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

s = requests.Session()

s.get('https://httpbin.org/cookies/set/sessioncookie/123456789')
r = s.get('https://httpbin.org/cookies')

print(r.text)
# '{"cookies": {"sessioncookie": "123456789"}}'

会话还可用于向请求方法提供默认数据。这是通过向会话对象上的属性提供数据来完成的:

s = requests.Session()
s.auth = ('user', 'pass')
s.headers.update({'x-test': 'true'})

# both 'x-test' and 'x-test2' are sent
s.get('https://httpbin.org/headers', headers={'x-test2': 'true'})

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

但是请注意,方法级参数将 not 即使使用会话,也要跨请求持久化。此示例只发送带有第一个请求的cookie,而不发送带有第二个请求的cookie::

s = requests.Session()

r = s.get('https://httpbin.org/cookies', cookies={'from-my': 'browser'})
print(r.text)
# '{"cookies": {"from-my": "browser"}}'

r = s.get('https://httpbin.org/cookies')
print(r.text)
# '{"cookies": {}}'

如果要手动向会话添加cookie,请使用 Cookie utility functions 操纵 Session.cookies .

会话还可以用作上下文管理器:

with requests.Session() as s:
    s.get('https://httpbin.org/cookies/set/sessioncookie/123456789')

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

从dict参数中移除值

有时,您需要从dict参数中省略会话级键。为此,只需将该键的值设置为 None 在方法级别参数中。它将自动省略。

会话中包含的所有值对您都是直接可用的。见 Session API Docs 学习更多。

请求和响应对象

无论何时调用给 requests.get() 朋友们,你们做了两件大事。首先,您正在构造 Request 对象,将发送到服务器以请求或查询某些资源。第二,A Response 一旦请求从服务器得到响应,就会生成对象。这个 Response 对象包含服务器返回的所有信息,还包含 Request 最初创建的对象。以下是从维基百科服务器获取一些非常重要信息的简单请求:

>>> r = requests.get('https://en.wikipedia.org/wiki/Monty_Python')

如果我们要访问服务器发送给我们的头,我们将执行以下操作:

>>> 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'}

但是,如果我们想要获取发送给服务器的头,我们只需访问请求,然后访问请求的头:

>>> r.request.headers
{'Accept-Encoding': 'identity, deflate, compress, gzip',
'Accept': '*/*', 'User-Agent': 'python-requests/1.2.0'}

准备好的请求

每当你收到 Response 对象来自API调用或会话调用, request 属性实际上是 PreparedRequest 那是用的。在某些情况下,在发送请求之前,您可能希望对主体或头部(或其他真正的内容)做一些额外的工作。简单的方法如下:

from requests import Request, Session

s = Session()

req = Request('POST', url, data=data, headers=headers)
prepped = req.prepare()

# do something with prepped.body
prepped.body = 'No, I want exactly this as the body.'

# do something with prepped.headers
del prepped.headers['Content-Type']

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

print(resp.status_code)

因为你对 Request 对象,立即准备并修改 PreparedRequest 对象。然后将它与其他参数一起发送给 requests.*Session.* .

但是,上述代码将失去具有请求的一些优势。 Session 对象。特别地, Session -级别状态(如cookies)不会应用于您的请求。得到一个 PreparedRequest 应用该状态后,替换对的调用 Request.prepare() 调用到 Session.prepare_request() ,像这样:

from requests import Request, Session

s = Session()
req = Request('GET',  url, data=data, headers=headers)

prepped = s.prepare_request(req)

# do something with prepped.body
prepped.body = 'Seriously, send exactly these bytes.'

# do something with prepped.headers
prepped.headers['Keep-Dead'] = 'parrot'

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

print(resp.status_code)

当您使用准备好的请求流时,请记住它不考虑环境。如果使用环境变量来更改请求的行为,这可能会导致问题。例如:中指定的自签名SSL证书 REQUESTS_CA_BUNDLE 不考虑。因此,一个 SSL: CERTIFICATE_VERIFY_FAILED 被扔掉。您可以通过将环境设置明确地合并到会话中来绕过这种行为:

from requests import Request, Session

s = Session()
req = Request('GET', 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)

SSL证书验证

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

>>> requests.get('https://requestb.in')
requests.exceptions.SSLError: hostname 'requestb.in' doesn't match either of '*.herokuapp.com', 'herokuapp.com'

我在这个域上没有SSL设置,所以它抛出了一个异常。杰出的。但Github做到了:

>>> requests.get('https://github.com')
<Response [200]>

你可以通过 verify 具有可信CA的证书的CA捆绑文件或目录的路径:

>>> requests.get('https://github.com', verify='/path/to/certfile')

或持久性:

s = requests.Session()
s.verify = '/path/to/certfile'

注解

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

还可以通过 REQUESTS_CA_BUNDLE 环境变量。

如果设置了 verify 假::

>>> requests.get('https://kennethreitz.org', verify=False)
<Response [200]>

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

客户端证书

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

>>> requests.get('https://kennethreitz.org', cert=('/path/client.cert', '/path/client.key'))
<Response [200]>

或持久性:

s = requests.Session()
s.cert = '/path/client.cert'

如果指定了错误的路径或无效的证书,将得到一个sslerror::

>>> requests.get('https://kennethreitz.org', cert='/wrong_path/client.pem')
SSLError: [Errno 336265225] _ssl.c:347: error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib

警告

本地证书的私钥 must 未加密。当前,请求不支持使用加密密钥。

CA证书

请求使用包中的证书 certifi . 这允许用户在不更改请求版本的情况下更新其可信证书。

在版本2.16之前,请求捆绑了一组它信任的根CA,源于 Mozilla trust store . 对于每个请求版本,证书只更新一次。什么时候? certifi 未安装,这导致在使用明显较旧版本的请求时出现非常过时的证书捆绑包。

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

正文内容工作流

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

tarball_url = 'https://github.com/requests/requests/tarball/master'
r = requests.get(tarball_url, stream=True)

此时,只下载了响应头,并且连接保持打开状态,因此允许我们将内容检索设置为条件:

if int(r.headers['content-length']) < TOO_LONG:
  content = r.content
  ...

您可以使用 Response.iter_content()Response.iter_lines() 方法。或者,您也可以从底层URLLIB3中读取未编码的主体。 urllib3.HTTPResponseResponse.raw .

如果你设置 streamTrue 在发出请求时,请求无法释放回池的连接,除非您使用所有数据或调用 Response.close . 这可能导致连接效率低下。如果您发现自己在使用时部分阅读请求正文(或根本不阅读请求正文) stream=True ,您应该在 with 确保始终关闭的语句:

with requests.get('https://httpbin.org/get', stream=True) as r:
    # Do things with the response here.

保持活力

好消息由于URLLIB3,在一个会话中保持活动是100%自动的!您在会话中提出的任何请求都将自动重用适当的连接!

请注意,只有在读取完所有主体数据后,才会将连接释放回池以便重用;请确保 streamFalse 或阅读 content 性质 Response 对象。

流式上传

请求支持流上载,这允许您发送大型流或文件而不将其读取到内存中。要传输和上传,只需为您的身体提供一个类似文件的对象:

with open('massive-body', 'rb') as f:
    requests.post('http://some.url/streamed', data=f)

警告

强烈建议您在 binary mode . 这是因为请求可能试图提供 Content-Length 头,如果它这样做,这个值将被设置为 bytes 在文件中。如果在中打开文件,可能会发生错误。 文本模式.

块编码请求

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

def gen():
    yield 'hi'
    yield 'there'

requests.post('http://some.url/chunked', data=gen())

对于分块编码的响应,最好使用 Response.iter_content() . 在理想的情况下你会 stream=True 根据请求,在这种情况下,可以通过调用 iter_content 用一个 chunk_size 参数 None . 如果要设置块的最大大小,可以设置 chunk_size 任何整数的参数。

发布多个多部分编码文件

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

<input type="file" name="images" multiple="true" required="true"/>

为此,只需将文件设置为 (form_field_name, file_info) ::

>>> url = 'https://httpbin.org/post'
>>> multiple_files = [
        ('images', ('foo.png', open('foo.png', 'rb'), 'image/png')),
        ('images', ('bar.png', open('bar.png', 'rb'), 'image/png'))]
>>> r = requests.post(url, files=multiple_files)
>>> r.text
{
  ...
  'files': {'images': 'data:image/png;base64,iVBORw ....'}
  'Content-Type': 'multipart/form-data; boundary=3131623adb2043caaeb5538cc7aa0b3a',
  ...
}

警告

强烈建议您在 binary mode . 这是因为请求可能试图提供 Content-Length 头,如果它这样做,这个值将被设置为 bytes 在文件中。如果在中打开文件,可能会发生错误。 文本模式.

事件钩子

请求有一个钩子系统,您可以使用它来操作请求过程的一部分,或者信号事件处理。

可用挂钩:

response

从请求生成的响应。

您可以通过传递 {{hook_name: callback_function}} 字典到 hooks 请求参数:

hooks={'response': print_url}

callback_function 将接收数据块作为其第一个参数。

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={'response': print_url})
https://httpbin.org/
<Response [200]>

您可以向单个请求添加多个挂钩。让我们一次呼叫两个钩子:

>>> r = requests.get('https://httpbin.org/', hooks={'response': [print_url, record_hook]})
>>> r.hook_called
True

还可以将挂钩添加到 Session 实例。然后,您添加的任何钩子都将在向会话发出的每个请求上被调用。例如::

>>> s = requests.Session()
>>> s.hooks['response'].append(print_url)
>>> s.get('https://httpbin.org/')
 https://httpbin.org/
 <Response [200]>

A Session 可以有多个钩子,将按添加钩子的顺序调用这些钩子。

自定义身份验证

请求允许您使用指定自己的身份验证机制。

任何作为 auth 请求方法的参数将有机会在发送请求之前对其进行修改。

身份验证实现是 AuthBase ,并且易于定义。请求提供了两个常见的身份验证方案实现 requests.authHTTPBasicAuthHTTPDigestAuth .

Let's pretend that we have a web service that will only respond if the 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['X-Pizza'] = self.username
        return r

然后,我们可以使用我们的比萨认证提出请求:

>>> requests.get('http://pizzabin.org/admin', auth=PizzaAuth('kenneth'))
<Response [200]>

流式处理请求

Response.iter_lines() 您可以轻松地迭代流式API,如 Twitter Streaming API . 简单设置 streamTrue 并使用 iter_lines ::

import json
import requests

r = requests.get('https://httpbin.org/stream/20', stream=True)

for line in r.iter_lines():

    # filter out keep-alive new lines
    if line:
        decoded_line = line.decode('utf-8')
        print(json.loads(decoded_line))

使用时 decode_unicode=True 具有 Response.iter_lines()Response.iter_content() 如果服务器未提供回退编码,您将希望提供回退编码::

r = requests.get('https://httpbin.org/stream/20', stream=True)

if r.encoding is None:
    r.encoding = 'utf-8'

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)

代理人

如果需要使用代理,可以使用 proxies 任何请求方法的参数::

import requests

proxies = {
  'http': 'http://10.10.1.10:3128',
  'https': 'http://10.10.1.10:1080',
}

requests.get('http://example.org', proxies=proxies)

还可以通过设置环境变量来配置代理 HTTP_PROXYHTTPS_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://example.org')

要对代理使用HTTP基本身份验证,请使用“http://user:password@host/”语法:

proxies = {'http': 'http://user:pass@10.10.1.10:3128/'}

要为特定方案和主机提供代理,请使用 scheme://hostname 键的窗体。这将与给定方案的任何请求和确切的主机名相匹配。

proxies = {'http://10.20.1.128': 'http://10.10.1.10:5323'}

请注意,代理URL必须包含该方案。

SOCKS

2.10.0 新版功能.

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

您可以从中获取此功能的依赖项 pip

$ pip install requests[socks]

一旦安装了这些依赖项,使用SOCKS代理和使用HTTP代理一样简单:

proxies = {
    'http': 'socks5://user:pass@host:port',
    'https': 'socks5://user:pass@host:port'
}

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

顺从

请求旨在符合所有相关规范和RFC,在这些规范和RFC的符合性不会给用户带来困难。对规范的这种关注可能会导致一些行为,对于不熟悉相关规范的人来说,这些行为可能是不寻常的。

编码

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

只有当HTTP头中没有显式字符集时,请求才会这样做。 and 这个 Content-Type 标题包含 text . 在这种情况下, RFC 2616 指定默认字符集必须为 ISO-8859-1 . 在这种情况下,请求遵循规范。如果需要不同的编码,可以手动设置 Response.encoding 属性,或使用原始 Response.content .

HTTP动词

请求提供对几乎所有HTTP谓词的访问:get、options、head、post、put、patch和delete。下面提供了使用GitHub API在请求中使用这些不同动词的详细示例。

我们将从最常用的动词开始:get。HTTP GET是一个等幂方法,它从给定的URL返回资源。因此,当您试图从Web位置检索数据时,应该使用这个动词。一个示例用法是尝试从GitHub获取有关特定提交的信息。假设我们想要承诺 a050faf 关于请求。We would get it like so:

>>> import requests
>>> r = requests.get('https://api.github.com/repos/requests/requests/git/commits/a050faf084662f3a352dd1a941f2c7c9f886d4ad')

我们应该确认Github的回应是正确的。如果有,我们想知道它是什么类型的内容。这样做:

>>> if r.status_code == requests.codes.ok:
...     print(r.headers['content-type'])
...
application/json; charset=utf-8

所以,Github返回json。太好了,我们可以用 r.json 方法将其解析为python对象。

>>> 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@kennethreitz.com', u'name': u'Kenneth Reitz'}

>>> print(commit_data[u'message'])
makin' history

到目前为止,很简单。好吧,我们来研究一下GitHub API。现在,我们可以查看文档,但是如果我们使用请求,可能会有更多的乐趣。我们可以利用requests-options动词来查看我们刚才使用的URL上支持哪些类型的HTTP方法。

>>> verbs = requests.options(r.url)
>>> verbs.status_code
500

呃,什么?那没用!结果发现,GitHub和许多API提供者一样,实际上并没有实现Options方法。这是一个令人恼火的疏忽,但没关系,我们可以只使用无聊的文档。但是,如果Github正确实现了选项,那么它们应该返回头中允许的方法,例如

>>> verbs = requests.options('http://a-good-website.com/api/cats')
>>> print(verbs.headers['allow'])
GET,HEAD,POST,OPTIONS

谈到文档,我们看到提交所允许的唯一其他方法是post,它创建了一个新的提交。当我们使用请求回购时,我们可能应该避免向它发送含人邮件。相反,让我们来讨论一下GitHub的问题功能。

添加此文档是为了响应 Issue #482 . 鉴于这个问题已经存在,我们将以它为例。让我们从得到它开始。

>>> r = requests.get('https://api.github.com/repos/requests/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

酷,我们有三条评论。让我们看看最后一个。

>>> 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

嗯,那似乎是个愚蠢的地方。让我们发表评论,告诉海报他很傻。不管怎样,海报是谁?

>>> print(comments[2][u'user'][u'login'])
kennethreitz

好吧,让我们告诉这个肯尼斯人,我们认为这个例子应该放在快速入门指南中。根据GithubAPI文档,实现这一点的方法是发布到线程。我们来做吧。

>>> body = json.dumps({u"body": u"Sounds great! I'll get right on it!"})
>>> url = u"https://api.github.com/repos/requests/requests/issues/482/comments"

>>> r = requests.post(url=url, data=body)
>>> r.status_code
404

啊,真奇怪。我们可能需要验证。那会很痛,对吧?错了。请求使得使用多种形式的身份验证变得容易,包括非常常见的基本身份验证。

>>> from requests.auth import HTTPBasicAuth
>>> auth = HTTPBasicAuth('fake@example.com', '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.

灿烂的。哦,等等,不!我想补充一句,我需要一段时间,因为我得去喂我的猫。如果我能编辑这个评论就好了!令人高兴的是,Github允许我们使用另一个HTTP动词patch来编辑此评论。让我们这样做。

>>> 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/requests/requests/issues/comments/5804413"

>>> r = requests.patch(url=url, data=body, auth=auth)
>>> r.status_code
200

杰出的。现在,为了折磨这个肯尼斯人,我决定让他流汗,而不是告诉他我正在努力。这意味着我想删除这个评论。Github允许我们使用非常恰当的命名删除方法删除注释。我们把它处理掉。

>>> r = requests.delete(url=url, auth=auth)
>>> r.status_code
204
>>> r.headers['status']
'204 No Content'

杰出的。都消失了。我最不想知道的是我用了多少钱。让我们看看。Github在头文件中发送这些信息,所以我不会下载整个页面,而是发送一个头文件请求来获取头文件。

>>> r = requests.head(url=url, auth=auth)
>>> print(r.headers)
...
'x-ratelimit-remaining': '4995'
'x-ratelimit-limit': '5000'
...

杰出的。是时候编写一个以各种令人兴奋的方式滥用GithubAPI的python程序了,4995次。

自定义动词

您可能会不时地使用一个服务器,无论出于什么原因,该服务器允许使用甚至需要使用上面未介绍的HTTP谓词。其中一个例子是一些WebDAV服务器使用的mkcol方法。不要担心,这些仍然可以用于请求。它们利用了内置的 .request 方法。例如::

>>> r = requests.request('MKCOL', url, data=data)
>>> r.status_code
200 # Assuming your call was correct

利用这个,您可以使用服务器允许的任何方法动词。

传输适配器

从v1.0.0开始,请求已转移到模块化内部设计。这样做的部分原因是为了实现传输适配器,最初 described here . 传输适配器提供了一种为HTTP服务定义交互方法的机制。特别是,它们允许您应用每个服务配置。

请求随单个传输适配器一起提供, HTTPAdapter . 此适配器提供默认请求与HTTP和HTTPS的交互,使用强大的 urllib3 类库。每当一个请求 Session 已初始化,其中一个附加到 Session 对象用于HTTP,一个用于HTTPS。

请求允许用户创建和使用自己的传输适配器,这些适配器提供特定的功能。一旦创建了传输适配器,就可以将其安装到会话对象上,同时指示它应该应用于哪个Web服务。

>>> s = requests.Session()
>>> s.mount('https://github.com/', MyAdapter())

mount调用将传输适配器的特定实例注册到前缀。一旦装入,任何使用该会话的HTTP请求(其URL以给定前缀开头)都将使用给定的传输适配器。

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

示例:特定的SSL版本

请求团队做出了一个特定的选择,使用基础库中默认的任何SSL版本。( urllib3 )通常情况下,这是可以的,但您可能会发现自己需要连接到使用与默认版本不兼容的服务端点。

为此,可以使用传输适配器,方法是获取httpadapter的大部分现有实现,并添加一个参数 ssl_version 传递给 urllib3. 我们将制作一个传输适配器,指示库使用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)

阻塞还是非阻塞?

在默认传输适配器就位后,请求不提供任何类型的非阻塞IO。这个 Response.content 在下载整个响应之前,属性将被阻止。如果您需要更大的粒度,库的流功能(请参见 流式处理请求 )允许您一次检索较小数量的响应。但是,这些调用仍将阻塞。

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

报头排序

在异常情况下,您可能希望以有序的方式提供报头。如果你通过 OrderedDictheaders 关键字参数,它将为头提供排序。 然而, 请求使用的默认头的顺序将是首选的,这意味着如果您重写 headers 关键字参数,与该关键字参数中的其他头相比,它们可能出现顺序错误。

如果这有问题,用户应该考虑在 Session 对象,通过设置 Session 风俗习惯 OrderedDict . 最好是订购。

超时

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

这个 connect Timeout是请求等待客户端建立到远程计算机的连接的秒数(对应于 connect() )打开插座。将连接超时设置为略大于3的倍数(默认值)是一个很好的实践。 TCP packet retransmission window .

一旦客户机连接到服务器并发送了HTTP请求, read Timeout是客户端等待服务器发送响应的秒数。(具体来说,它是客户机将等待的秒数 between 从服务器发送的字节数。在99.9%的情况下,这是服务器发送第一个字节之前的时间)。

如果为超时指定单个值,如下所示:

r = requests.get('https://github.com', timeout=5)

超时值将应用于 connect 以及 read 超时。如果要单独设置值,请指定元组::

r = requests.get('https://github.com', timeout=(3.05, 27))

如果远程服务器非常慢,您可以告诉请求永远等待响应,方法是将none作为超时值传递,然后检索一杯咖啡。

r = requests.get('https://github.com', timeout=None)