跳到主内容

Scrapy 开源爬虫项目怎么跑通?结构和中间件,我拿 4 组实测数据试了试

上个月同事拿着一篇几年前的中文教程来问我,说 items.py 里那行 class DemoItem(scrapy.Item) 死活用不起来。我让他把版本打出来一看——他装的是 Scrapy 2.19.0,那套 scrapy.Field() 的写法早被换成 @dataclass 了。我把整个流程在本机重跑一遍,发现真正劝退人的不是框架本身,而是「教程和实际装上的版本对不上」。

起因:跑不通的往往不是代码

Scrapy 是纯 Python 的开源爬虫框架,仓库在 github.com/scrapy/scrapy,许可证 BSD-3-Clause,现在由 Zyte 维护。官方要求 Python 3.10+(我这台是 3.13.14),安装一行 pip install scrapy,建议放虚拟环境里别装到系统 Python 上。实际会带下来 lxml、parsel、w3lib、twisted、cryptography 一整套,七分钟装完没踩编译问题——不过官方提醒了,Windows 上某些依赖可能需要 Microsoft Visual C++。

先接受一个前提会比较省事:框架的默认行为每年都在改,而教程不会跟着更新。

过程:从 startproject 到出第一条数据

scrapy startproject demo 生成的结构是这样:

demo/
├── scrapy.cfg          # default = demo.settings
└── demo/
    ├── items.py        # 数据容器
    ├── middlewares.py  # 蜘蛛 + 下载中间件
    ├── pipelines.py    # 落地处理
    ├── settings.py     # 全部开关
    └── spiders/

骨架和老教程一样,内容对不上。items.py 现在是 @dataclass 标注的普通类,没有 scrapy.Item 也没有 scrapy.Field();pipelines.py 的签名也从 process_item(self, item, spider) 变成了 process_item(self, item)。照旧写法加回 spider 参数反而会报错。

Spider 本体倒是没怎么变。我用官方教学站 quotes.toscrape.com 做靶子——这是 Scrapy 教程专门拿来练习的站点,用它练手不会打扰任何真实业务。核心就是 parse() 里用 CSS 选择器取值,再 response.follow(nxt, callback=self.parse) 翻页。实测 11 个页面抓到 100 条,-o result.json 直接出文件。

踩坑:模板替你做的决定,你未必知道

最值得说的一处。新模板 settings.py 里这两行是没被注释掉的:

CONCURRENT_REQUESTS_PER_DOMAIN = 1
DOWNLOAD_DELAY = 1

而大家熟知的那行 #CONCURRENT_REQUESTS = 16 反而是注释状态。也就是说你刚建好项目、什么都没配的时候,它已经在给自己限速了。我拿同一个 Spider 跑了三组,都是 11 页 100 条:

DOWNLOAD_DELAY=0, 并发 8   →  8.29 秒
DOWNLOAD_DELAY=1(默认)     →  11.49 秒
DOWNLOAD_DELAY=3           →  35.17 秒

整理 Docker Compose 那篇时我留过一句「先把报错分类,再逐类找根因」,移到爬虫上一样成立:慢和报错得分开看。Scrapy 的 stats 写得很清楚——我这 11 个请求里有一个 404,是请求目标站 robots.txt 拿不到,站点没有 robots.txt 时它默认放行,后面 10 页照常抓完。

解法:中间件怎么配才会生效

Downloader Middleware 管「请求发出前」和「响应回来后」,Spider Middleware 管「交给 Spider 前」和「产出后」。很多人写在 middlewares.py 里却发现没被调用,原因只有一个——写了不等于注册了。实测:

# 没注册,同样两页
$ scrapy crawl quotes -s CLOSESPIDER_PAGECOUNT=2 | grep -c "\[MW\]"
0

# 注册后
$ scrapy crawl quotes -s CLOSESPIDER_PAGECOUNT=2 \
    -s DOWNLOADER_MIDDLEWARES='{"demo.middlewares.UaLogMiddleware": 543}'
[MW] process_request: https://quotes.toscrape.com/robots.txt
[MW] process_response status=200 bytes=11064

中间件本体只是个普通类,靠 DOWNLOADER_MIDDLEWARES 字典注册,值 543 是优先级(越小越靠近引擎,内置的占了 100 到 900 之间的常用位)。那段日志还藏着个容易忽略的点:robots.txt 的请求也会经过你的下载中间件,在 process_request 里做计数时要算进去,否则统计对不上。

还有个从稳定性出发的做法。我们给内置浏览器加过每 30 秒执行一次无副作用 JS 的健康检查协程,为的是没人盯着时也能自己发现问题。Scrapy 这对口的是 AUTOTHROTTLE,模板里默认注释着,把 AUTOTHROTTLE_ENABLED = True 打开,它按响应延迟自动调节奏,比自己拍一个 DOWNLOAD_DELAY 稳。

验证:跑完之后确认的几件事

一是版本要会看。scrapy version -v 会把 Python、lxml、Twisted 的具体版本一并列出来,排查时直接贴这份输出比描述半天有用。二是项目根多了个 ADDONS = {},新版引入的插件机制,老教程完全没提——它让你用一条配置挂上一组预设 settings,比零散开关堆在文件里干净。

三是节奏。下载量大时要把地址分批来、一批几十张、中间歇一会儿,搬到 Scrapy 就是别急着把 DOWNLOAD_DELAY 调成 0。那两行默认限速看着慢,其实是官方替你守住的边界——调之前先确认目标站的 robots.txt 允许什么、自己要不要承受那个请求密度。

常见问题

照老教程写 items.py 报错怎么办?

新版模板用标准库的 @dataclass,不再是 scrapy.Item 加 scrapy.Field()。先 scrapy version 确认版本,再回 docs.scrapy.org 查对应版本写法,别混着抄。

一上来就很慢,是机器不行吗?

先查 CONCURRENT_REQUESTS_PER_DOMAIN 和 DOWNLOAD_DELAY。实测同样 11 个页面,延迟 1 秒跑 11.49 秒,设 3 秒到 35.17 秒。绝大多数「慢」来自这两行,跟硬件关系不大。

中间件写了为什么没反应?

写类不算,要在 settings 里用 DOWNLOADER_MIDDLEWARES(或 SPIDER_MIDDLEWARES)注册,值是优先级数字。上面那组对照,没注册时调用次数是 0。

目标站没有 robots.txt 会失败吗?

不会,本次目标站返回 404,后面 10 页照常抓完。但这不代表可以不管规则——没有 robots.txt 不等于允许随意抓取,公开页面采集也要遵守目标平台的使用条款与相关法律法规。

Pipeline 要不要加 spider 参数?

看版本。2.19.0 模板生成的是 process_item(self, item)。稳妥做法是打开自己项目里的 pipelines.py 看模板怎么写,别照抄教程。

相关阅读

分享到:

本文链接:https://www.biyeyuanma.cn/post/314.html

服务热线

加我微信

加我微信