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

打印 上一主题 下一主题

主题 504|帖子 504|积分 1512

在这篇文章中,我们将深入探究Requests库的高级用法,包括变乱钩子、自定义身份验证、流式处理请求、代理利用,以及其他一些强大的特性。
变乱钩子(Event Hooks)

Requests库提供了一个强大的钩子体系,答应用户参与请求过程或对信号变乱进行处理。钩子可以在请求发出后立刻触发,比方响应钩子response。
利用钩子

通过将钩子函数传递给请求,可以对请求进行定制化处理。钩子以字典情势传递,如下所示:
  1. hooks = {'response': print_url}
复制代码
这里的print_url函数将作为响应钩子被调用,打印出响应的URL:
  1. def print_url(r, *args, **kwargs):
  2.     print(r.url)
复制代码
回调函数必须处理自己的非常。任何未经处理的非常都不会以静默方式传递,因此应由调用 Requests 的代码处理。假如回调函数返回一个值,则假定它是替换传入的数据。假如函数不返回任何事变,其他任何事变都不会受到影响。
  1. def record_hook(r, *args, **kwargs):
  2.     r.hook_called = True
  3.     return r
复制代码
让我们在运行时打印一些请求方法参数:
  1. requests.get('https://httpbin.org/', hooks={'response': print_url})
  2. https://httpbin.org/
  3. <Response [200]>
复制代码
可以向单个请求添加多个钩子。让我们一次调用两个钩子:
  1. r = requests.get('https://httpbin.org/', hooks={'response': [print_url, record_hook]})
  2. r.hook_called
  3. True
复制代码
还可以向实例添加钩子。添加的任何钩子都会在向聚会会议提出的每一项请求时都应被召唤。比方:Session
  1. s = requests.Session()
  2. s.hooks['response'].append(print_url)
  3. s.get('https://httpbin.org/')
  4. https://httpbin.org/
  5. <Response [200]>
复制代码
可以有多个钩子,这些钩子将按次序调用 它们将被添加。Session
自定义身份验证(Custom Authentication)

Requests支持自定义身份验证机制。通过实现AuthBase类,可以创建特定的认证方式,如下所示的PizzaAuth:
  1. from requests.auth import AuthBase
  2. class PizzaAuth(AuthBase):
  3.     def __init__(self, username):
  4.         self.username = username
  5.     def __call__(self, r):
  6.         r.headers['X-Pizza'] = self.username
  7.         return r
复制代码
利用这种方式,可以轻松地为请求添加自定义的HTTP认证。然后,我们可以利用我们的 Pizza Auth 提出请求:
  1. requests.get('http://pizzabin.org/admin', auth=PizzaAuth('kenneth'))
  2. <Response [200]>
复制代码
流式处理请求(Streaming Requests)

对于需要处理大量数据的API,Requests提供了流式处理功能。通过设置stream=True并利用iter_lines()方法,可以逐行读取响应数据:
  1. import json
  2. import requests
  3. r = requests.get('https://httpbin.org/stream/20', stream=True)
  4. for line in r.iter_lines():
  5.     # filter out keep-alive new lines
  6.     if line:
  7.         decoded_line = line.decode('utf-8')
  8.         print(json.loads(decoded_line))
复制代码
将 decode_unicode=True 与 或 一起利用时,您将需要 要在服务器未提供回退编码的情况下提供回退编码,请实行以下操纵:
  1. r = requests.get('https://httpbin.org/stream/20', stream=True)
  2. if r.encoding is None:
  3.     r.encoding = 'utf-8'
  4. for line in r.iter_lines(decode_unicode=True):
  5.     if line:
  6.         print(json.loads(line))
复制代码
  告诫
不是安全的。 多次调用此方法会导致接收到的某些数据迷失了。假如需要从多个地方调用它,请利用生成的迭代器对象:
  1. lines = r.iter_lines()
  2. # Save the first line for later or just skip it
  3. first_line = next(lines)
  4. for line in lines:
  5.     print(line)
复制代码
代理(Proxies)

Requests答应通过代剃头送请求。可以为单个请求设置代理,也可以在Session对象上配置代理,从而影响该会话的所有请求:
  1. import requests
  2. proxies = {
  3.   'http': 'http://10.10.1.10:3128',
  4.   'https': 'http://10.10.1.10:1080',
  5. }
  6. requests.get('http://example.org', proxies=proxies)
复制代码
大概,可以为整个配置一次:
  1. proxies = {
  2.     'http': 'http://10.10.1.10:3128',
  3.     'https': 'http://10.10.1.10:1080',
  4. }
  5. session = requests.Session()
  6. session.proxies.update(proxies)
复制代码
  告诫
设置的行为可能与预期差异。 提供的值将被情况代理覆盖 (由 urllib.request.getproxies 返回的那些)。 为确保在存在情况代理的情况下利用代理, 在所有单个请求上显式指定参数为最初在上面表明过。session.proxies
  当代理配置未按请求覆盖时,如上所示, 请求依靠于尺度定义的代理配置情况变量。还支持这些变量的大写变体。 因此,可以设置它们来配置请求(仅设置相关的请求 满足您的需求):http_proxy
  1. $ export HTTP_PROXY="http://10.10.1.10:3128"
  2. $ export HTTPS_PROXY="http://10.10.1.10:1080"
  3. $ export ALL_PROXY="socks5://10.10.1.10:3434"
  4. $ python
  5. >>> import requests
  6. >>> requests.get('http://example.org')
复制代码
要将 HTTP 根本身份验证与代理一起利用,请在上述任何配置条目中利用 http://user:password@host/ 语法:
  1. $ export HTTPS_PROXY="http://user:pass@10.10.1.10:1080"
  2. $ python
  3. >>> proxies = {'http': 'http://user:pass@10.10.1.10:3128/'}
复制代码
  告诫
将敏感的用户名和暗码信息存储在情况变量或版本控制的文件存在安全风险。
  要为特定方案和主机提供代理,请利用密钥的 scheme://hostname 表单。这将匹配对给定方案和确切主机名的任何请求。
  1. proxies = {'http://10.20.1.128': 'http://10.10.1.10:5323'}
复制代码
最后,请注意,利用代理进行 https 连接通常需要本地计算机信任代理的根证书。默认情况下,以下列表可以通过以下方式找到受请求信任的证书:
  1. from requests.utils import DEFAULT_CA_BUNDLE_PATH
  2. print(DEFAULT_CA_BUNDLE_PATH)
复制代码
可以通过将 (or ) 情况变量设置为另一个文件路径来覆盖此默认证书捆绑包:REQUESTS_CA_BUNDLECURL_CA_BUNDLE
  1. $ export REQUESTS_CA_BUNDLE="/usr/local/myproxy_info/cacert.pem"
  2. $ export https_proxy="http://10.10.1.10:1080"
  3. $ python
  4. >>> import requests
  5. >>> requests.get('https://example.org')
复制代码
SOCKS代理支持

Requests从2.10.0版本开始支持SOCKS代理,这要求安装额外的第三方库。
您可以从以下位置获取此功能的依靠项:pip
  1. $ python -m pip install requests[socks]
复制代码
一旦安装了这些依靠项,利用 SOCKS 代理就同样简朴 利用HTTP的:
  1. proxies = {
  2.     'http': 'socks5://user:pass@host:port',
  3.     'https': 'socks5://user:pass@host:port'
  4. }
复制代码
利用该方案会导致 DNS 解析在客户端上进行,而不是在代理服务器上进行。这与 curl 一致,curl 利用该方案来决定是在客户端还是代理上实行 DNS 解析。假如要解析代理服务器上的域,请利用 as the scheme。socks5
编码(Encodings)

Requests 库在接收到响应时会尝试猜测恰当的编码来解码响应内容。它起首查抄 HTTP 头部中是否指定了编码,假如没有找到,它将利用 charset_normalizer 或 chardet 库来尝试确定正确的编码。
  1. # Requests 会检查 HTTP 头部中是否指定了编码
  2. if r.encoding is None:
  3.     # 如果没有找到编码,Requests 会尝试猜测编码
  4.     r.encoding = 'utf-8'
复制代码
HTTP 动词(HTTP Verbs)

Requests 支持多种 HTTP 动词,答应实行各种范例的 HTTP 请求。以下是利用 GET、POST、OPTIONS 等动词的示例:
  1. # 使用 GET 请求获取资源
  2. r = requests.get('https://api.github.com/repos/psf/requests/git/commits/a050faf')
  3. # 使用 POST 请求提交数据
  4. r = requests.post('https://api.github.com/repos/psf/requests/issues/482/comments', data=body)
  5. # 使用 PATCH 请求更新资源
  6. r = requests.patch('https://api.github.com/repos/psf/requests/issues/comments/5804413', data=body)
  7. # 使用 DELETE 请求删除资源
  8. r = requests.delete('https://api.github.com/repos/psf/requests/issues/comments/5804413')
复制代码
我们应该确认 GitHub 的响应是正确的。假如有,我们想知道它是什么范例的内容。像如许做:
  1. import requests
  2. r = requests.get('https://api.github.com/repos/psf/requests/git/commits/a050faf084662f3a352dd1a941f2c7c9f886d4ad')
  3. if r.status_code == requests.codes.ok:
  4.     print(r.headers['content-type'])
  5. application/json; charset=utf-8
复制代码
因此,GitHub 返回 JSON。这很好,我们可以利用该方法将其解析为 Python 对象。
  1. commit_data = r.json()
  2. print(commit_data.keys())
  3. ['committer', 'author', 'url', 'tree', 'sha', 'parents', 'message']
  4. print(commit_data['committer'])
  5. {'date': '2012-05-10T11:10:50-07:00', 'email': 'me@kennethreitz.com', 'name': 'Kenneth Reitz'}
  6. print(commit_data['message'])
  7. makin' history
复制代码
到目前为止,就是这么简朴。好吧,让我们稍微研究一下 GitHub API。现在我们可以检察文档,但假如我们如许做,我们可能会有更多的乐趣。我们可以利用 Requests OPTIONS 动词来 看看我们刚才利用的 url 支持哪些范例的 HTTP 方法。
  1. verbs = requests.options(r.url)
  2. verbs.status_code
  3. 500
复制代码
呃,什么?这是无济于事的!究竟证实,GitHub 和许多 API 提供商一样,没有实际实现 OPTIONS 方法。这是一个令人讨厌的疏忽,但好吧,我们可以利用无聊的文档。假如 GitHub 正确实现了 OPTIONS,但是,它们应返回标题,比方
  1. verbs = requests.options('http://a-good-website.com/api/cats')
  2. print(verbs.headers['allow'])
  3. GET,HEAD,POST,OPTIONS
复制代码
转到文档中,我们看到唯一答应的其他方法 commits 的数据范例为POST,它将创建一个新的提交。当我们利用 Requests 存储库时, 我们可能应该制止对它进行鸠拙的操纵。相反,让我们利用 GitHub 的“题目”功能。
添加此文档是为了响应题目。鉴于这个题目已经存在,我们将以它为例。让我们从获取它开始。
  1. r = requests.get('https://api.github.com/repos/psf/requests/issues/482')
  2. r.status_code
  3. 200
  4. issue = json.loads(r.text)
  5. print(issue['title'])
  6. Feature any http verb in docs
  7. print(issue['comments'])
  8. 3
复制代码
很酷,我们有三个评论。让我们来看看此中的最后一个。
  1. r = requests.get(r.url + '/comments')
  2. r.status_code
  3. 200
  4. comments = r.json()
  5. print(comments[0].keys())
  6. ['body', 'url', 'created_at', 'updated_at', 'user', 'id']
  7. print(comments[2]['body'])
  8. Probably in the "advanced" section
复制代码
我们可能需要进行身份验证。那会很痛楚,对吧?通过“请求”,可以轻松利用多种情势的身份验证,包括 非经常见的根本身份验证。
  1. from requests.auth import HTTPBasicAuth
  2. auth = HTTPBasicAuth('fake@example.com', 'not_a_real_password')
  3. r = requests.post(url=url, data=body, auth=auth)
  4. r.status_code
  5. 201
  6. content = r.json()
  7. print(content['body'])
  8. Sounds great! I'll get right on it.
复制代码
我想知道的最后一件事是我的速率限制有多少。让我们来相识一下。GitHub 在标头中发送该信息,因此我不会下载整个页面,而是发送一个 HEAD 请求来获取头。
  1. r = requests.head(url=url, auth=auth)
  2. print(r.headers)
  3. 'x-ratelimit-remaining': '4995'
  4. 'x-ratelimit-limit': '5000'
  5. ...
复制代码
自定义动词(Custom Verbs)

假如需要利用非尺度 HTTP 动词,可以利用 request 方法直接指定:
  1. # 使用自定义 HTTP 动词 MKCOL
  2. r = requests.request('MKCOL', url, data=data)
  3. r.status_code
  4. 200 # Assuming your call was correct
复制代码
链讨论(Link Headers)

Requests 能够解析 HTTP 链讨论,并提供了一个易于利用的接口来访问它们:
  1. # GitHub 使用链接头进行分页
  2. url = 'https://api.github.com/users/kennethreitz/repos'
  3. r = requests.head(url)
  4. print(r.links["next"])  # {'url': 'https://api.github.com/users/kennethreitz/repos?page=2', 'rel': 'next'}
复制代码
请求将自动解析这些链讨论,并使它们易于利用:
  1. r.links["next"]
  2. {'url': 'https://api.github.com/users/kennethreitz/repos?page=2&per_page=10', 'rel': 'next'}
  3. r.links["last"]
  4. {'url': 'https://api.github.com/users/kennethreitz/repos?page=7&per_page=10', 'rel': 'last'}
复制代码
传输适配器(Transport Adapters)

Requests 从 v1.0.0 版本开始接纳模块化计划,引入了传输适配器的概念。这些适配器答应我们为特定的服务配置特定的行为:
  1. # 创建一个自定义的 SSL 版本传输适配器
  2. class Ssl3HttpAdapter(HTTPAdapter):
  3.     def init_poolmanager(self, *args, **kwargs):
  4.         kwargs['ssl_version'] = ssl.PROTOCOL_SSLv3
  5.         self.poolmanager = PoolManager(*args, **kwargs)
  6. # 使用自定义适配器
  7. s = requests.Session()
  8. s.mount('https://', Ssl3HttpAdapter())
复制代码
Requests 团队已做出特定选择,利用任何 SSL 版本底层库 (urllib3) 中的默认值。通常这很好,但是偶然,你可能会发现自己需要连接到服务终结点利用的版本与默认版本不兼容。
为此,可以利用传输适配器,方法是获取大部分现有的实现 HTTPAdapter,并添加一个参数ssl_version获取 传递到 URLLIB3。我们将制作一个传输适配器,用于指示利用 SSLv3 的库:
  1. import ssl
  2. from urllib3.poolmanager import PoolManager
  3. from requests.adapters import HTTPAdapter
  4. class Ssl3HttpAdapter(HTTPAdapter):
  5.     """"Transport adapter" that allows us to use SSLv3."""
  6.     def init_poolmanager(self, connections, maxsize, block=False):
  7.         self.poolmanager = PoolManager(
  8.             num_pools=connections, maxsize=maxsize,
  9.             block=block, ssl_version=ssl.PROTOCOL_SSLv3)
复制代码
自动重试(Automatic Retries)

Requests 答应我们实现自动重试机制,以增强连接的鲁棒性:
  1. # 实现自动重试
  2. from urllib3.util import Retry
  3. from requests.adapters import HTTPAdapter
  4. retries = Retry(
  5.     total=3,
  6.     backoff_factor=0.1,
  7.     status_forcelist=[502, 503, 504],
  8.     allowed_methods={'POST'},
  9. )
  10. s = requests.Session()
  11. s.mount('https://', HTTPAdapter(max_retries=retries))
复制代码
超时设置(Timeouts)

设置超时是制止请求无穷挂起的好风俗。对外部服务器的大多数请求都应该附加超时,以防服务器未实时响应。默认情况下,请求不管帐时 out 除非显式设置了超时值。假如没有超时,代码可能会挂起几分钟或更长时间。
连接超时是请求等待秒数 client 在套接字上建立与远程呆板(对应于 connect()) 调用的连接。设置连接超时是一种很好的做法略大于 3 的倍数,这是默认的 TCP 数据包重传窗口。
一旦客户端连接到服务器并发送了 HTTP 请求,读取超时就是客户端等待服务器的秒数发送响应。(详细来说,它是客户端的秒数 将在从服务器发送的字节之间等待。在 99.9% 的情况下,这是服务器发送第一个字节之前的时间)。可以为连接和读取分别设置超时时间:
  1. # 设置超时
  2. r = requests.get('https://github.com', timeout=5)
复制代码
假如要单独设置值,请指定一个元组:connectread
  1. r = requests.get('https://github.com', timeout=(3.05, 27))
复制代码
假如远程服务器速度非常慢,可以告诉请求永久等待响应,通过将 None 作为超时值传递
  1. r = requests.get('https://github.com', timeout=None)
复制代码
  注意
连接超时实用于每次尝试连接到 IP 地址。 假如一个域名存在多个地址,则底层将 按次序尝试每个地址,直到一个地址乐成连接。 这可能会导致有效的总连接超时时间延伸数倍超过指定时间,比方,同时具有 IPv4 和 IPv6 的无响应服务器地址的感知超时将增长一倍,因此请考虑到这一点设置连接超时。urllib3

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

灌篮少年

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表