python爬虫番外篇 | Reuqests库高级用法(1)


今天来介绍了一些 Requests 更高级的功能。

1.会话对象(Session Objects)

Session 对象允许您在 请求。它还会在从 会话实例,并将使用的连接池。所以如果您正在向同一主机(底层 TCP)发出多个请求连接将被重用,这可以带来显著的性能增加。urllib3Session 对象具有主 Requests 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"}}'

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

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

传递给请求方法的任何字典都将与 设置的会话级别值。方法级参数覆盖会话参数。
但请注意,方法级参数不会在请求,即使使用会话。此示例将仅发送 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 实用程序函数来操作 。

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

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

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

从 dict 参数中删除值
有时,我们会希望从 dict 参数中省略会话级键。自 执行此操作时,只需在方法级别中将该键的值设置为参数。它将被自动省略。None

2.请求和响应对象(Request and Response Objects)

每当给朋友打电话时,我们都在做两个重大事项。首先,你正在构造一个对象,这个对象将是发送到服务器以请求或查询某些资源。其次,一旦 Requests 从服务器获得响应,就会生成一个对象。 该对象包含服务器返回的所有信息,以及还包含最初创建的对象。这是一个简单的请求从维基百科的服务器获取一些非常重要的信息:requests.get() Request 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'}

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

r.request.headers

{'Accept-Encoding': 'identity, deflate, compress, gzip',
'Accept': '*/*', 'User-Agent': 'python-requests/1.2.0'}

3.准备好的请求(Prepared Requests)

每当我们收到一个对象时 从 API 调用或 Session 调用中,该属性实际上是所使用的属性。在某些情况下,可能希望做一些额外的事情在发送请求。简单的方法如下:request Prepared Request

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)

由于没有对对象执行任何特殊操作,因此立即准备并修改对象。然后将和其他参数一起发送。RequestPreparedRequestrequests.Session.


但是,上述代码将失去具有Requests 对象的一些优点。具体而言,-level 状态(如 cookie)将 不会应用于的请求。要获得该状态应用时,将 to 的调用替换为 的调用,如下所示:

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_BUNDLESSL: 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)

4.SSL证书验证

Requests 验证 HTTPS 请求的 SSL 证书,就像 Web 浏览器一样。 默认情况下,SSL 验证处于启用状态,如果出现以下情况,Requests 将抛出 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]>

可以将路径传递到具有受信任 CA 证书的 CA_BUNDLE 文件或目录:verify

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

或持久性:

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

注意
如果设置为目录的路径,则必须已使用以下方法处理该目录 随 OpenSSL 提供的实用程序。verifyc_rehash

也可以通过环境变量指定此受信任 CA 列表。 如果未设置,将用作回退。REQUESTS_CA_BUNDLEREQUESTS_CA_BUNDLECURL_CA_BUNDLE

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

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

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

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

5.客户端证书

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

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

警告
本地证书的私钥必须是未加密的。 目前,Requests 不支持使用加密密钥。

6.CA 证书

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

在版本 2.16 之前,Requests 捆绑了一组它信任的根 CA, 来源于Mozilla信托商店。证书仅更新了每个请求版本一次。当未安装时,这导致了使用明显较旧的证书捆绑包时,证书捆绑包非常过时请求的版本。certifi

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

7.正文内容工作流程(Body Content Workflow)

默认情况下,当发出请求时,将下载响应的正文马上。可以覆盖此行为并延迟下载响应 body,直到使用参数访问该属性:stream

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

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

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

可以使用和方法进一步控制工作流。 或者,可以从底层读取未解码的正文 urllib3 。
如果在发出请求时设置为,则“请求”不能 将连接释放回池,除非消耗了所有数据或调用 。这可能导致连接效率低下。如果发现自己部分阅读请求 使用时正文(或根本不阅读它们),应该在语句中发出请求,以确保它始终处于关闭状态:streamTruestream=Truewith

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

8.keep-alive

好消息 — 多亏了 urllib3,在一个会话中 keep-alive 是 100% 自动的! 在会话中提出的任何请求都将自动重用适当的 连接!

请注意,只有在所有正文全部发生时,连接才会释放回池以供重复使用数据已被读取;请确保设置或读取对象的属性。streamFalsecontentResponse

9.流式上传(Streaming Uploads)

Requests 支持流式上传,允许发送大型流或文件,而不将它们读入内存。要流式传输和上传,只需提供一个 你身体的类似文件的物体:

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

警告
强烈建议以二进制文件打开文件模式。这是因为请求可能会尝试提供的标头,以及它是否执行此值 将设置为文件中的字节数。可能会出现错误如果以文本模式打开文件。Content-Length

10.块编码的请求(Chunk-Encoded Requests)

Requests 还支持对传出和传入请求进行分块传输编码。要发送块编码的请求,只需提供一个生成器(或任何没有的迭代器一个长度)对于你的身体:

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

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

对于分块编码响应,最好使用 循环访问数据。在将在请求中设置的理想情况,其中在这种情况下,您可以通过使用参数进行调用来逐块迭代。如果要设置块的最大大小, 可以将参数设置为任何整数。stream=Trueiter_contentchunk_sizeNonechunk_size

11.POST 多个多部分编码文件(POST Multiple Multipart-Encoded Files)

可以在一个请求中发送多个文件。例如,假设想要将图像文件上传到具有多个文件字段“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',
  ...
}

警告
强烈建议以二进制文件打开文件 模式。这是因为请求可能会尝试提供的标头,以及它是否执行此值将设置为文件中的字节数。可能会出现错误 如果以文本模式打开文件。Content-Length

  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值