理解你的需求。从一个写了多年 Python、用 httpx 处理过数不清 HTTP 请求的人的角度来聊这个话题我尽量不搞那些生硬的列表和说教就像平时在工位上跟同事讨论一个问题那样。先说它到底是什么。httpx 本质上是一个 Python 的 HTTP 客户端库。如果你用过 requests那基本上能猜到它大概能干什么——发 GET 请求、POST 请求、处理 cookies、处理会话等等。但有一点不太一样httpx 从设计之初就把“异步”当成了头等公民。不是后来补丁式地加上 async 支持而是从一开始就规划了两条腿走路同步和异步都能跑得流畅。打个比方说requests 就像一辆经典的手动挡轿车皮实耐用你熟悉它每一个档位。而 httpx 是一辆自动挡的车同时还能一键切换到赛车模式。它们都能把你从 A 点送到 B 点但后者给了你更多选择尤其是在需要同时处理大量请求的场景下。它能做什么以及为什么它值得关注。除了常规的 GET/POST 这些基础操作httpx 有几个地方让我觉得它不只是“另一个 requests 替代品”。一是对 HTTP/1.1 和 HTTP/2 的原生支持。requests 至今还是基于 urllib3只支持 HTTP/1.1。而 httpx 底层用了 h11 和 h2 这两个库连接复用、多路复用这些特性在处理高并发或者需要与支持 HTTP/2 的服务交互时差别很明显。举个具体的场景你写一个爬虫去抓某个 Web 应用的内容如果目标服务器已经用了 HTTP/2用 httpx 能更快地建立连接减少 TCP 握手和 TLS 协商的开销。二是它完善的类型注解。整个库的代码都是类型标注的配合现代编辑器或者静态类型检查工具写代码时体验极好。你写client.get(url)返回的是什么类型参数应该传什么类型IDE 都能直接提示。这不是什么惊天动地的特性但实际写起来少了很多查文档的打断。三是它对 ASGI 应用的直接支持。你可以用 httpx 直接给一个 ASGI 应用发送请求而不需要真正启动一个 HTTP 服务器。比如你在测试一个 FastAPI 或者 Starlette 应用时可以这样写asyncwithhttpx.AsyncClient(appapp)asclient:rawaitclient.get(/)这样就能直接调用你的应用层逻辑不走网络栈测试速度快得像本地函数调用。对于写 Web 服务的人来说这是个很顺手的功能。怎么用一个真实的例子。假设你要写一个工具定期拉取某个 API 的数据并且需要处理重试、超时、以及对某些路径做连接池复用。这是我从一个生产脚本里摘出来调整过的写法importhttpximportasyncioclassDataFetcher:def__init__(self,base_url,api_key):self.base_urlbase_url self.headers{Authorization:fBearer{api_key}}# 设置超时为10秒最多保持50个连接self.clienthttpx.AsyncClient(base_urlself.base_url,headersself.headers,timeout10.0,limitshttpx.Limits(max_keepalive_connections50,max_connections100))# 注意这里特意没有用 context manager而是在 shutdown 中手动关闭# 因为客户端要作为对象的长期属性复用asyncdeffetch(self,path,paramsNone):try:respawaitself.client.get(path,paramsparams)resp.raise_for_status()returnresp.json()excepthttpx.HTTPStatusErrorasexc:# 如果是429或者5xx先不处理重试逻辑raiseasyncdefshutdown(self):awaitself.client.aclose()asyncdefmain():fetcherDataFetcher(https://api.example.com,your-key)try:resultawaitfetcher.fetch(/v1/data,params{limit:10})print(result)finally:awaitfetcher.shutdown()asyncio.run(main())这里有几个细节一是把客户端作为对象的实例属性而不是每次调用都新建一个。因为每次创建客户端都会新建连接池频繁创建的开销很大。二是在 shutdown 方法中显式关闭客户端确保底层连接正确释放。一些实践的体会。写 httpx 时容易踩的一个坑是超时配置。默认情况下httpx 的客户端没有设置连接超时或读取超时也就是说如果你不传 timeout 参数它可能会一直等下去。这在线上环境几乎是个事故隐患。一定要显式设置 timeout。另一个是重试策略。httpx 没有内置重试机制这点和 requests 一样。需要自己实现。写一个简单的重试装饰器或者用 tenacity 这样的库来控制。重试时要考虑是否对幂等请求重试以及重试间隔的抖动jitter防止造成雪崩。还有一点容易被忽略在异步代码中如果调用了同步的 httpx 方法比如httpx.request()它会阻塞事件循环。所以在异步应用中一定要用AsyncClient并且用await调用。和 requests、aiohttp 的对比。这个对比没什么谁好谁坏更多是适用场景的取舍。requests 是经典选择。如果你想快速写个脚本发几个请求或者你的项目已经全面基于 requests没有必要为了“新”而去迁移。requests 的稳定性经过无数项目的检验它的错误处理、文档、社区支持都非常成熟。aiohttp 是另一个主打异步的 HTTP 客户端同时也是服务端框架。它的优势在于生态成熟早期很多 Python 异步爬虫和 Web 客户端都是用 aiohttp。如果你依赖于 aiohttp 的一些特有功能比如 WebSocket 客户端、CookieJar 的细粒度控制、或者已经大量用 aiohttp 写好的代码那继续用 aiohttp 是合理的。但 aiohttp 的一个问题在于它的 API 设计更偏向低级。比如你要设置一个简单的 headers需要手动构造一个dict而 httpx 提供了更接近 requests 的风格上手更平滑。另外aiohttp 目前对 HTTP/2 支持有限需要结合 h2 自己处理而 httpx 原生就支持。还有一点httpx 的文档和代码风格明显更现代、更清晰。如果你想学新的项目或者你的团队比较注重开发者体验httpx 是个更舒服的选择。我个人的感受是httpx 在处理错误时的 traceback 比 aiohttp 更容易读调试起来节省时间。总的来说如果你的项目是非异步的、对 HTTP/2 没有硬性要求、并且你不想引入额外的依赖requests 是最稳妥的。如果项目中已经有异步 IO 的大量使用比如基于 asyncio 的爬虫、微服务客户端同时希望 API 风格接近 requestshttpx 几乎是最优解。aiohttp 则更适合那些需要深入控制 HTTP 细节、或者同时需要 HTTP 服务端功能的场景。每种工具都有它的生态位。了解它们在什么情况下更顺手比盲目追求“热门”要明智得多。