本质是避免被识别为攻击,需动态节流、复用连接、注入思考时间并排查本地开销。用ratelimit控频、aiohttp单session限连接池、Locust加between延迟、requests配连接池,并优先curl诊断DNS/TLS瓶颈。

requests 请求太猛,目标服务器直接 429 或超时怎么办
本质不是“爬得快”,而是“别让对方当攻击”。requests 默认发请求不带任何节流,连续调用就像敲门不停——门没开,先把门框震松了。真实场景里,429 Too Many Requests、503 Service Unavailable 或连接被重置(ConnectionResetError),基本就是节奏失控的信号。
最直接的解法是加等待,但不能写死 time.sleep(1):目标反爬可能按窗口计数(比如每 10 秒最多 5 次),也可能按 IP+UA 组合限流。所以得用可配置的速率控制器:
- 用
time.time()记上一次请求时间,动态算间隔,比固定 sleep 更贴合限流窗口 - 优先选
ratelimit这类轻量库(pip install ratelimit),它基于装饰器封装了令牌桶逻辑,比手写 while 循环判断更稳 - 千万别在多线程里共用一个计时器变量——每个线程得有自己的速率上下文,否则实际并发远超预期
asyncio + aiohttp 做并发压测时,如何不把本机端口耗尽
异步不是万能加速器。开 100 个 aiohttp.ClientSession 并发请求,若没设连接池上限,会瞬间占满本地可用端口(尤其 Windows 默认只有 ~65K 端口),触发 OSError: [Errno 24] Too many open files 或 DNS 解析失败。
关键在控制连接粒度:
立即学习“Python免费学习笔记(深入)”;
- 全局只用 1 个
aiohttp.ClientSession实例,复用 TCP 连接;不要每次请求都新建 session - 初始化 session 时显式限制连接池:
aiohttp.TCPConnector(limit=20, limit_per_host=5),limit_per_host比总 limit 更重要——防止单域名打爆 - 配合
asyncio.Semaphore(10)控制并发请求数,比光靠 connector 更易调试和观察
用 Locust 模拟真实用户行为时,为什么 QPS 总是虚高
Locust 默认按“每秒启动 N 个用户”推进,但若每个用户脚本里没加思考时间(think time),所有请求就堆在毫秒级发出,QPS 看着高,实则完全脱离人类操作节奏,也绕过了多数基于行为的反爬规则。
必须手动注入真实延迟:
- 在
task方法里调用self.wait_time = between(1, 5),让每个用户随机停 1–5 秒再发下个请求 - 避免用
constant(0.1)这类极短间隔——即使目标允许,也会暴露脚本特征(真人不可能每 100ms 刷新一次) - 如果压测目标有登录态,记得在
on_start里做登录并复用session,否则每个用户重复登录会放大压力,且触发账号风控
压测中发现响应变慢,是目标扛不住还是自己代码拖后腿
响应延迟上升时,第一反应常是“对方崩了”,但大概率是本地解析或 SSL 开销吃掉了时间。特别是批量请求 HTTPS 页面时,DNS 查询和 TLS 握手会成为瓶颈。
排查顺序很关键:
- 用
curl -w "@format.txt" -o /dev/null -s "https://example.com"单独测 DNS+TLS+首字节时间,对比 Python 脚本耗时,快速定位是否为语言层开销 - requests 默认每次请求都重做 DNS 解析,加
requests.adapters.HTTPAdapter(pool_connections=10, pool_maxsize=10)复用连接池,能省掉 80%+ 的握手时间 - 如果目标支持 HTTP/2(如现代 CDN),
aiohttp可自动启用,但requests不支持,硬切协议会带来额外复杂度,得权衡
真正难控的从来不是并发数,而是每个请求背后的时间分布——DNS 缓存失效、TLS 会话复用中断、甚至目标服务器临时 GC,都会让延迟毛刺变得不可预测。留出 20% 的余量,比强行卡满阈值更接近真实场景。


















