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 几乎一致。
代码
python
# 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 使用。
同步示例:
python
import httpx
with httpx.Client() as client:
r = client.get("https://httpbin.org/delay/1")
print(r.status_code)
异步示例(需在 async 环境中运行,如 Jupyter 或 asyncio.run):
python
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:
python
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):
python
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):
python
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 参数进行配置。
示例:
python
import httpx
# 设置所有阶段超时为 3 秒
try:
r = httpx.get("https://httpbin.org/delay/5", timeout=3.0)
except httpx.TimeoutException:
print("请求超时!")
也可以分别设置:
python
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)。
python
# 连接超时+读超时
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。
例如:
python
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(如 例.测试)。
- Punycode背景
互联网早期,域名只允许使用 ASCII 字符(a-z, 0-9, -)。
但中文、阿拉伯文、俄文等用户也希望用自己的语言注册域名,比如:
https://例子.测试
但 DNS 系统(域名解析底层)不认非 ASCII 字符!
于是,人们发明了 Punycode ------ 一种编码规则,把 Unicode 域名转换成纯 ASCII 字符串。
- Punycode是什么
定义:一种将 Unicode 字符串(如中文、emoji)编码为 ASCII 字符串的算法。
格式:以 xn-- 开头,后面是编码后的字母数字组合。
目的:让国际化域名(IDN, Internationalized Domain Names)能在现有 DNS 系统中工作。
示例:
python
import httpx
# 注意:这个域名可能不存在,仅演示编码
url = "https://例子.测试"
print(httpx.URL(url)) # 自动转为 punycode 形式
✅ 实际请求时,DNS 解析会处理转换。
保持连接和连接池
解释:
HTTPX 会复用 TCP 连接(HTTP Keep-Alive),避免每次请求都新建连接。Client 对象内部维护连接池,提升性能。
示例:
python
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,并在后续请求中自动携带。
示例:
python
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 证书),防止中间人攻击。和浏览器一样严格。
示例(正常情况):
python
r = httpx.get("https://github.com") # 验证通过
禁用验证(不推荐):
python
r = httpx.get("https://self-signed.badssl.com", verify=False)
✅ 安全默认开启。
基本/摘要认证
解释:
支持 HTTP Basic Auth 和 Digest Auth。
Basic Auth 示例:
python
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+ 开始支持):
python
# 需要服务器支持 digest auth
auth = httpx.DigestAuth("user", "pass")
r = httpx.get("https://httpbin.org/digest-auth/auth/user/pass", auth=auth)
优雅的键值对 Cookie
解释:
可手动设置 Cookie,像字典一样操作。
示例:
python
cookies = {"session_id": "abc123"}
r = httpx.get("https://httpbin.org/cookies", cookies=cookies)
print(r.json())
自动解压缩
解释:
如果服务器返回 gzip、deflate 等压缩内容,HTTPX 会自动解压,你拿到的是原始内容。
示例:
python
r = httpx.get("https://httpbin.org/gzip")
print(r.json()["gzipped"]) # True,但你无需手动解压
自动内容解码
解释:
根据响应头的 Content-Type(如 charset=utf-8),自动将 bytes 解码为 str(通过 .text 属性)。
示例:
python
r = httpx.get("https://httpbin.org/encoding/utf8")
print(type(r.text)) # <class 'str'>
Unicode 响应体
解释:
支持任意 Unicode 字符,不会乱码(前提是服务器正确声明编码)。
多部分文件上传
解释:
支持 multipart/form-data 上传文件,类似 HTML 表单。
示例:
python
files = {"file": ("test.txt", b"Hello World")}
r = httpx.post("https://httpbin.org/post", files=files)
print(r.json()["files"])
也可上传真实文件:
python
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 代理。
示例:
python
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)读取,避免一次性加载到内存。
示例:
python
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 secret123machine api.example.com
login bob
password mypass456
每一组由三行组成:
- machine 域名:指定这个账号密码用于哪个网站(只写域名,不要带 http://)
- login 用户名
- password 密码
你可以为多个网站分别配置。
- 它解决了什么问题?
假设你有一个 API 接口需要 Basic Auth 认证:
python
# 没有 .netrc 时,你必须在代码里写死账号密码(不安全!)
r = httpx.get("https://httpbin.org/basic-auth/alice/secret123", auth=("alice", "secret123"))
但如果你把账号密码写在代码里:
- 容易被提交到 Git(泄露!)
- 换密码要改代码
- 多人协作麻烦
✅ 有了 .netrc,你的代码可以完全不出现密码:
python
# 代码干净又安全!
r = httpx.get("https://httpbin.org/basic-auth/alice/secret123")
只要 .netrc 里配了 machine httpbin.org 的账号密码,HTTPX 会自动读取并加上 Basic Auth 头!
- 那如果代码里同时写了 auth 和 .netrc,以代码里的 auth 为准,.netrc 被忽略。
示例(需先创建 ~/.netrc):
python
machine httpbin.org
login user
password pass
然后
python
r = httpx.get("https://httpbin.org/basic-auth/user/pass") # 自动带 auth
分块请求(Chunked Transfer Encoding)
解释:
当你发送的数据长度未知时,可使用分块传输(无需提前知道 Content-Length)。
示例:
python
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 客户端之一。