urllib3

urllib3 的文档 urllib3 是 python 的 HTTP 客户端,它提供了很多 python 标准库中没有的关键特性:线程安全、连接池、客户端 SSL/TLS 验证、用分段编码上传文件、重试请求和处理HTTP重定向的助手、支持gzip和deflate编码、HTTP和SOCKS的代理支持、100%的测试覆盖率。

1.用户指南

1.1 发出请求

import urllib3
# 先实例化PoolManager的对象,该对象能自动处理线程安全和连接池的细节
http = urllib3.PoolManager()
# 用 request() 发请求
r = http.request('GET', 'http://httpbin.org/robots.txt')

‘GET’ 可替换成其它的 HTTP 请求。

1.2 响应内容

request() 方法返回 HTTPResponse 对象,该对象有 status、data、headers 等属性。
status:响应的状态码;
headers:HTTP的请求头,是 HTTPHeaderDict 对象;
data:响应的内容,bytes 对象。data 是 JSON 内容时能通过解码和反序列化来加载。

1.3 请求数据

请求头

能以字典的形式在 request() 的 headers 参数中指定请求头。

查询的参数

对于 GET、HEAD、DELETE 请求,能把参数以字典的格式放到 fields 参数里传给 request()。

r = http.request('GET', 'http://httpbin.org/get', fields={'key1': 'value1', 'key2': 'value2'})

而 POST 和 PUT 请求需要把参数手动编码到 url 上。

from urllib.parse import urlencode
encoded_arg = urlencode({'key1': 'value1', 'key2': 'value2'})
url = url + '?' + encoded_arg
r = http.request('POST', url)

urlencode 函数也能接收双元素元组的序列:

encoded_arg = urlencode([('key1', 'value1'), ('key2', 'value2')])
表单数据

POST 和 PUT 请求中,放在 fields 中的参数会被 urllib3 自动编码为表单格式。

JSON

发送 JSON 请求要通过 body 参数指定 JSON 数据,并设置请求头中的 Content-Type 项。

import json
data = {'key': 'value'}
encoded_data = json.dumps(data).encode('utf-8')
r = http.request('POST', url, 
	body=encoded_data, 
	headers={'Content-Type': 'application/json'})
文件和二进制数据

上传文件把 fields 中的文件字段设置为形如 (文件名,数据) 的元组。

with open('test.txt') as f:
	file_data = f.read()
r = http.request('POST', url,
	fields = {'filefield': ('test.txt', file_data),})

指定文件名不是必须的,能用来匹配浏览器的行为。也能在元组中传递第三项来显示指定文件的 MIME1 类型:(文件名,数据,MIME类型)。
用 body 参数能直接传输二进制数据,Content-Type 最好同时改一下。

1.4 证书验证

1.25 版本后的 urllib3 会在默认情况下验证 HTTPS 连接 (cert_reqs = ‘CERT_REQUIRED’)。
除非另外指定,否则 urllib3 会自动加载默认的系统证书库。最可靠的跨平台方式是使用能提供 Mozilla 根证书捆绑的 certifi 模块。
使用 pip 直接安装或者用 urllib3 的额外命令 secure 安装。
获得证书后,您可以创建一个在发出请求时验证证书的 PoolManager。

import certifi
http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED',
	ca_certs=certifi.where())

PoolManager 会自动处理证书验证,验证失败时会抛出 SSLError。

1.5 使用超时参数

使用超时参数能控制请求中止前允许运行的最大时长。

r = http.request('GET', url, timeout=2.5)

使用一个 Timeout 实例能进行更细的控制:连接和读取分别设置时长。

r = http.request('GET', url, 
	timeout=urllib3.Timeout(connect=1.0, read=2.0))

可以在创建池的时候指定超时,使所有的请求都是相同的超时。该设置 会被 request() 中的超时设置所覆盖。

http = urllib3.PoolManager(timeout=3.0)

1.6 重试请求

默认情况下,urllib3将重试请求3次,并最多进行3次重定向。能通过 retries 参数来控制重试请求。

r = http.request('GET', url, retries=10)

retries 设置为 False 时,禁用所有重试和重定向请求。要禁用重定向但保留重试逻辑,设置 redirect 为 False。使用 Retry 实例能进行更精细的控制。
在创建池的时候指定重试,会为所有请求都配置该策略。

2. 高级用法

2.1 自定义池行为

PoolManager 类会根据需要自动为每个主机创建 ConnectionPool 实例。默认情况下最多创建 10 个实例。如果想请求很多不同的主机,增加该数值有助于提高性能。

http = urllib3.PoolManager(num_pools=50)

这么做同时也会增加内存和套接字的花销。
与上述情况类似。ConnectionPool 类还保留了一个独立的 HTTPConnection 实例池。其中的连接在一个独立请求期间使用,请求完成后又会返回池内。这种可复用的连接默认只有一个。如果想同时对相同的主机做很多请求的话,增加该数值有助于提高性能(下面两句代码等效2,选用一句即可)。

# 二者选其一
http = urllib3.PoolManager(maxszie=10)
http = urllib3.HTTPConnectionPool('baidu.com', maxsize=10)

ConnectionPool 类的池行为和 PoolManager 类不同。默认情况下,创建新请求时如果池内没有空闲的连接,就会新建一个连接。但是该连接在池内连接数已大于最大值的时候不会保留下来。也就是说,maxsize 参数不是可以向某个主机打开的最大连接数,而是池内可保留的最大连接数。指定参数 block=True 时,允许打开的最大连接数就是 maxsize 设定的数值。

# 二者选其一
http = urllib3.PoolManager(maxsize=10, block=True)
http = urllib3.HTTPConnectionPool(maxsize=10, block=True)

新请求会被阻塞,直到池中有连接可用为止。在多线程的应用中,这样做能避免太多连接使主机内存溢出。

2.2 流和IO

处理大的响应时,最好使用流式传输。

http = urllib3.PoolManager()
r = http.request('GET',
	'http://httpbin.org/bytes/1024',
	preload_content=False)
for chunk in r.stream(32):
	print(chunk)
r.release_conn()

把 preload_content 设为 False 时,urllib3 会流失传输响应内容。stream 函数使你能遍历响应内容的大块。必须用 release_conn() 函数把 http 连接释放回连接池,该连接才能复用。
流式传输获得的 HTPResponse 对象能看做是一个文件对象,允许进行缓冲。能使用 read()函数,还能用 codecs 模块对内容进行解码。

2.3 代理

使用 ProxyManager3

proxy = urllib3.ProxyManager('http://host:port/')
proxy.request('GET', url)

通过 SOCKSProxyManager 能连接到 SOCKS4 和 SOCKS5 的代理。使用 SOCKS 代理需要安装 PySocks 或者安装 urllib3 时带上额外的 socks 模块。

2.4 自定义SSL证书

不用使用 certifi 就能提供自己的证书捆绑包。当你已经生成自己的证书或者使用私有证书机构时,这非常有用。只要在创建 PoolManager 时提供到证书捆绑包的完整路径就行。

http = urllib3.PoolManager(cert_request='CERT_REQUIRED', 
	ca_certs='path/bundle')

使用自己的证书捆绑包时,只有能用该证书验证的请求才能成功。对于不需要自定义证书验证的 URL,最好有一个单独的 PoolManager 用来发出请求。

2.5 客户端证书

指定客户端证书可用于服务端和客户端验证彼此的身份。通常这些证书是从同一个证书机构颁发的。

http = urllib3.PoolManager(cert_file='/path/client_cert.pem',
	cert_reqs='CERT_REQUIRED',
	ca_certs='/path/certificate_bundle')

如果您具有加密的客户端证书私钥,则可以使用key_password参数指定用于解密密钥的密码。

http = urllib3.PoolManager(cert_file='/path/client_cert.pem',
	cert_reqs='CERT_REQUIRED',
	key_file='/path/client.key',
 	key_password='password')

2.6 证书验证和Mac OS 系统

苹果提供的 Python 和 OpenSSL 库包含一个修补程序,让它们能自动检查系统钥匙串的证书。如果您指定自定义证书并且看到请求意外成功,这可能会令人惊讶。例如,如果您要指定自己的证书进行验证,并且服务器提供了不同的证书,则您以为连接会失败。但是,如果该服务器出示系统钥匙串中的证书,则连接将成功。

2.7 SSL警告

urllib3 会基于证书验证支持的级别发出几个不同的警告。这些警告表明特殊情况的出现,能用不同的方式解决。
InsecureRequestWarning:对 HTTPS URL 发出请求时没有用证书验证。
InsecurePlatformWarning:在有过期 ssl 模块的Python 2平台上会发生。这些旧的 ssl 模块可能导致一些不安全的请求在该失败的地方成功,而安全的请求在该成功的地方失败。
SNIMissingWarning:2.7.9之前的Python 2版本上会出现。这些版本缺少 SNI 支持,可能会导致服务器出示客户端认为无效的证书。
不建议进行未经验证的 HTTPS 请求。如果了解风险并想忽视这些警告要使用 disable_warnings()。

urllib3.disable_warnings()

此外,还能用标准库的 logging 模块捕获这些警告;可以通过设置PYTHONWARNINGS 环境变量或使用 -W 标志在解释器级别抑制警告 。


  1. 多用途互联网邮件扩展类型。使 HTTP 传输的数据更加丰富。斜杠前是数据的大类型如文本、声音、图像等,斜杠后面是具体的文件类型。Headers 中的 Content-Type 指定的就是 MIME 类型。 ↩︎
  2. PoolManager 是 urllib3.request.RequestMethods 的子类;HTTPConnectionPool 同时是 ConnectionPool 和 urllib3.request.RequestMethods 的子类。所以,它们的对象都能发出请求。 ↩︎
  3. ProxyManager 是 PoolManager 的子类。 ↩︎