🐍 Python 发 HTTP 请求,requests、aiohttp 还是 httpx?一文彻底搞懂不踩坑!
做爬虫、调第三方 API、写自动化脚本,Python 里发 HTTP 请求几乎是必修课。但面对 requests、aiohttp、httpx 三个主流库,很多开发者容易懵:到底选哪个?它们的 get/post 写法一样吗?同步异步混用会有什么后果?
今天这篇,用一句话定位 + 核心代码对比 + 4 大避坑指南 + 选型决策树,帮你一次性理清脉络。建议收藏,下次写网络请求前翻出来对照,直接少走弯路!
🔑 一、一句话搞懂核心定位
| | |
|---|
requests | 同步阻塞时代的“老大哥” | |
aiohttp | 异步非阻塞的“性能猛兽” | 高并发爬虫、异步微服务、需 WebSocket/服务端 |
httpx | 异步时代的“六边形战士”,兼容 requests 语法,同时支持同步/异步 & HTTP/2 | |
💡 记忆口诀:requests 直来直去,aiohttp 挂起等待,httpx 语法同步底层可异步。
📊 二、终极对比表(2026 现状)
| requests | aiohttp | httpx |
|---|
| 执行模型 | | | |
| 并发方式 | 需 threading/multiprocessing | | |
| HTTP/2 | | | |
| 服务端能力 | | | |
| 底层运行时 | urllib3 | | anyio |
| API 风格 | | | httpx.Client() |
| 2026 维护状态 | | | |
💻 三、get / post 写法一样吗?
参数名高度一致(params, json, headers, cookies, timeout 等),但调用姿势和返回值处理天差地别。
1️⃣ GET 请求对比
# ✅ requests(同步)
import requests
resp = requests.get("https://api.example.com/data", params={"id": 1})
print(resp.json()) # 直接调用,阻塞等待
# ✅ aiohttp(异步)
import aiohttp, asyncio
async def fetch():
async with aiohttp.ClientSession() as session:
async with session.get("https://api.example.com/data", params={"id": 1}) as resp:
print(await resp.json()) # 必须 await,否则返回协程对象
asyncio.run(fetch())
# ✅ httpx(同步/异步双修)
import httpx
# 同步
with httpx.Client() as client:
print(client.get(url, params={"id": 1}).json())
# 异步
async with httpx.AsyncClient() as client:
print((await client.get(url, params={"id": 1})).json())
2️⃣ POST 请求对比(JSON + 超时)
# requests
requests.post(url, json={"user": "admin"}, timeout=10)
# aiohttp
async with session.post(
url,
json={"user": "admin"},
timeout=aiohttp.ClientTimeout(total=10) # ⚠️ 必须用 ClientTimeout
) as resp:
data = await resp.json()
# httpx
async with httpx.AsyncClient() as client:
resp = await client.post(url, json={"user": "admin"}, timeout=10.0)
data = resp.json() # 已封装 await 逻辑
📌 核心差异总结:
- •
requests:发请求 → 线程阻塞 → 拿结果 - •
aiohttp:发请求 → 协程挂起 → 事件循环调度其他任务 → I/O 完成 → 唤醒 → await 读数据 - •
httpx:语法像 requests,底层按同步/异步自动切换执行模型
⚠️ 四、90% 开发者会踩的 4 个坑(附解决方案)
| | |
|---|
| 1. 同步异步混用 | 在 async def 里调用 requests.get(),整个事件循环卡死 | 异步代码必须配异步客户端(aiohttp/httpx.AsyncClient) |
| 2. 不复用 Session | aiohttp 每次请求新建 ClientSession(),连接池失效,性能暴跌 10 倍+ | 全局复用 async with ClientSession() as session: |
| 3. 超时参数写错 | aiohttp.post(url, timeout=5) | 必须用 timeout=aiohttp.ClientTimeout(total=5) |
| 4. 响应体不读取 | async with session.get() as resp: 结束后不 await resp.json(),连接泄漏 | 异步响应必须显式 await 读取 body,否则底层连接无法归还池 |
🧭 五、2026 选型决策树(直接抄作业)
需要发 HTTP 请求?
├─ 请求量 < 50次/次,纯脚本或内部工具?
│ └─ 选 requests(简单够用)
├─ 需要高并发(>100 同时请求)、或项目已用 FastAPI/Starlette?
│ └─ 首选 httpx.AsyncClient(现代标准)
├─ 需要 WebSocket 长连接 / 自己写异步 HTTP 服务端?
│ └─ 选 aiohttp(生态最全)
└─ 必须支持 HTTP/2 或未来协议演进?
└─ 选 httpx(唯一原生支持)
📝 六、学习记忆卡片(建议截图保存)
| | | |
|---|
| requests | requests.get(url, params={}) | |
| httpx | async with httpx.AsyncClient() as c: await c.get() | 复用 Client,控制并发用 Semaphore |
| aiohttp | async with ClientSession() as s: async with s.get() as r: | |
💬 结语
Python 的网络请求库早已不是“谁更好用”的单选题,而是**“谁更匹配你的架构与场景”**的匹配题。
requests 守住了简洁的底线,aiohttp 证明了异步的威力,而 httpx 则用兼容性与现代协议支持,成为了 2026 年新项目的事实首选。
下次再写网络请求前,不妨先问自己三个问题:
答案自然浮现。