文章目录
- 正文内容工作流¶
- 保持活动状态Keep-Alive¶
- 流式上传¶
- 多编码块请求Chunk-Encoded Requests¶
- 上传多个多编码部分的文件¶
- 事件钩子¶
- 自定义身份验证¶
- 流式请求¶
- 代理¶
- SOCKS¶
- 合规性¶
- 编码¶
正文内容工作流¶
默认情况下,当您发出请求时,响应的内容即下载。你可以通过 stream
参数覆盖这个行为,推迟下载响应内容直到访问Response.content
属性:
tarball_url = 'https://github.com/psf/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
方法来控制工作流,或者以 Response.raw
从底层 urllib3 的 urllib3.HTTPResponse <urllib3.response.HTTPResponse>
读取未解码的响应内容。
如果你在请求中把stream
设为True
,Requests无法将连接释放回连接池,除非你获取了所有的数据,或者调用了Response.close
。 这样会带来连接效率低下的问题。如果你发现在使用stream=True
的同时还在部分读取请求的 body(或者完全没有读取 body),那么你就应该考虑使用 with
语句发送请求,这样可以保证请求一定会被关闭:
with requests.get('https://httpbin.org/get', stream=True) as r:
# Do things with the response here.
保持活动状态Keep-Alive¶
好消息!感谢urllib3,keep-alive在会话内是100%自动的!您在会话中发出的任何请求都将自动使用适当的连接!
请注意,只有在读取了所有响应内容后,才会将连接释放回池以供重用;请确保将stream
设置为False
,或读取Response
对象的content
属性。
流式上传¶
Requests支持流式上传,允许您发送较大的流或文件而无需将其读入内存。要使用流式上传,仅需为你的请求体提供一个类文件对象即可:
with open('massive-body', 'rb') as f:
requests.post('http://some.url/streamed', data=f)
警告
我们强烈建议你用二进制模式(binary mode)打开文件。这是因为Requests可以为你提供 header中的Content-Length,在这种情况下该值会被设为文件的字节数。如果你用文本模式打开文件,就可能碰到错误。
多编码块请求Chunk-Encoded Requests¶
对于接收和发送的请求,Requests还支持多分编码传输。要发送分块编码请求,只需为请求体提供一个生成器(或任何不带长度的迭代器):
def gen():
yield 'hi'
yield 'there'
requests.post('http://some.url/chunked', data=gen())
对于分块的编码请求,我们最好使用Response.iter_content()
对其数据进行迭代。在理想情况下,设置stream=True
,并将chunk_size
参数设为None
,这样你就可以通过调用iter_content
进行分块的迭代。如果要设置分块的最大长度,你可以把chunk_size
参数设为任意整数。
上传多个多编码部分的文件¶
您可以在一个请求中发送多个文件。例如,将图像文件上传到一个带有多个文件字段“images”的HTML表单:
<input type="file" name="images" multiple="true" required="true"/>
为此,只需将files设置为一个元组列表(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)打开文件。这是因为Requests会尝试为您设置header 中的“Content-Length”头,这种情况下该值将被设置为文件中的字节数。如果以文本模式打开文件,则可能发生错误。
事件钩子¶
Requests有一个钩子系统,您可以使用它来操作请求过程的某些部分,或者信号事件处理。
可用钩子: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]>
每个Session
可以有多个钩子,这些钩子将按照它们的添加顺序进行调用。
自定义身份验证¶
Requests允许您使用自己指定的身份验证机制。
任何传递给请求方法的auth
参数的可调用对象,在请求发出之前都有机会修改请求。
自定义的身份验证机制是通过定义requests.auth.AuthBase
的子类来实现,这非常容易。Requests在requests.auth
提供了两个常用的身份验证机制:HTTPBasicAuth<requests.auth.HTTPBasicAuth>
和HTTPDigestAuth<requests.auth.HTTPDigestAuth>
。
假设我们有一个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['X-Pizza'] = self.username
return r
然后,我们可以使用我们的PizzaAuth来进行网络请求:
>>> requests.get('http://pizzabin.org/admin', auth=PizzaAuth('kenneth'))
<Response [200]>
流式请求¶
使用Response.iter_lines()
你可以很方便地对流式API(例如 Twitter 的流式 API ) 进行迭代。简单地设置stream
为True
便可以使用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))
在使用Response.iter_lines()
或Response.iter_content()
函数设置decode_unicode=True
时,你需要提供一个回退编码方式,以防服务器没有提供默认编码而导致错误:
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_PROXY
和HTTPS_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
作为key。这将根据连接方式和特定主机进行匹配:
proxies = {'http://10.20.1.128': 'http://10.10.1.10:5323'}
请注意,代理URL必须包含特定连接。
SOCKS¶
2.10.0 新版功能
除了基本的HTTP代理之外,请求还支持使用SOCKS协议的代理。这是一个可选功能,若要使用, 你需要安装第三方库。
您可以从pip
获取依赖:
$ python -m pip install requests[socks]
一旦安装了这些依赖,使用SOCKS代理就和使用HTTP代理一样简单:
proxies = {
'http': 'socks5://user:pass@host:port',
'https': 'socks5://user:pass@host:port'
}
使用socks5
连接会导致DNS解析发生在客户机上,而不是在代理服务器上。这与curl一致,curl使用不同连接来确定在客户端或者代理服务器上执行DNS解析。如果要解析代理服务器上的域,请使用socks5
连接。
合规性¶
Requests 符合所有相关的规范和 RFC,这样不会为用户造成不必要的困难。但这种对规范的考虑导致一些行为对于不熟悉相关规范的人来说看似有点奇怪。
编码¶
当你收到一个响应时,Requests会猜测响应的编码方式,在你调用Response.text
时对响应进行解码。Requests首先在HTTP头部检测是否存在指定的编码方式,如果不存在,则会使用 charade
来尝试猜测编码方式。
只有当HTTP头部不存在明确指定的字符集,并且Content-Type头部字段包含text
值之时, Requests才不去猜测编码方式。在这种情况下,RFC 2616 指定默认字符集必须是 ISO-8859-1 。Requests 遵从这一规范。如果你需要一种不同的编码方式,你可以手动设置 Response.encoding
属性,或使用原始的Response.content
。