用 httpx 写 SerpBase 异步客户端:连接池与超时配置

什么时候值得换异步

一天几百次请求,requests 完全够用,没必要折腾。真正开始出现瓶颈是「一次要发几十上百个查询」的场景------批量关键词、多市场矩阵。同步串行跑,网络往返的时间全加起来,任务一跑就是十几分钟。

httpx 是 requests 的现代替代,API 几乎一样,但原生支持 async,还能复用连接池。

客户端只建一次

这是最容易犯的用法错误:把 AsyncClient 当 requests 用,每次请求都新建一个。

python 复制代码
import httpx

# 正确:一个进程共享一个客户端
client = httpx.AsyncClient(
    base_url="https://api.serpbase.dev",
    headers={"X-API-Key": KEY},
    limits=httpx.Limits(max_connections=20, max_keepalive_connections=10),
    timeout=httpx.Timeout(5.0, read=30.0),
)

async def search(q: str) -> dict:
    resp = await client.post(
        "/google/search",
        json={"q": q, "gl": "cn", "hl": "zh-cn"},
    )
    return resp.json()

客户端内部维护连接池,max_keepalive_connections 让 TCP 连接和 TLS 会话复用起来。程序结束前记得 await client.aclose(),或者用 async with。

超时拆成两段

httpx.Timeout(5.0, read=30.0) 的意思是:建连最多 5 秒,读取最多 30 秒。

这和 requests 的 timeout=(5, 30) 是同一个思路。分开设置有两个好处:建连慢通常是网络或 DNS 的问题,读取慢是服务端的问题,日志里区分开,定位方向完全不同。

并发控制在信号量里

搜索接口有 QPS 限制,客户端并发越高不等于吞吐越高。用信号量把并发压在限制内:

python 复制代码
import asyncio

sem = asyncio.Semaphore(10)

async def search_one(q: str) -> dict:
    async with sem:
        return await search(q)

async def batch(keywords: list[str]) -> list[dict]:
    return await asyncio.gather(*[search_one(q) for q in keywords])

10 路并发是我常用的起点。再往上加之前,先看服务端返回的响应码:出现限流类状态时,正确的反应是降并发、按 Retry-After 等待,而不是硬怼。

重试次数保持克制

重试做在客户端里,用 tenacity 之类都可以,但配置上偏保守更稳妥:

python 复制代码
from tenacity import retry, stop_after_attempt, wait_exponential

@retry(stop=stop_after_attempt(2), wait=wait_exponential(min=1, max=8))
async def search_with_retry(q: str) -> dict:
    return await search(q)

指数退避、次数少、配合信号量,重试是安全的;无限制重试 + 高并发,只会把限流问题放大。

我做错的第一版

第一版为了「更并发」,把信号量开到 100。结果限流响应暴增,重试跟着变多,整体吞吐反而比 10 路并发的版本低。

后来才明白:这类接口的吞吐上限在服务端配额那一侧,客户端的并发数只是在接近上限,超过它就是空转和重试。真正的优化,是把并发配到刚好填满配额。

现在采集脚本都用 httpx,20 路连接复用、10 路信号量、两秒退避。一次网页搜索 1 credit,新账号 500 免费 credits,批量实验成本很低。SerpBase 的字段结构见 SerpBase 文档。

相关推荐
c_hn_0071 小时前
量化数据12:国内期货L2历史数据:Tick、逐笔、五档盘口一次性打包下载
数据库·1024程序员节·#深度订单簿数据·#历史行情订单薄·#本地csv数据存储
倔强的石头1061 小时前
【Linux指南】进程通信系列(七):System V 信号量 —— 同步互斥的内核原语
linux·运维·服务器
GeW2 小时前
从"补短板"到"强优势"Linux与数据库认证成为产业跃迁人才支点
linux·数据库
数据库小学妹2 小时前
MySQL NULL陷阱:NOT IN、三值逻辑与NOT EXISTS
数据库·后端·mysql
SelectDB2 小时前
Doris 异步物化视图实战:与 Snowflake MV / Dynamic Tables 分层建模对照
大数据·数据库·云原生
枫昕柚2 小时前
MySQL 建库命令详解:从 CREATE DATABASE 到create table
数据库·mysql
张张123y3 小时前
后端第十六课:Multi-Agent 多智能体(让多个 Agent 分工协作)
大数据·数据库·人工智能·python·deepseek·dsh·agent 框架
HZZD_HZZD3 小时前
大口径摄像水表白皮书拆解:DN40加装改造与字轮识别实测
运维·物联网·腾讯云
todoitbo3 小时前
MongoDB迁移之后,Spring Data 和 Hibernate 各自接哪类请求
大数据·数据库·mongodb·spring·hibernate·金仓数据库