先把结论说清楚:限流一旦触发,最该保护的不是“这一轮跑完整”,而是已拿到的原始响应、请求参数和失败位置。只要这三样还在,你可以在限额恢复后只补缺口,不必整批重跑;如果原始响应没留,后面任何重试都等于从零开始。
同样是返回错误,处理方式完全不同。先看错误是来自目标站点的访问频率限制,还是来自你调用的测速工具自身配额,还是本地并发把连接池打满。三者的证据不一样:目标站点限流通常表现为固定时间窗内连续失败、响应头带重试提示;工具配额耗尽往往在调用入口就被拒绝,根本没发出请求;本地资源问题则伴随超时、连接重置,且换低并发后立刻缓解。
判断清楚之前不要盲目加代理或换 IP,那可能把“配额问题”误当成“封禁问题”,反而浪费更多额度。一个可执行动作是:先停掉所有并发,用单线程重放最近三条失败请求。如果单线程能成功,说明是并发或频率问题;如果单线程仍被拒,才考虑配额或封禁。
限流是概率事件,不能等它发生才想存什么。脚本里应该边跑边写,而不是全部跑完再统一输出。
落盘格式用追加写的 JSON 行或 CSV 都行,关键是每次请求后立即 flush。假设你计划跑 500 个目标,跑到第 180 个时被限流;如果前 180 条原始响应都在,你只需从第 181 条继续,前 180 条无需重放。这就是保留带来的直接收益,也是后续所有决策的前提。
如果脚本原本是一次性拉取全部目标,限流后最合理的动作是改写成“分片 + 断点”结构。每个分片记录自己的完成状态,重跑时先读游标,跳过已完成分片。这样即使限流反复出现,每次也只损失当前分片内未落盘的部分。
改写适用于目标数量大、限流窗口短且可预测的情况。它的代价是脚本复杂度上升,需要维护状态文件。如果目标只有几十个、限流窗口很长,改写可能不划算,直接等窗口结束重跑一次更省事。判断依据是:重跑一次的耗时是否明显大于维护断点结构的成本。这个比较只能用自己的实际目标数和窗口长度估算,不能套用别人的数字。
不是所有限流都值得抢救。出现以下信号时,继续重试的期望收益很低:
退出的正确姿势是:保存当前进度,记录最后一次失败的错误类型和时间,停止自动重试。然后人工核对一条失败请求,确认它到底是限流还是别的原因。这个动作的结果决定下一步——如果确认是限流,等窗口恢复后从游标继续;如果确认是权限或目标问题,先解决那个问题,否则续跑仍然会失败。
限流后你常看到请求量骤降甚至归零,但这不能单独证明你的处理是对的。请求量归零还可能是因为脚本卡死、游标写错导致跳过全部剩余目标、或重试逻辑把请求吞掉了。要区分这些原因,得同时看失败日志和游标文件:如果游标没前进且没有新失败记录,很可能是脚本异常而非限流生效。
同样,限流解除后第一轮补跑成功,也不能证明之前的保留策略一定完整。只有当你核对补跑结果与原始请求指纹能一一对应时,才能说数据没有缺口。缺了这步核对,看起来跑完了,实际可能混入了重复或错位的结果。