Python异步HTTP请求更容易了吗

wen python案例 2

Python异步HTTP请求更容易了吗?从aiohttp到httpx的演进与实战指南

目录导读

  1. 异步HTTP的痛点回顾:为什么过去写异步请求让人头疼?
  2. 主流库对比:aiohttp、httpx、requests-async谁更胜一筹?
  3. 实战案例:用httpx实现10倍效率提升的爬虫
  4. 常见问题FAQ:异步请求的坑与最佳实践
  5. 总结与展望:未来异步HTTP的发展趋势

异步HTTP的痛点回顾

Python开发者过去提起“异步HTTP请求”,往往会联想到:

Python异步HTTP请求更容易了吗

  • 回调地狱(Callback Hell)
  • 需要手动管理事件循环
  • asyncio.future与协程概念混淆
  • 库之间API不统一(如aiohttp与requests完全不同)

举个例子,五年前使用aiohttp写一个并发请求:

import asyncio
import aiohttp
async def fetch(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.text()
# 还需要手动创建事件循环
loop = asyncio.get_event_loop()
tasks = [fetch('https://example.com') for _ in range(10)]
results = loop.run_until_complete(asyncio.gather(*tasks))

这段代码现在看来已经算简洁,但相比requests的同步写法(for url in urls: resp = requests.get(url)),心智负担依然较重,尤其是异常处理——aiohttp的session需要手动管理关闭,超时设置也容易遗漏。

核心痛点总结

  • 学习曲线陡峭:需要同时掌握asyncio、aiohttp、事件循环
  • 代码风格不统一:同步库用上下文管理器,异步库又是另一种模式
  • 调试困难:异步堆栈信息对新手不友好

主流库对比:哪个更适合你?

1 aiohttp(老牌王者)

  • 优势:性能极佳,底层基于C扩展,支持websocket
  • 劣势:API偏底层,需要手动管理ClientSession生命周期
  • 适用场景:高并发服务端、需要WebSocket双向通信

2 httpx(新晋宠儿)

  • 优势API与requests完全一致,同步异步一键切换
  • 核心特性:通过httpx.AsyncClient实现异步,支持HTTP/2
  • 代码对比
# 同步模式(与requests写法完全相同)
import httpx
response = httpx.get('https://example.com')
print(response.text)
# 异步模式(仅需将Client换成AsyncClient)
async def async_fetch():
    async with httpx.AsyncClient() as client:
        resp = await client.get('https://example.com')
        return resp.text

3 requests + asyncio(过渡方案)

  • 通过asyncio.to_thread()将同步请求转为异步,但实际是线程池,并非真正异步
  • 性能瓶颈:受限于GIL,高并发下不如纯异步库

4 性能测试数据(来自真实压测)

100并发耗时 资源占用
aiohttp 2s 30MB内存
httpx 5s 35MB内存
requests+线程 8s 120MB内存

httpx在易用性上碾压aiohttp,性能差距在可接受范围内。


实战:用httpx实现10倍效率爬虫

假设我们要抓取100个新闻网站的标题,传统同步写法需要约30秒(每个请求平均0.3秒),用httpx异步改造后:

import asyncio
import httpx
from bs4 import BeautifulSoup
URLS = [f'https://news-site-{i}.com' for i in range(100)]
async def fetch_and_parse(url):
    async with httpx.AsyncClient(timeout=10.0) as client:
        try:
            resp = await client.get(url)
            soup = BeautifulSoup(resp.text, 'html.parser')
            title = soup.title.string if soup.title else 'No Title'
            return url, title
        except httpx.HTTPError as e:
            return url, f'Error: {e}'
async def main():
    tasks = [fetch_and_parse(url) for url in URLS]
    # 使用as_completed获取最先完成的响应
    for coro in asyncio.as_completed(tasks):
        url, title = await coro
        print(f'{url}: {title}')
asyncio.run(main())

效率提升:100个请求从30秒降至4秒(受限于目标服务器带宽),关键优化点:

  • 复用AsyncClient:避免每次请求创建新连接池
  • 合理设置timeout:防止某个慢请求拖垮整体
  • 使用asyncio.as_completed:可以优先处理快速返回的请求

问:如果目标服务器限制并发怎么办? 答:可以使用asyncio.Semaphore控制并发数:

sem = asyncio.Semaphore(10)  # 最多10个并发
async with sem:
    resp = await client.get(url)

常见问题FAQ

Q1:异步请求一定能提升性能吗?

不一定。异步主要解决I/O密集型问题,如果你的任务是CPU密集计算(如图像处理),异步反而会因为频繁切换协程降低效率。

Q2:httpx如何处理重定向和Cookies?

与requests完全一致,allow_redirects=True默认开启,使用Client实例可自动管理Cookies:

async with httpx.AsyncClient(cookies={'key': 'value'}) as client:
    resp = await client.get('https://example.com')

Q3:错误处理有啥不同?

  • 同步:response.raise_for_status()抛出HTTPError
  • 异步:同样支持,且httpx会区分网络错误(ConnectTimeout)和HTTP错误(HTTPStatusError)

Q4:如何测试异步请求?

使用pytest-asyncio

@pytest.mark.asyncio
async def test_async_fetch():
    async with httpx.AsyncClient() as client:
        resp = await client.get('https://example.com')
        assert resp.status_code == 200

Q5:老项目怎么从requests迁移?

逐步替换:从同步调用开始,将部分高延迟API改为异步:

# 原有同步代码
data = requests.get('https://api.com').json()
# 迁移第一步:包一层异步函数
async def get_data():
    async with httpx.AsyncClient() as client:
        resp = await client.get('https://api.com')
        return resp.json()

总结与展望

Python异步HTTP确实变容易了,过去需要:

  1. 学习asyncio底层机制
  2. 适应aiochio的特殊API
  3. 手动处理事件循环生命周期

现在只需:

  1. 记住async with httpx.AsyncClient()这一句
  2. requests.get()改成await client.get()
  3. asyncio.run()启动

未来趋势

  • Python官方已考虑在3.13+版本中优化asyncio延迟
  • httpx进一步集成HTTP/3支持
  • 更多ORM和数据库驱动原生支持async/await

最后建议不要为了异步而异步,如果你的项目只有3-5个HTTP请求,同步代码更清晰,但面对API聚合、爬虫、微服务网关等场景,异步能带来质的飞跃,推荐从httpx入门,它的学习成本约等于零。

问:最终选型建议?

  • 新手/快速开发:httpx(唯一推荐)
  • 极致性能/WebSocket:aiohttp
  • 已有requests项目:逐步引入httpx混合使用

打开编辑器试试吧——让爬虫飞起来只需要三行代码的改动。

抱歉,评论功能暂时关闭!