爬虫跑着跑着就断?一套 Python 爬虫稳定性排查与加固思路
【摘要】 长时间运行的爬虫容易卡死或崩溃。本文把中断原因分为网络异常、被限制、解析报错、进程退出四类,给出超时重试、控频、异常隔离、断点续跑和 systemd 守护的实用做法。
前言
写过爬虫的大概都遇到过这种情况:脚本刚跑起来好好的,过个把小时就卡住不动,或者直接抛异常退出,第二天一看只抓了三分之一。这篇把我自己踩过的坑整理一下,从"为什么会断"到"怎么让它断了也能接着跑",给一套能直接用的思路。
一、先搞清楚是哪种"断"
断,大致分四类,排查方向完全不同:
- 网络层异常:
ConnectionResetError、ReadTimeout、DNS 解析失败。多半是没设超时,或者对方偶发抖动。 - 被目标站限制:开始返回 403、429,或者返回 200 但内容是验证页。这是频率太高或者请求特征太明显。
- 解析报错:页面结构变了、某条数据缺字段,
None上取属性直接崩掉整个进程。 - 进程本身挂了:内存涨爆被 OOM kill、SSH 断开把前台进程带走、机器重启。
建议先把日志打全:每个请求记录 URL、状态码、耗时、异常类型。跑一晚上,看日志就知道是哪一类占大头。
二、网络层:超时 + 重试 + 退避
最常见的错误是 requests.get(url) 不带 timeout,对方不回包就会一直挂着,看起来就是"卡死"。
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = requests.Session()
retry = Retry(
total=5,
backoff_factor=1, # 1s, 2s, 4s, 8s...
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["GET"],
)
session.mount("http://", HTTPAdapter(max_retries=retry))
session.mount("https://", HTTPAdapter(max_retries=retry))
resp = session.get(url, timeout=(5, 20)) # 连接 5s,读取 20s
要点:
- 一定要设
timeout,连接和读取分开设。 - 用
Session复用连接,比每次新建快,也更不容易被识别。 - 退避重试,别失败了立刻狂重试,那样只会更快被封。
三、被限制:控频比换 IP 更重要
很多人第一反应是上代理池,其实先把频率降下来,大部分问题就没了:
- 每个请求之间加随机间隔,比如
time.sleep(random.uniform(1, 3))。 - 并发数别开太大,单站点 2~4 个并发一般够用。
- 带上正常的
User-Agent、Referer,别用库默认的。 - 遇到 429 时读一下
Retry-After头,按它说的等。
另外记得遵守目标站的 robots 协议和使用条款,别把人家服务器打挂。
四、解析层:单条失败不能拖垮全局
for item in items:
try:
data = parse(item)
save(data)
except Exception as e:
logger.warning("parse failed: %s %s", item_url, e)
failed.append(item_url) # 记下来,后面单独补
continue
解析函数里多用 .get()、判空,别默认字段一定存在。失败的 URL 单独存一份,跑完再补抓。
五、断点续跑:这才是根治
不管做得多稳,长任务总有断的时候,关键是断了能接着跑,而不是从头再来:
- 把待抓 URL 放进队列(简单场景用 SQLite 一张表就行),字段包括
url、status(pending/done/failed)、retry_count。 - 每抓完一条就更新状态,程序启动时只取
status='pending'的。 - 已抓数据按主键去重写入,重复跑也不会产生脏数据。
cur.execute("SELECT url FROM tasks WHERE status='pending' LIMIT 100")
这样哪怕进程被杀,重启后最多重复一两条。
六、进程守护
在云服务器上跑长任务,别直接前台 python spider.py:
- 临时跑用
nohup python spider.py > spider.log 2>&1 &或者tmux。 - 长期跑写成 systemd 服务,配
Restart=on-failure,挂了自动拉起:
[Service]
ExecStart=/usr/bin/python3 /opt/spider/spider.py
Restart=on-failure
RestartSec=10
- 内存持续上涨的,检查是不是把结果全堆在列表里没落盘,或者 Session/浏览器实例没释放。用 Selenium、Playwright 的,定期重启浏览器实例。
总结
爬虫稳定性说到底就三件事:请求要有超时和重试,频率要克制,任务要能断点续跑。前两条减少断的次数,第三条让断了也不心疼。把这几块搭好,一个爬虫挂在云服务器上跑几天基本没问题。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)