Scrapy 橙皮书已围绕 Scrapy 2.19 更新,并继续采用实例驱动的方式讲解框架核心。对开发者来说,这类教程的价值不只是列出 API,而是把请求调度、页面解析、数据导出和限速配置串成一条完整链路。
下面不依赖在线站点,直接用本地 HTTP 服务搭建一个可重复运行的小项目。
Scrapy 项目真正解决了什么
一个最小爬虫似乎只需要“发送请求并解析 HTML”,但生产任务通常还包含这些问题:
- 哪些 URL 已经抓取,哪些请求正在排队;
- 请求失败后是否重试,超时如何处理;
- 如何限制并发和抓取频率;
- 解析出的字段怎样清洗、校验和持久化;
- 网站结构变化后,如何定位失效的选择器。
Scrapy 将这些职责拆分给 Engine、Scheduler、Downloader、Spider、Item Pipeline 等组件。Spider 更适合专注于两件事:生成后续请求,以及把响应转换成结构化数据。调度、下载、重试和导出则交给框架或扩展组件处理。
这种分工也是阅读实战型教程时应抓住的主线:不要只记 response.css() 的语法,而要理解一条 Item 是如何从 URL 一路流向输出文件的。
搭建一个可重复运行的本地爬虫
下面假设使用 Bash,并且本机已经安装 Python。安装时将 Scrapy 限定在 2.19 系列,避免未来升级导致示例行为变化:
mkdir scrapy-219-demo
cd scrapy-219-demo
python -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install 'Scrapy>=2.19,<2.20'
scrapy startproject orange .
mkdir -p site
Windows PowerShell 激活虚拟环境时,可将 source .venv/bin/activate 替换为:
.venv\Scripts\Activate.ps1
创建一个本地测试页面 site/index.html:
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>Scrapy 本地测试页</title>
</head>
<body>
<article class="quote">
<p class="text">框架负责流程,Spider 负责业务规则。</p>
<span class="author">Alice</span>
<a class="tag" href="/tags/python">Python</a>
<a class="tag" href="/tags/scrapy">Scrapy</a>
</article>
<article class="quote">
<p class="text">可观测的爬虫比能运行的爬虫更容易维护。</p>
<span class="author">Bob</span>
<a class="tag" href="/tags/engineering">Engineering</a>
</article>
</body>
</html>
在 orange/spiders/quotes.py 中编写 Spider:
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
allowed_domains = ["127.0.0.1"]
start_urls = ["http://127.0.0.1:8000/index.html"]
def parse(self, response):
for quote in response.css("article.quote"):
yield {
"text": quote.css("p.text::text").get(default="").strip(),
"author": quote.css("span.author::text").get(default="").strip(),
"tags": quote.css("a.tag::text").getall(),
"source_url": response.url,
}
打开第一个终端,启动本地 HTTP 服务:
cd scrapy-219-demo
source .venv/bin/activate
python -m http.server 8000 --directory site
再打开第二个终端运行爬虫:
cd scrapy-219-demo
source .venv/bin/activate
scrapy crawl quotes -O output.jsonl
cat output.jsonl
-O 会覆盖旧文件,适合反复调试;如果需要追加输出,可以使用小写的 -o。JSON Lines 每行保存一个对象,便于流式处理和逐条排查错误。
预期输出类似:
{"text":"框架负责流程,Spider 负责业务规则。","author":"Alice","tags":["Python","Scrapy"],"source_url":"http://127.0.0.1:8000/index.html"}
{"text":"可观测的爬虫比能运行的爬虫更容易维护。","author":"Bob","tags":["Engineering"],"source_url":"http://127.0.0.1:8000/index.html"}
从示例走向长期运行任务
本地页面没有延迟、封禁和结构漂移,真实网站则完全不同。可以在 orange/settings.py 中加入一组保守配置,再根据目标服务的承载能力调整:
ROBOTSTXT_OBEY = True
CONCURRENT_REQUESTS_PER_DOMAIN = 2
DOWNLOAD_DELAY = 1.0
DOWNLOAD_TIMEOUT = 20
RETRY_TIMES = 2
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 1.0
AUTOTHROTTLE_MAX_DELAY = 10.0
AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
FEED_EXPORT_ENCODING = "utf-8"
LOG_LEVEL = "INFO"
这些配置不是“抓得越慢越好”,而是在吞吐量、稳定性和对目标服务的影响之间取得平衡。尤其需要注意:
- 遵守 robots.txt 和站点条款。 技术上能够访问,不代表业务上或法律上允许采集。
- 为选择器准备测试。 页面把
article.quote改名后,爬虫可能不会报错,只会静默地产出零条数据。 - 对关键字段做校验。 作者、价格、时间等字段缺失时,应明确丢弃、补默认值或记录异常。
- 固定依赖版本。 完成验证后,可用
python -m pip freeze > requirements.txt保存环境。 - 观察统计信息。 关注响应状态码、重试次数、抓取条数和日志错误,而不只是输出文件是否生成。
例如,可以先用 Scrapy Shell 单独验证选择器,不必每次启动完整任务:
scrapy shell http://127.0.0.1:8000/index.html
进入交互环境后执行:
response.css("article.quote").getall()
response.css("article.quote span.author::text").getall()
这比在 Spider 中反复修改、运行和查看文件更快,也能清楚区分“请求没有成功”和“选择器没有匹配”两类问题。
文档源码也值得纳入开发流程
从目录结构看,教程使用 Sphinx 管理文档源文件,并将配置放在 doc/conf.py。如果下载了对应项目,可以这样尝试在本地构建文档;具体依赖文件名应以仓库实际内容为准:
python -m venv .docs-venv
source .docs-venv/bin/activate
python -m pip install sphinx
sphinx-build -b html doc doc/_build/html
python -m http.server 8080 --directory doc/_build/html
本地构建的优势是可以全文搜索、修改示例,并让文档版本与项目代码保持一致。若项目额外使用主题或 Sphinx 插件,还需要安装其文档依赖。
采用 Scrapy 2.19 前的检查清单
Scrapy 适合多页面抓取、请求调度、并发控制和结构化数据导出。如果任务只是请求一个固定 API,requests 或 httpx 可能更轻;如果页面必须执行大量 JavaScript,则需要评估浏览器自动化方案,或将其与 Scrapy 的调度能力组合使用。
开始实际项目之前,建议确认:
- Python 与 Scrapy 2.19 的运行环境已经独立并锁定;
- 已获得必要的抓取许可,并设置合理频率;
- Spider 只承担请求生成和页面解析职责;
- 关键选择器与字段有测试或告警;
- 输出采用可恢复、可追踪的格式;
- 已记录失败请求、重试和状态码分布。
先让一个本地、确定性的例子完整跑通,再逐步接入真实网站、Pipeline 和持久化系统,通常比一开始就堆叠代理池、分布式调度和浏览器渲染更稳妥。