资讯动态

python中用Scrapy实现定时爬虫的实例讲解

发布时间:2026/10/9 16:06:36 来源:尧图企业网站定制
前言很多人第一次想「让爬虫每天自动跑一次」时会去 Scrapy 的文档里翻一个叫「定时」的配置项——然后发现没有。这不是文档缺失而是设计使然一次scrapy crawl的运行本质上是一个进程从启动、调度、抓取到收尾的完整生命周期它跑完就退出。所以「定时」这件事Scrapy 自己不提供需要在你外面加一层调度。本文就讲清楚为什么要这样以及两种可行的落地方式各有什么讲究。顺带纠正一个常见误解定时爬虫的难点不在「怎么触发」而在于「这次跑和上次跑的数据怎么不重不漏」。一个每天凌晨三点准时启动、但每次把上周的数据又灌一遍的爬虫还不如手动跑。所以本文除了讲触发也会讲增量与状态保存。老规矩先讲合规定时爬虫意味着持续、反复地访问目标站点它对对方造成的压力是按时间累积的。请务必先确认目标站点允许抓取、遵守 robots.txt、把DOWNLOAD_DELAY设到礼貌值、把每日抓取量控制住。不要用「定时」去实现高频轮询或对禁止抓取的路径反复试探。代码以 Python 3 为基准网上若出现 Python 2 写法print语句、xrange那是早已过时的东西Python 2.7 已于 2020 年 1 月 1 日停止维护。一、为什么 Scrapy 不内置定时要理解这一点得知道 Scrapy 的运行时依赖一个事件循环旧版是 Twisted reactor新版也支持 asyncio。关键约束是一个进程里事件循环通常只能启动并停止一次。所以「在同一个进程里for循环跑十次爬虫」这种做法在经典配置下是不成立的——第二次启动 reactor 会报错。这直接决定了两种定时思路要么让「调度」发生在进程之外每次触发都开一个全新进程要么在一个长生命周期的进程里用事件循环自身的能力来安排「隔一段时间再跑一次」。前者简单可靠后者省下反复启动的开销。下面分别讲。二、方案 A进程外调度推荐思路是写一个普通的抓取命令然后交给操作系统按时间表反复执行。Linux 上用 cronWindows 上用「任务计划程序」服务器上也可以用 systemd 的 timer。先准备好命令本身确认手工执行没问题# 在项目根目录含 scrapy.cfg 的那一层执行scrapy crawl my_spider然后把它写进 crontabcrontab -e打开编辑# 每天凌晨 3:00 抓一次注意用绝对路径cron 的环境变量很干净0 3 * * * cd /srv/myspider /usr/local/bin/scrapy crawl my_spider /var/log/myspider.log 21这里几个细节都是踩过坑才记得住的cd到项目目录是必须的否则scrapy crawl找不到scrapy.cfg用绝对路径而不是依赖 PATH把输出重定向到日志文件否则出错信息会随邮件发给 crontab 的主人或直接消失。Windows 上用schtasks创建计划任务效果等价schtasks /Create /TN MySpider /TR C:\proj\run_spider.bat /SC DAILY /ST 03:00方案 A 的优点是「一次一次干干净净」每次都是新进程不会有状态残留缺点是每次都要重新启动解释器和框架。对天级、小时级的抓取来说这点开销完全可以忽略这也是绝大多数场景的正确答案。三、方案 B进程内循环如果你的抓取频率很高比如每五分钟一次反复起进程的开销就值得省了。做法是让进程常驻用事件循环的定时器周期性触发一次抓取。思路是用CrawlerRunner自己管理爬虫用 Twisted 的LoopingCall安排周期最后手动启动 reactor。# 适用于 Python 3.8Scrapy 2.xfrom twisted.internet import task, reactorfrom scrapy.crawler import CrawlerRunnerfrom scrapy.utils.project import get_project_settingsfrom myproject.spiders.myspider import MySpider# get_project_settings() 会读取项目里的 settings.pyrunner CrawlerRunner(get_project_settings())def run_spider():# crawl() 返回一个 Deferred抓取结束后触发d runner.crawl(MySpider)def on_error(failure):print(本次抓取出错, failure.value)d.addErrback(on_error)return d# 每 3600 秒触发一次nowTrue 表示启动后立刻先跑一次loop task.LoopingCall(run_spider)loop.start(3600.0, nowTrue)# 启动事件循环程序会在这里一直挂着reactor.run()这段代码里最值得记住的是「返回Deferred」这个细节LoopingCall在回调返回Deferred时会等它完成后才安排下一次因此不会出现两次抓取重叠。如果你不返回它就有可能出现上一次还没跑完、下一次又启动的尴尬局面。另外CrawlerRunner与CrawlerProcess的分工要分清楚类是否替你启动事件循环适用场景CrawlerProcess会start()后阻塞到抓取结束「跑一次就退出」的独立脚本CrawlerRunner不会需要你自己管理 reactor已在使用 Twisted/asyncio 的应用、常驻进程较新版本的 Scrapy 还加入了协程风格的AsyncCrawlerProcess与AsyncCrawlerRunner并在把TWISTED_REACTOR_ENABLED设为False时支持在同一个进程里多次启动流程。这些较新 API 的引入版本与使用方式请以你所用版本的官方文档为准不同版本的差异不小。四、让定时爬虫真正可用的配置触发只是第一步。下面这几项配置决定了定时爬虫是「能用」还是「添乱」。1. 控制单次运行的边界。用CLOSESPIDER_TIMEOUT和CLOSESPIDER_PAGECOUNT给每次运行设上限避免某次异常把进程挂死或抓量失控# 适用于 Python 3.8Scrapy 2.xsettings.py 片段CLOSESPIDER_TIMEOUT 900 # 单次运行最多 900 秒CLOSESPIDER_PAGECOUNT 500 # 单次运行最多抓 500 个页面2. 保存抓取状态支持续爬。JOBDIR会把调度状态落到磁盘中断后可以接着上次跑# 适用于 Python 3.8Scrapy 2.xsettings.py 片段JOBDIR crawl_state/my_spider3. 去重与增量。定时爬虫最容易犯的错就是「每次都把全部数据重新落一遍」。要么在管道里按业务主键做去重要么利用 Scrapy 的请求指纹机制注意JOBDIR恢复的是调度状态它不等于业务层去重。具体去重策略要按你的数据来定。4. 日志与告警。定时任务失败时你是看不到终端的。务必把日志写到文件并在连续失败时给你发通知否则「爬虫挂了三个月没人发现」是常态。5. 限速始终生效。定时跑并不代表可以放松频率控制DOWNLOAD_DELAY、CONCURRENT_REQUESTS_PER_DOMAIN、AutoThrottle 这几项在常驻进程里一样要配好。常见坑点1. 想在同一个进程里重复process.start()❌ 用for循环反复CrawlerProcess().start()第二次直接抛异常。 ✅ 用进程外调度cron/任务计划或改用CrawlerRunner 事件循环定时器。2. 在循环里忘了让回调返回 Deferred❌LoopingCall的回调不返回Deferred上一次没跑完下一次又启动任务重叠。 ✅ 让回调return d让LoopingCall等它完成再安排下一次。3. cron 里没cd到项目目录❌ 任务里只写scrapy crawl x运行时报「找不到项目」。 ✅ 写成cd /srv/myspider scrapy crawl x或用绝对路径调用。4. cron 里依赖了 PATH 或环境变量❌ 手工能跑放进 cron 就失败因为环境变量不同。 ✅ 用解释器与可执行文件的绝对路径必要时在脚本里显式设置所需变量。5. 每次定时运行都覆盖同一份结果❌ 固定输出到同一个文件、每次清空历史数据全丢。 ✅ 输出到带日期的文件名或写入数据库按批标记。6. 没有单次运行的边界❌ 不设CLOSESPIDER_TIMEOUT某次卡住后进程一直挂着下次任务又启动。 ✅ 设置超时与页数上限并在调度层做「互斥锁」防止重复启动。7. 定时越跑越频繁❌ 把周期设成几分钟还放松了限速等于持续骚扰目标站。 ✅ 周期按内容更新频率定频率永远优先服从礼貌。8. 用 Python 2 的老示例❌ 照抄教程里的print语句、xrange。 ✅ 改成print(...)、range。总结需求做法天级 / 小时级定时cron、任务计划程序、systemd timer首选高频、常驻进程CrawlerRunnerLoopingCallreactor.run()跑一次就退出CrawlerProcess(get_project_settings()).start()单次运行设上限CLOSESPIDER_TIMEOUT/CLOSESPIDER_PAGECOUNT支持续爬JOBDIR防重叠回调返回Deferred调度层加互斥可观测日志落盘 失败告警底线限速、robots、控制单日抓量「Scrapy 实现定时爬虫」的正确答案八成是「Scrapy 负责抓操作系统负责定时」。把这两件事分开各自的复杂度都会下降。真正需要花心思的是数据层的增量与去重以及围绕它的日志与告警——毕竟一个安静地失败了的定时任务比一个明确报错的任务危险得多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑