先给结论:限流发生后,第一动作不是换密钥或加代理继续打,而是立刻停止写入、把已取得的结果按“可复现的最小单元”落盘,并记录最后一次成功请求的游标或时间边界。这样做的原因是,限流只说明当前通道暂时不可用,不说明已抓到的数据失效;但如果继续重试并覆盖同一批输出,反而会把完整结果冲成半截数据。下面用一个假设情境把决策过程串起来。
假设你用脚本调用某款SEO工具的接口,按分页拉取一批关键词的排名与索引数据,每页返回若干条,脚本把结果追加写入同一个JSON文件。跑到第37页时接口开始返回限流响应,脚本按原有逻辑重试三次后失败退出。此时文件里已有36页数据,但脚本没有记录“已完成到第几页”,重跑时会从第1页重新覆盖写入。这就是典型的“结果被自己的重试毁掉”,而不是被限流毁掉。
要避免这种损失,需要把结果分成两层保存:原始响应层和汇总层。原始响应层按请求参数命名单独存放,汇总层只在原始层完整时重建。限流发生时,原始层不动,汇总层保持上一次完整状态。
不同限流原因的应对方式不同,可以先看响应里是否带有可区分的信息:
这三类的证据可以从响应头、错误码和重试曲线里找。如果等待后立即恢复,偏速率型;如果等待很久仍失败且时间点接近某个整点,偏配额型;如果降低并发就好转,偏并发型。判断清楚之前不要盲目增加重试次数,否则会把速率型误判成配额型,白白浪费等待时间。
无论哪类限流,落盘策略是通用的。建议按以下顺序执行:
page-37.json。这套动作的结果是:限流只影响尚未获取的部分,已获取部分始终可重建。下一步的决策依据也清楚了——如果进度文件显示已完成大部分,可以只补缺失页;如果完成比例很低,再考虑调整请求节奏或更换调用方式。
在重新发起请求前,先确认两件事,否则可能再次触发限流:
如果这两个条件都不满足就恢复调用,很可能在很短时间内再次被限流,而这次可能连进度文件都来不及更新。因此恢复动作本身也要小步进行:先请求一页,确认成功并落盘后,再决定是否继续下一页。
如果脚本调用的接口本身不返回分页游标,或者每次请求结果都依赖实时状态、无法按页重建,那么“按页落盘再合并”的策略就不成立。此时更稳妥的做法是缩短单次运行的范围,把一次大抓取拆成多次小抓取,每次只处理一个明确子集,并在每次运行结束后立即导出结果。判断依据是:你的请求是否可以被切分成互不依赖的单元。能切分,就按页保护;不能切分,就按批次保护。
限流本身不是数据丢失的原因,覆盖写入和缺少进度记录才是。把这两点处理好,限流发生后你损失的只是时间,不是已有结果。