Python-httpx
本文最后更新于:2021年3月2日 上午
信息
HTTPX 是功能齐全的 Python 的 HTTP客户端,它提供同步和异步API,支持 HTTP/1.1 和 HTTP/2
官方文档:https://www.python-httpx.org/
github: https://github.com/encode/httpx/
安装
版本需求:Python 3.6+
1 |
|
如果希望支持 HTTP/2,可以使用:
1 |
|
如果希望支持 brotli 压缩算法,可以使用:
1 |
|
快速开始
请求
基础请求方法
基础的请求方法都类似
1 |
|
自定义header
1 |
|
get请求参数
1 |
|
post请求数据
1 |
|
post请求上传文件
1 |
|
可以一次性传输多个文件
1 |
|
post请求发送json
1 |
|
流式响应
二进制
1
2
3with httpx.stream("GET", "https://www.example.com") as r:
for data in r.iter_bytes():
print(data)文本
1
2
3with httpx.stream("GET", "https://www.example.com") as r:
for text in r.iter_text():
print(text)多行文本
1
2
3with httpx.stream("GET", "https://www.example.com") as r:
for line in r.iter_lines():
print(line)响应源码
未被经过 gzip, deflate, brotli 解压缩的源码1
2
3with httpx.stream("GET", "https://www.example.com") as r:
for chunk in r.iter_raw():
print(chunk)*注意**
如果您以任何上述方式使用流式响应,则response.content和response.text属性将不可用,并且在访问时会引发错误。
但是,你可以使用响应流功能来有条件地加载响应主体:1
2
3
4with httpx.stream("GET", "https://www.example.com") as r:
if r.headers['Content-Length'] < 99:
r.read()
print(r.text)超时
实际上,如果不进行特别的设置,这个超时的时间时5秒
1
httpx.get('https://github.com/', timeout=0.001)
你可以通过将它设置为None来禁用它,虽然这可能导致永远挂起
1
httpx.get('https://github.com/', timeout=None)
授权验证
明文身份验证
1
httpx.get("https://example.com", auth=("my_user", "password123"))
Digest 身份验证
1
2auth = httpx.DigestAuth("my_user", "password123")
httpx.get("https://example.com", auth=auth)响应
内容获取
二进制数据
程序会自动处理 gzip 和 deflate 压缩的响应
如果安装了 brotlipy,那么 brotli 压缩的响应也能够处理1
2
3r = httpx.get('https://www.example.org/')
print(r.content)文本
1
2
3r = httpx.get('https://www.example.org/')
print(r.encoding)
print(r.text)通常情况下都是utf-8,遇到不是的话,需要手动指定
1
r.encoding = 'ISO-8859-1'
json
1
2r = httpx.get('https://api.github.com/events')
print(r.json())状态码
1
2r = httpx.get('https://httpbin.org/get')
print(r.status_code)对于正常的响应,httpx有非常简单的判断方法
1
2is_ok = r.status_code == httpx.codes.OK
print(is_ok)对于异常的响应,也有简易的抛出错误的方法
这个方法若是响应正常,会返回None.若是出现问题,则会抛出对应错误1
r.raise_for_status()
响应头
1
2r = httpx.get('https://httpbin.org/get')
print(r.headers)重定向
1
2
3
4
5# 由http跳转到https
r = httpx.get('http://github.com/')
print(r.url)
print(r.status_code)
print(r.history)如果不想要跳转,那么可以设置
1
2
3r = httpx.get('http://github.com/', allow_redirects=False)
print(r.status_code)
print(r.history)在使用head方式发送请求时,也能用这个参数来启用跳转
1
2
3r = httpx.head('http://github.com/', allow_redirects=True)
print(r.url)
print(r.history)cookies
从响应中获取
1
2r = httpx.get('http://httpbin.org/cookies/set?chocolate=chip', allow_redirects=False)
print(r.cookies['chocolate'])请求时设置(简易)
1
2
3cookies = {"peanut": "butter"}
r = httpx.get('http://httpbin.org/cookies', cookies=cookies)
print(r.json())请求时设置(标准)
1
2
3
4
5cookies = httpx.Cookies()
cookies.set('cookie_on_domain', 'hello, there!', domain='httpbin.org')
cookies.set('cookie_off_domain', 'nope.', domain='example.org')
r = httpx.get('http://httpbin.org/cookies', cookies=cookies)
print(r.json())
更多用法
Client 实例
如果你曾经用过Requests,那么你可以将
httpx.Client()
看作是requests.Session()
来快速理解
如果你并不是想要写“一次性访问代码”,那么应该用客户端实例
每次在使用 “快速入门” 里面的顶层API来访问网络时都会 建立TCP连接。频繁的建立TCP连接会使得程序的效率会大幅度下降
Client
实例拥有 HTTP连接池。当你访问同一个网络,Client
重用已经创建了的TCP连接。这样做能显著地提高订正API的性能
- 减少了请求之间的延迟(重用连接无需握手)
- 减少CPU使用率和往返次数
- 减少网络拥塞
Client
也有其它的功能
- Cookie保持
- 在所有传出请求中应用配置
- 通过HTTP代理发送请求
- 使用HTTP/2
使用
推荐使用 with
语句来使用
1 |
|
也可以显式的在不需要使用用用close
函数关闭
1 |
|
请求
Clent
拥有和高层级API一样的函数,比如说 get()
, post()
,在 “快速开始” 中的方法大多都有实现
1 |
|
共享请求配置
可以为Client
配置内容,其配置的内容在发送请求时也会应用
1 |
|
此处get()
并没有传入headers,但由于Client
存在headers配置,最终的GET请求也存在headers配置
配置合并
当你同时在Client
中配置内容,在请求函数中也配置内容,可能会发生两种情况:
- 对于 Headers, Cookies, Query参数
它们的值会合并并一起应用到最后的请求中1
2
3
4
5
6
7
8
9
10headers = {'X-Auth': 'from-client'}
params = {'client_id': 'client1'}
with httpx.Client(headers=headers, params=params) as client:
headers = {'X-Custom': 'from-request'}
params = {'request_id': 'request1'}
r = client.get('https://example.com', headers=headers, params=params)
print(r.request.url)
print(r.request.headers['X-Auth'])
print(r.request.headers['X-Custom']) - 对于其它参数,请求函数中设置的参数会覆盖
Client
中设置的参数1
2
3
4
5
6
7with httpx.Client(auth=('tom', 'mot123')) as client:
r = client.get('https://example.com', auth=('alice', 'ecila123'))
_, _, auth = r.request.headers['Authorization'].partition(' ')
import base64
base64.b64decode(auth)
b'alice:ecila123'基础url
基础url可以让请求输入参数时少输入一些url更多的1
2
3
4with httpx.Client(base_url='http://httpbin.org') as client:
r = client.get('/headers')
print(r.request.url)Client
内容可以查看API
Request 实例
httpx 是支持显式创建 Request
实例的
1 |
|
创建的 Request
实例可以使用Client
实例的 send()
方法来发送
1 |
|
如果想要使用不同层级的配置,可以使用Client
实例的build_request()
方法
1 |
|
事件钩子 Event Hooks
httpx 可以让你注册 “事件钩子”,每当指定的事件发生,钩子内容会被调用
现在能够使用的钩子有两个
- request - 请求即将发送时调用。传递请求实例
- response - 响应返回后调用。传递响应实例
例:利用事件钩子进行记录
1 |
|
例:在状态码为4xx或5xx时,抛出错误
1 |
|
钩子能够传入多个函数,只要一并放入参数中即可
1 |
|
在设置完毕后,可以使用Client
对象的event_hooks
属性查看设置
1 |
|
如果未来使用异步请求,那么钩子函数也需要是异步函数
监视响应进度
如果需要监视响应进度(例如下载文件时的下载进度),可以使用 stream请求和response.num_bytes_downloaded
例:使用 tqdm
库 配合显示下载进度
1 |
|
.netrc支持
HTTPX支持 .netrc
文件。 在trust_env = True
情况下,如果未定义auth
参数,则HTTPX尝试将auth
从.netrc
文件添加到请求的标头中
默认情况下 trust_env
参数值为True
如果NETRC
环境没设置,那么 httpx 会尝试去读取系统默认环境下的文件
1 |
|
注意,如果使用Client
,那么trust_env
参数应该在Client
中进行设置,而不是在请求方法中传入
HTTP 代理
httpx支持使用代理,传入proxies
传入内容即可使用
例: 传入HTTP代理 与 HTTPS代理
1 |
|
代理路由
httpx提供多种不同的选择代理的方法
- 通配
让所有请求都走这个代理1
2
3proxies = {
"all://": "http://localhost:8030",
} - 协议匹配
根据请求类型走不同的代理1
2
3
4proxies = {
"http://": "http://localhost:8030",
"https://": "http://localhost:8031",
} - 域名匹配
域名为example.com
的请求走这个代理域名为1
2
3proxies = {
"all://example.com": "http://localhost:8030",
}example.com
的HTTP请求走这个代理域名及子域名匹配1
2
3proxies = {
"http://example.com": "http://localhost:8030",
}example.com
的请求走这个代理1
2
3proxies = {
"all://*example.com": "http://localhost:8030",
}example.com
子域名的请求都走这个代理1
2
3proxies = {
"all://*.example.com": "http://localhost:8030",
} - 端口匹配
访问”example.com”的1234端口的HTTPS请求走这个代理所有1234端口的请求走这个代理1
2
3proxies = {
"https://example.com:1234": "http://localhost:8030",
}1
2
3proxies = {
"all://*:1234": "http://localhost:8030",
} - 不走代理
只要设定键值为None
即可1
2
3
4
5
6proxies = {
# Route requests through a proxy by default...
"all://": "http://localhost:8031",
# Except those for "example.com".
"all://example.com": None,
}代理类型
代理根据它的流程有两种类型:转发 与 隧道 - 转发:就只是单纯的转发
- 隧道:中间人,即将客户端的数据包解析出来,再将数据重新发送。这样子包的记录信息(IP、Mac等)都时代理的信息
默认情况下 httpx 传输 HTTP时会使用 转发类型的代理,而传输HTTPS则使用隧道类型的代理
httpx提供了代理检查功能,通过使用httpx.Proxy
实例,设置 FORWARD_ONLY
或 TUNNEL_ONLY
1 |
|
Timeout
实际上,除了直接设置总体超时时间,httpx还可以对超时进行更细致的设定
超时有四种类型:
连接超时 connect
指定等待与请求的主机建立连接之前的最长时间。 如果HTTPX在此时间段内无法连接,则会引发ConnectTimeout异常读取超时 read
指定了等待接收数据(例如,响应主体的块)的最长时间。 如果HTTPX在此时间段内无法接收数据,则会引发ReadTimeout异常写超时 write
指定等待发送数据块(例如,请求正文的块)的最大持续时间。 如果HTTPX在此时间段内无法发送数据,则会引发WriteTimeout异常从连接池获取连接超时 pool
指定等待从连接池获取连接的最大持续时间。 如果HTTPX在此时间段内无法获取连接,则会引发PoolTimeout异常
1 |
|
连接池限制
你可以设置httpx连接池连接数量限制
max_keepalive
允许的保持活动连接数。为None
则表示无限制(默认值:10)max_connections
最大链接数。为None
则表示无限制(默认值:100)
1 |
|
SSL证书
通过HTTPS发出请求时,HTTPX需要验证所请求主机的身份。 为此,它使用由受信任的证书颁发机构(CA)交付的SSL证书捆绑包(也称为CA bundle)
默认情况下,httpx 使用 Certifi 的 CA bundle
在大多数情况下,这个设置已经足够了。但在一些情况可能需要使用不同的CA bundle
如果想要使用自己的CA bundle,可以通过 verify
参数传入
1 |
|
或者使用标准库ssl.SSLContext
1 |
|
httpx 也拥有方便创建 SSLContext
的函数create_ssl_context
函数接受与 httpx.Client
或httpx.AsyncClient
相同的SSL配置参数集(trust_env,verify,cert和http2参数)
1 |
|
如果你完全不需要SSL,可以通过对 verify
参数传入False
来进行设置
1 |
|
SSL配置与Client实例
如果你使用Client
实例,并且希望使用SSL配置,那么应该在创建它的时候传入
请求函数(get,post等)并不支持对SSL配置进行修改。如果在不同情况下需要不同的SSL设置,则应该使用多个客户端实例,对每个实例进行不同的设置。每个客户端将在该池中的所有连接上使用具有特定的固定SSL配置的隔离连接池
HTTPS请求与本地服务器
向本地服务器(例如在本地主机上运行的开发服务器)发出请求时,通常将使用未加密的HTTP连接
如果需要建立与本地服务器的HTTPS连接(例如,测试仅HTTPS服务),则需要创建并使用自己的证书(自签证书)
在服务器生成密钥对以后,拿到其对应的.pem
密钥文件,传入verify
参数之中即可
1 |
|
自定义传输
Client
能接受传输参数,该参数允许您提供一个自定义的传输对象,该对象将用于执行请求的实际发送
对于某些高级配置,需要直接实例化传输类,并将其传递给客户端实例。 httpcore
软件包提供了只能通过此低级API使用的local_address
配置
1 |
|
类似地,httpcore提供了一个uds选项,用于通过Unix域套接字进行连接,该选项仅可通过以下底层API使用:
1 |
|
与 Client
不同,较低级别的httpcore传输实例不包含用于配置方面(例如连接池详细信息)的任何默认值,因此在使用此API时需要提供更明确的配置
自定义传输类
传输实例必须实现 httpcore
定义的传输API
自定义传输类应该继承httpcore.AsyncHTTPTransport
来实现与AsyncClient
一起使用的传输
或者继承httpcore.SyncHTTPTransport
来实现与Client
一起使用的传输
定制传输实现的完整示例为:
1 |
|
上面代码的作用与下面的代码相同
1 |
|
异步
异步是一种并发模型,其效率远远高于多线程,并且可以提供显着的性能优势并允许使用长期存在的网络连接(例如WebSockets)
想要用 httpx 发出异步请求,则需要使用AsyncClient
1 |
|
有AsyncClient
有很多与Client
相似的请求方法
- AsyncClient.get(url, …)
- AsyncClient.options(url, …)
- AsyncClient.head(url, …)
- AsyncClient.post(url, …)
- AsyncClient.put(url, …)
- AsyncClient.patch(url, …)
- AsyncClient.delete(url, …)
- AsyncClient.request(method, url, …)
- AsyncClient.send(request, …)
关闭
AsyncClient
的关闭的方法时aclose()
1 |
|
但是,实际上依旧推荐使用with
来使用,并不推荐显式关闭
流式响应
1 |
|
Response.aread()
用于有条件地读取流块内的响应Response.aiter_bytes()
用于以字节形式流式传输响应内容Response.aiter_text()
用于以文本形式流式传输响应内容Response.aiter_lines()
用于将响应内容作为文本流传输Response.aiter_raw()
用于流式传输原始响应字节,而无需应用内容解码Response.aclose()
用于关闭响应。 通常不需要这样做,因为.stream块会在退出时自动关闭响应
对于无法使用上下文块的情况,可以通过使用client.send(...,stream = True)
发送一个 Request实例 来进入“手动模式”
1 |
|
AsyncIO
1 |
|
HTTP2
HTTP/2 提供了更有效的传输,并具有潜在的性能优势。HTTP2不会更改请求或响应的核心语义,但会更改数据与服务器之间的发送方式
httpx 默认并不支持 HTTP/2,如果想要使用,则需要启用HTTP/2支持
首先,需要安装 httpx 的 HTTP/2依赖
1 |
|
再带那么重初始化支持 HTTP/2 的 Client
1 |
|
Client
和 AsyncClient
都可以使用 HTTP/2 支持
检查HTTP版本
在Client
上启用 HTTP/2 支持不一定意味着请求和响应将通过 HTTP/2 传输,因为只有客户端和服务器都支持 HTTP/2,HTTP2才会运行。如果连接到仅支持HTTP / 1.1的服务器,那么Client
会自动改用 HTTP/1.1 进行连接
通过检查Response.http_version
属性来确定使用了哪个版本的HTTP协议
1 |
|
后续可继续查阅官方文档内容
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!