先给结论:限流发生时,最危险的动作不是停下,而是让脚本在失败后继续重试并覆盖本地文件。保护已有结果的核心是“先隔离、再续跑”:把每次请求的原始响应按唯一键落盘,写入临时文件并原子替换,失败时保留旧文件;同时记录已成功处理的分片,让下一次运行从断点继续,而不是从头重来。这样即使工具端返回限流,你损失的是时间,而不是已经拿到的数据。
很多人在脚本里看到请求失败率上升,就判断“这次采集废了”。但如果你核对落盘文件,常会发现结果条数并没有明显减少。原因可能是脚本的重试逻辑在限流窗口外补回了请求,也可能是部分分片本来就没有新数据。请求量、抓取量或失败计数归零,都不能单独证明数据处理正确;它还可能意味着任务提前退出、缓存命中、接口返回空集合,或者分片键写错导致重复覆盖。要区分这些解释,必须看“每个分片的成功标记”和“文件修改时间”,而不是只看总请求数。
第一种解释是外部限流:长尾关键词工具对单位时间内的调用次数或并发连接数设了上限,超出后返回错误码或空响应。这种情况下,已有结果通常还在,只是新结果进不来。第二种解释是脚本自伤:重试没有退避,多个进程同时写同一个输出文件,或者失败后把不完整的响应当成完整结果写入。这种情况下,已有结果可能已经被污染或截断。两者都会表现为“请求失败”,但前者保护数据的关键是等待和续跑,后者保护数据的关键是加锁和校验。
假设你的脚本按关键词前缀分片,每片调用一次工具接口。把流程改成:请求成功后,先把原始响应写入 raw/分片键.json.tmp,校验 JSON 可解析后,再用原子替换改名为 raw/分片键.json;解析和汇总只读这个正式文件,不直接读接口返回值。限流发生时,未成功的分片没有正式文件,旧文件保持原样。下一次运行先扫描 raw/ 下已存在的正式文件,跳过这些分片,只请求缺失的分片。这个动作的结果是:你的已有结果不会被半截响应覆盖,续跑范围也可核对。下一步就可以根据缺失分片数量决定是降低并发、延长间隔,还是换时间段再跑,而不是盲目重跑全量。
假设你有 10 个分片,第 1 到 6 片已成功落盘,第 7 片触发限流。如果脚本失败后直接写正式文件,第 7 片可能留下空文件或错误内容,下一次运行时若按“文件存在即跳过”,就会永久丢掉这一片。如果采用临时文件加原子替换,第 7 片没有正式文件,下一次运行会重新请求它。两种做法的差别不在工具端,而在本地写入策略。数字只用于说明比较方法,不代表任何真实工具的额度或表现。
第一,限流窗口和分片粒度要匹配。如果分片太大,一个分片内包含很多次请求,断点只能到分片级,重跑成本仍然高;把分片拆细,续跑更精确,但文件数量增加。第二,工具端的返回结构可能变化,续跑前先用一个已知分片做校验请求,确认字段仍可解析,再继续批量处理。若校验失败,先停下修解析逻辑,不要带着错误结构跑完全部分片。具体工具的错误码含义、调用上限和重试建议,需要以该工具当前文档或实际返回为准,不要套用其他工具的额度。
保护已有结果的关键不是追求一次跑完,而是让每次失败都留下可核对的进度边界。先隔离原始响应,再原子替换正式文件,最后按缺失分片续跑,这三步能把限流从“可能毁掉结果”变成“只是推迟下一片”。