前言
httpx是python 的下一代 http 客户端。(官网自称)
httpx 是一个功能全面的 python 3 http 客户端,提供同步和异步 api,并支持 http/1.1 和 http/2。
我们就将官网的特性逐一进行讲解。
1. 广泛兼容的 requests api
解释:
httpx 的设计目标之一是让熟悉 requests 库的用户几乎无需学习成本就能上手。它的核心 api(如 httpx.get(), httpx.post(), response.text, response.json() 等)与 requests 几乎一致。
代码
# requests 风格
import httpx
resp = httpx.get("https://httpbin.org/get")
print(resp.status_code)
print(resp.json())
这段代码和用 requests 写法几乎一样,说明兼容性很好。
2. 标准同步接口,但也支持异步操作(如有需要)
解释:
httpx 同时提供了 同步客户端(httpx.client) 和 异步客户端(httpx.asyncclient)。
- 同步:适合脚本、简单爬虫、非高并发场景。
- 异步:适合高并发 i/o 密集型任务(如同时请求多个 api),需配合 async/await 使用。
同步示例:
import httpx
with httpx.client() as client:
r = client.get("https://httpbin.org/delay/1")
print(r.status_code)
异步示例(需在 async 环境中运行,如 jupyter 或 asyncio.run):
import asyncio
import httpx
async def main():
async with httpx.asyncclient() as client:
r = await client.get("https://httpbin.org/delay/1")
print(r.status_code)
asyncio.run(main())
3. 支持 http/1.1 和 http/2
解释:
httpx 默认使用 http/1.1,但如果目标服务器支持 http/2 且你安装了 httpx[http2](即 pip install httpx[http2]),它会自动协商使用 http/2。http/2 支持多路复用、头部压缩等,性能更好。
验证是否启用 http/2:
import httpx
# 需先安装: pip install httpx[http2]
with httpx.client(http2=true) as client:
r = client.get("https://http2.pro/")
print("using http/2:", r.http_version == "http/2")
4. 能够直接向 wsgi 应用 或 asgi 应用 发起请求
解释:
通常我们通过网络(如 http://localhost:8000)测试 web 应用。但 httpx 可以绕过网络层,直接调用 python 的 wsgi(如 flask/django)或 asgi(如 fastapi/starlette)应用对象,用于单元测试或集成测试,速度更快、更可靠。
wsgi(web server gateway interface)
(1)诞生时间:2003 年(python 社区标准,pep 3333)
(2)作用:定义了一个标准接口,让 python web 框架(如 flask、django)和 web 服务器(如 gunicorn、uwsgi)能够互相通信。
(3)特点:
同步:每个请求必须等前一个处理完才能开始。
不支持 websocket、长连接、http/2。
只处理 http。
(4)典型框架:flask、django(默认模式)、bottle。asgi(asynchronous server gateway interface)
(1)诞生时间:2017 年左右(为支持现代 web 需求而生)
(2)作用:wsgi 的异步超集,同样是一个标准接口,但支持异步 i/o 和多种协议。
(3)特点:
异步:一个请求等待数据库时,可以去处理另一个请求。
支持 websocket、http/2、sse(服务器推送)等。
向后兼容 wsgi(可通过适配器运行 wsgi 应用)。
(4)典型框架:fastapi、starlette、quart(异步版 flask)、django 3.0+(部分支持)。
wsgi 示例(flask):
from flask import flask
import httpx
from werkzeug.test import client as wsgiclient
from httpx._transports.wsgi import wsgitransport
app = flask(__name__)
@app.route("/hello")
def hello():
return {"msg": "hi"}
# 使用 httpx 直接调用 wsgi app
transport = wsgitransport(app=app)
with httpx.client(transport=transport, base_url="http://testserver") as client:
r = client.get("/hello")
print(r.json()) # {'msg': 'hi'}
asgi 示例(fastapi):
from fastapi import fastapi
import httpx
from httpx._transports.asgi import asgitransport
app = fastapi()
@app.get("/hello")
def hello():
return {"msg": "hi"}
transport = asgitransport(app=app)
with httpx.client(transport=transport, base_url="http://testserver") as client:
r = client.get("/hello")
print(r.json())
这对测试非常有用,无需启动真实服务器。
5. 严格的全局超时控制
解释:
httpx 允许你设置连接超时(connect)、读取超时(read)、写入超时(write) 和连接池超时(pool)。默认超时是 5 秒,可通过 timeout 参数精细控制。
- 连接超时(connect)指定等待与目标主机建立套接字连接的最长时间。如果 httpx 在此时间内无法建立连接,将引发 connecttimeout 异常。
- 读取超时(read)指定等待接收数据块(例如响应体的一部分)的最长持续时间。如果 httpx 在此时间内无法接收数据,将引发 readtimeout 异常。
- 写入超时(write)指定等待发送数据块(例如请求体的一部分)的最长持续时间。如果 httpx 在此时间内无法发送数据,将引发 writetimeout 异常。
- 连接池超时(pool)指定从连接池获取连接的最长等待时间。如果 httpx 在此时间内无法获取连接,将引发 pooltimeout 异常。与此相关的配置项是连接池允许的最大连接数,可通过 limits 参数进行配置。
示例:
import httpx
# 设置所有阶段超时为 3 秒
try:
r = httpx.get("https://httpbin.org/delay/5", timeout=3.0)
except httpx.timeoutexception:
print("请求超时!")
也可以分别设置:
timeout = httpx.timeout(connect=1.0, read=2.0, write=1.0, pool=5.0)
r = httpx.get("https://example.com", timeout=timeout)
比 requests 的超时控制更细粒度(requests 只有 connect + read)。
# 连接超时+读超时 response = requests.get(url, timeout=5) # 分别设置连接超时和读超时。第一个数字是连接超时时间(connect timeout),第二个数字是读取超时时间(read timeout) response = requests.get(url, timeout=(3, 5))
6. 完整的类型注解
解释:
httpx 所有函数、类、方法都带有 pep 484 类型提示(type hints),配合 mypy 或 ide(如 pycharm、vscode)可实现静态类型检查,减少 bug。
例如:
def fetch(url: str) -> httpx.response:
return httpx.get(url)
resp: httpx.response = fetch("https://example.com")
ide 能自动提示 resp.status_code 是 int,resp.text 是 str 等。
7. 100% 测试覆盖率
解释:
这是指 httpx 的源代码每一行都被单元测试覆盖到(通过工具如 pytest-cov 验证)。这不保证没有 bug,但说明开发者非常重视质量,变更不易引入回归问题。
📌 这属于项目质量指标,对用户透明,但让你用得更放心。
8. 以及 requests 的所有标准功能…
接下来这些其实是 requests 的经典功能,httpx 也都支持:
国际化域名和 url(idn)
解释:
支持包含非 ascii 字符的域名,如 https://例子.测试,httpx 会自动将其转换为 punycode(如 xn–fsq.xn–0zwm56d)。
- punycode背景
互联网早期,域名只允许使用 ascii 字符(a-z, 0-9, -)。
但中文、阿拉伯文、俄文等用户也希望用自己的语言注册域名,比如:
https://例子.测试
但 dns 系统(域名解析底层)不认非 ascii 字符!
于是,人们发明了 punycode —— 一种编码规则,把 unicode 域名转换成纯 ascii 字符串。
- punycode是什么
定义:一种将 unicode 字符串(如中文、emoji)编码为 ascii 字符串的算法。
格式:以 xn-- 开头,后面是编码后的字母数字组合。
目的:让国际化域名(idn, internationalized domain names)能在现有 dns 系统中工作。
示例:
import httpx # 注意:这个域名可能不存在,仅演示编码 url = "https://例子.测试" print(httpx.url(url)) # 自动转为 punycode 形式
✅ 实际请求时,dns 解析会处理转换。
保持连接和连接池
解释:
httpx 会复用 tcp 连接(http keep-alive),避免每次请求都新建连接。client 对象内部维护连接池,提升性能。
示例:
import httpx
with httpx.client() as client:
for _ in range(3):
r = client.get("https://httpbin.org/get")
print(r.status_code)
# 三次请求复用同一个连接(如果服务器支持)
带 cookie 持久化的会话
解释:
client 会自动保存服务器返回的 cookie,并在后续请求中自动携带。
示例:
import httpx
with httpx.client() as client:
# 第一次请求设置 cookie
client.get("https://httpbin.org/cookies/set?name=value")
# 第二次请求自动带 cookie
r = client.get("https://httpbin.org/cookies")
print(r.json()) # {'cookies': {'name': 'value'}}
浏览器式 ssl 验证
解释:
默认启用 ssl 证书验证(使用系统 ca 证书),防止中间人攻击。和浏览器一样严格。
示例(正常情况):
r = httpx.get("https://github.com") # 验证通过
禁用验证(不推荐):
r = httpx.get("https://self-signed.badssl.com", verify=false)
✅ 安全默认开启。
基本/摘要认证
解释:
支持 http basic auth 和 digest auth。
basic auth 示例:
r = httpx.get("https://httpbin.org/basic-auth/user/pass", auth=("user", "pass"))
print(r.status_code) # 200 if ok
digest auth 需要 httpx.digestauth(注意:requests 有,httpx 从 v0.23+ 开始支持):
# 需要服务器支持 digest auth
auth = httpx.digestauth("user", "pass")
r = httpx.get("https://httpbin.org/digest-auth/auth/user/pass", auth=auth)
优雅的键值对 cookie
解释:
可手动设置 cookie,像字典一样操作。
示例:
cookies = {"session_id": "abc123"}
r = httpx.get("https://httpbin.org/cookies", cookies=cookies)
print(r.json())
自动解压缩
解释:
如果服务器返回 gzip、deflate 等压缩内容,httpx 会自动解压,你拿到的是原始内容。
示例:
r = httpx.get("https://httpbin.org/gzip")
print(r.json()["gzipped"]) # true,但你无需手动解压
自动内容解码
解释:
根据响应头的 content-type(如 charset=utf-8),自动将 bytes 解码为 str(通过 .text 属性)。
示例:
r = httpx.get("https://httpbin.org/encoding/utf8")
print(type(r.text)) # <class 'str'>
unicode 响应体
解释:
支持任意 unicode 字符,不会乱码(前提是服务器正确声明编码)。
多部分文件上传
解释:
支持 multipart/form-data 上传文件,类似 html 表单。
示例:
files = {"file": ("test.txt", b"hello world")}
r = httpx.post("https://httpbin.org/post", files=files)
print(r.json()["files"])
也可上传真实文件:
with open("test.txt", "rb") as f:
files = {"file": f}
r = httpx.post("https://httpbin.org/post", files=files)
http(s) 代理支持
解释:
可通过 proxies 参数设置 http/https/socks 代理。
示例:
proxies = {"http://": "http://10.10.1.10:3128", "https://": "http://10.10.1.10:3128"}
r = httpx.get("https://example.com", proxies=proxies)
连接超时
已在上面第 5 条“严格的全局超时控制”中涵盖。只是比requests更加细致。
流式下载
解释:
对于大文件,可逐块(chunk)读取,避免一次性加载到内存。
示例:
with httpx.stream("get", "https://httpbin.org/stream-bytes/1024") as r:
for chunk in r.iter_bytes(chunk_size=128):
print(len(chunk)) # 每次 128 字节
.netrc 支持
解释:
如果存在 ~/.netrc 文件,httpx 会自动读取其中的认证信息(用户名/密码)用于对应域名。
.netrc文件
.netrc 是一个“自动填账号密码”的小本本,放在你电脑的家目录里,专门给命令行工具(比如 curl、httpx、ftp)用,避免每次都要手动输用户名和密码。
- 文件位置
- linux / macos:~/.netrc(即 /home/你的用户名/.netrc 或 /users/你的用户名/.netrc)
- windows:通常不支持,或需通过环境变量指定(httpx 在 windows 上对 .netrc 支持有限)
⚠️ 注意:这个文件必须权限设为 600(仅自己可读写),否则很多工具会拒绝使用(安全考虑):
chmod 600 ~/.netrc
- 文件格式(非常简单)
.netrc 是纯文本文件,内容长这样:
machine httpbin.org login alice password secret123 machine api.example.com login bob password mypass456
每一组由三行组成:
- machine 域名:指定这个账号密码用于哪个网站(只写域名,不要带 http://)
- login 用户名
- password 密码
你可以为多个网站分别配置。
- 它解决了什么问题?
假设你有一个 api 接口需要 basic auth 认证:
# 没有 .netrc 时,你必须在代码里写死账号密码(不安全!)
r = httpx.get("https://httpbin.org/basic-auth/alice/secret123", auth=("alice", "secret123"))
但如果你把账号密码写在代码里:
- 容易被提交到 git(泄露!)
- 换密码要改代码
- 多人协作麻烦
✅ 有了 .netrc,你的代码可以完全不出现密码:
# 代码干净又安全!
r = httpx.get("https://httpbin.org/basic-auth/alice/secret123")
只要 .netrc 里配了 machine httpbin.org 的账号密码,httpx 会自动读取并加上 basic auth 头!
- 那如果代码里同时写了 auth 和 .netrc,以代码里的 auth 为准,.netrc 被忽略。
示例(需先创建 ~/.netrc):
machine httpbin.org login user password pass
然后
r = httpx.get("https://httpbin.org/basic-auth/user/pass") # 自动带 auth
分块请求(chunked transfer encoding)
解释:
当你发送的数据长度未知时,可使用分块传输(无需提前知道 content-length)。
示例:
def data_generator():
yield b"part1"
yield b"part2"
r = httpx.post("https://httpbin.org/post", content=data_generator())
总结
httpx = requests 的易用性 + 异步 + http/2 + 类型安全 + 现代架构。
无论是写脚本、做测试、还是构建高性能服务,它都是目前 python 最先进的 http 客户端之一。
参考文献
到此这篇关于python的下一代http客户端httpx特性的文章就介绍到这了,更多相关python客户端httpx特性内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论