重跑总从头开始是因为进度仅存于内存变量而未持久化;必须在数据保存成功后立即写入文件(如last_id.txt),并flush确保落盘,避免崩溃丢失。

用 requests + 文件记录状态时,为什么重跑总从头开始?
因为你没把「当前爬到哪一页/哪个ID」真正持久化,只存在变量里,程序一崩就丢。断点续传不是靠 try-except 捕获异常,而是靠每次成功处理一个单元后,立刻写入最新进度。
- 别用内存变量(比如
current_page = 1)存进度,改用文件或轻量数据库 - 写进度必须在「确认该条数据已保存成功」之后,不能在发请求前或响应解析前
- 推荐用单行纯文本文件(如
last_id.txt),读写简单、无依赖、不怕并发冲突(单任务场景下) - 如果用 JSON 或 CSV 记录多字段状态,注意加
flush()和异常保护,否则可能写一半崩溃导致状态损坏
scrapy 中怎么让爬虫知道上次停在哪?
Scrapy 本身不内置断点续传,得靠 JOBDIR 配置 + 自定义中间件配合实现「请求级」恢复,但实际更常用的是「业务级」控制:自己管起始参数。
- 在
start_requests()里读取last_id.txt,构造起始 URL 或参数,例如https://api.example.com/items?offset=2450 - 禁用
JOBDIR的自动去重逻辑(它只记 request fingerprint,不感知业务语义),否则跳过你本想重试的请求 - 避免在
parse里直接修改全局变量来更新进度——Scrapy 是异步的,顺序不可靠;应在pipeline的process_item成功后,再调用同步写文件函数 - 示例:写入成功后执行
with open("last_id.txt", "w") as f: f.write(str(item["id"])),并确保f.flush()
遇到网络超时或 503,重试几次后该继续还是停?
断点续传不等于无限重试。真正的续传前提是「已知失败位置可安全跳过」,而不是卡在某次请求反复死循环。
- 对单个请求设置合理重试次数(如
max_retries=3),超过就记录错误 ID 到failed_ids.log,然后return跳过,继续下一个 - 不要在
except里 sleep 后直接continue或递归重试——这会阻塞整个 pipeline,且无法被外部中断 - 如果目标接口有反爬限流,建议在重试间歇加随机 delay(如
time.sleep(random.uniform(1, 3))),避免触发封 IP - 所有重试逻辑应封装进工具函数,统一处理状态更新和日志,别散落在各个
parse方法里
JSON/CSV 导出时如何避免重复写入或覆盖?
边爬边写文件,最容易出现「重复写同一行」或「最后几条丢失」,根源是打开模式和缓冲策略不对。
立即学习“Python免费学习笔记(深入)”;
- 写 CSV 用
open(..., "a", newline="")追加模式,不是"w";写 JSON 建议每行一个对象(JSON Lines),也用"a" - 每次
write()后立即flush(),尤其在 Linux 下,否则进程崩溃时缓冲区内容不会落盘 - 不要一次性构建大列表再 dump——内存爆了不说,崩溃即全丢;改成逐条
json.dump(item, f); f.write("\n"); f.flush() - 如果要最终合并成单个 JSON 数组,那是后期处理的事,爬虫阶段只保证每条独立可恢复
最常被忽略的一点:状态文件和数据文件必须在同一个存储介质上,且写操作要原子化。比如 NFS 挂载目录下 open().write() 可能不触发真实落盘,导致你以为存了,其实没存。本地 SSD + os.fsync() 才算真正保险。


















