在本篇文档我们讲一下 Requests 的一些高级特性。
会话对象
会话对象让你能够跨请求保持某些参数。它也会在同一个 Session 实例发出的所有请求之间保持 cookie, 期间使用 urllib3 的 connection pooling 功能。所以如果你向同一主机发送多个请求,底层的 TCP 连接将会被重用,从而带来显著的性能提升。
会话对象具有主要的 Requests API 的所有方法。我们来跨请求保持一些 cookie:
s = requests.Session()
s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
r = s.get("http://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('http://httpbin.org/headers', headers={'x-test2':'true'})
任何你传递给请求方法的字典都会与已设置会话层数据合并。方法层的参数覆盖会话的参数。不过需要注意,就算使用了会话,方法级别的参数也不会被跨请求保持。下面的例子只会和第一个请求发送 cookie ,而非第二个:
s = requests.Session()
r = s.get('http://httpbin.org/cookies', cookies={'from-my':'browser'})
print(r.text) #'{"cookies": {"from-my": "browser"}}'
r = s.get('http://httpbin.org/cookies')
print(r.text) #'{"cookies": {}}'
如果你要手动为会话添加 cookie,就使用 Cookie utility 函数 来操纵 Session.cookies。
会话还可以用作前后文管理器:
withrequests.Session()ass:
s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
这样就能确保 with 区块退出后会话能被关闭,即使发生了异常也一样。
有时你会想省略字典参数中一些会话层的键。要做到这一点,你只需简单地在方法层参数中将那个键的值设置为 None ,那个键就会被自动省略掉。
包含在一个会话中的所有数据你都可以直接使用。
准备的请求 (Prepared Request)
当你从 API 或者会话调用中收到一个 Response 对象时,request 属性其实是使用了 PreparedRequest。有时在发送请求之前,你需要对 body 或者 header (或者别的什么东西)做一些额外处理,下面演示了一个简单的做法:
from requests import Request, Session
s = Session()
req = Request('GET', url,
data=data,
headers=header
)
prepped = req.prepare()
# do something with prepped.body
# do something with prepped.headers
resp = s.send(prepped,
stream=stream,
verify=verify,
proxies=proxies,
cert=cert,
timeout=timeout
)
print(resp.status_code)
由于你没有对 Request 对象做什么特殊事情,你立即准备和修改了 PreparedRequest 对象,然后把它和别的参数一起发送到 requests.* 或者 Session.*。
然而,上述代码会失去 Requests Session 对象的一些优势, 尤其 Session 级别的状态,例如 cookie 就不会被应用到你的请求上去。要获取一个带有状态的 PreparedRequest, 请用 Session.prepare_request() 取代 Request.prepare() 的调用,如下所示:
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
# do something with prepped.headers
resp = s.send(prepped,
stream=stream,
verify=verify,
proxies=proxies,
cert=cert,
timeout=timeout
)
print(resp.status_code)
SSL 证书验证
Requests 可以为 HTTPS 请求验证 SSL 证书,就像 web 浏览器一样。SSL 验证默认是开启的,如果证书验证失败,Requests 会抛出 SSLError:
>>> requests.get('https://requestb.in')
requests.exceptions.SSLError: hostname'requestb.in' doesn'tmatch eitherof'*.herokuapp.com','herokuapp.com'
在该域名上我没有设置 SSL,所以失败了。但 Github 设置了 SSL:
>>> requests.get('https://github.com', verify=True)
<Response [200]>
你可以为 verify 传入 CA_BUNDLE 文件的路径,或者包含可信任 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 环境变量定义可信任 CA 列表。
如果你将 verify 设置为 False,Requests 也能忽略对 SSL 证书的验证。
>>> requests.get('https://kennethreitz.org', verify=False)
<Response [200]>
默认情况下, verify 是设置为 True 的。选项 verify 仅应用于主机证书。
# 对于私有证书,你也可以传递一个 CA_BUNDLE 文件的路径给 verify。你也可以设置 # REQUEST_CA_BUNDLE 环境变量。
客户端证书
你也可以指定一个本地证书用作客户端证书,可以是单个文件(包含密钥和证书)或一个包含两个文件路径的元组:
>>> requests.get('https://kennethreitz.org', cert=('/path/client.cert','/path/client.key'))
<Response [200]>
或者保持在会话中:
s= requests.Session()
s.cert ='/path/client.cert'
但如果你指定了一个错误路径或一个无效的证书就会报错:SSLError: [Errno 336265225] _ssl.c:347: error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib。
警告:
本地证书的私有 key 必须是解密状态。目前,Requests 不支持使用加密的 key。
保持活动状态(持久连接)
归功于 urllib3,同一会话内的持久连接是完全自动处理的!同一会话内你发出的任何请求都会自动复用恰当的连接!
注意:只有所有的响应体数据被读取完毕连接才会被释放为连接池;所以确保将 stream 设置为 False 或读取 Response 对象的 content 属性。
块编码请求
对于出去和进来的请求,Requests 也支持分块传输编码。要发送一个块编码的请求,仅需为你的请求体提供一个生成器(或任意没有具体长度的迭代器):
defgen():
yield'hi'
yield'there'
requests.post('http://some.url/chunked', data=gen())
对于分块的编码请求,我们最好使用 Response.iter_content() 对其数据进行迭代。在理想情况下,你的 request 会设置 stream=True,这样你就可以通过调用 iter_content 并将分块大小参数设为 None,从而进行分块的迭代。如果你要设置分块的最大体积,你可以把分块大小参数设为任意整数。
POST 多个分块编码的文件
你可以在一个请求中发送多个文件。例如,假设你要上传多个图像文件到一个 HTML 表单,使用一个多文件 field 叫做 "images":
<input type="file" name="images" multiple="true" required="true"/>
要实现,只要把文件设到一个元组的列表中,其中元组结构为 (form_field_name, file_info):
url ='http://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)打开文件。这是因为 requests 可能会为你提供 header 中的 Content-Length,在这种情况下该值会被设为文件的字节数。如果你用文本模式打开文件,就可能碰到错误。
*本文来源网络,如有侵权,请联系删除。