7. Crawlee
这是什么:Crawlee 是商业化爬虫平台 Apify 团队开源的爬虫框架(Node.js 版久经沙场,Python 版已稳定)。它的核心是一套异步爬虫基础设施:请求队列、去重、自动限速、失败重试、代理轮换、数据集存储全部内置——而且从 HTTP 爬虫升级到浏览器渲染爬虫只换一个类名。
为什么学它:前两个框架解决「爬得快、爬得多」,Crawlee 解决「爬得现代」:全异步架构天然高并发;HTTP 爬不动 JS 页面时,把 BeautifulSoupCrawler 换成 PlaywrightCrawler,解析代码一行不改。需要混合采集(一部分接口、一部分渲染页)的项目用它最顺。
如何用:装包 → 写第一个异步爬虫 → 理解请求队列与数据集 → 见识一键切换浏览器渲染。
装包
pip install "crawlee[beautifulsoup]"
第一个异步爬虫
Python 版 Crawlee 基于 asyncio(异步编程,函数用 async def、调用用 await)。存成 crawlee_demo.py 运行:
import asyncio
from crawlee.beautifulsoup_crawler import BeautifulSoupCrawler, BeautifulSoupCrawlingContext
async def main():
crawler = BeautifulSoupCrawler(max_requests_per_crawl=10) # 最多抓 10 个请求
@crawler.router.default_handler # 每个页面进来都走这里
async def request_handler(context: BeautifulSoupCrawlingContext):
context.log.info(f"正在抓取 {context.request.url}")
soup = await context.parse() # 解析成 BeautifulSoup 对象
for quote in soup.select("div.quote"):
await context.push_data({ # 数据存进数据集
"text": quote.select_one("span.text").get_text(strip=True),
"author": quote.select_one("small.author").get_text(strip=True),
})
await context.enqueue_links() # 页面上的链接自动入队
await crawler.run(["https://quotes.toscrape.com/"])
asyncio.run(main())
你会看到:日志逐条打出 INFO 正在抓取 …,程序结束后项目目录多出一个 storage/ 文件夹——数据在里面(下一节讲)。
三个关键调用记牢:
| 调用 | 作用 | 对应手写爬虫的 |
|---|---|---|
context.parse() |
拿到 BeautifulSoup 对象 | 第 3 章的解析 |
context.push_data({...}) |
数据写入内置数据集 | 第 4 章的存储 |
context.enqueue_links() |
页面所有链接自动进请求队列 | 第 4 章的队列 + 去重 + urljoin |
你没写的东西:队列管理、URL 去重、相对地址拼接、限速、重试——Crawlee 的请求队列(默认本地文件存储)全包了。
数据去了哪:storage 目录
运行后在项目目录找到 storage/datasets/default/,里面每个结果一个文件,像这样:
{ "text": "\u201cThe world as we have created it\u2026\u201d", "author": "Albert Einstein" }
想一次拿全?再写几行把数据集导出:
dataset = await context.get_dataset() # 在 handler 里拿
print(await dataset.get_data()) # {"items": [...], ...}
也可以用 Crawlee 自带 CLI:pip install "crawlee[cli]" 后运行 crawlee run ...,或直接读 storage/ 里的 JSON。注意:storage/ 会缓存上次的请求队列和数据,改了代码想从头跑,先删掉整个 storage 目录。
换一个类:从 HTTP 爬虫到浏览器渲染
遇到 JS 渲染的页面(第 1 章的动态形态),HTTP 请求拿到的是空壳——换 PlaywrightCrawler,用真浏览器跑完 JS 再解析:
pip install "crawlee[playwright]"
playwright install chromium # 下载浏览器内核,一次性
from crawlee.playwright_crawler import PlaywrightCrawler, PlaywrightCrawlingContext
crawler = PlaywrightCrawler(max_requests_per_crawl=10) # 只换这一行
# handler 里同样 context.parse() 拿 BeautifulSoup、push_data、enqueue_links
解析代码完全不变——这就是 Crawlee 的设计哲学:HTTP 和浏览器只是「取页面的方式」,业务逻辑只有一份。(对 JS 渲染想深入,是进阶话题,本教程点到为止。)
三大框架怎么选
| 维度 | Scrapy(第 5 章) | feapder(第 6 章) | Crawlee(本章) |
|---|---|---|---|
| 架构 | twisted 异步 | 多线程 | asyncio 异步 |
| 分布式 | 加装 scrapy-redis 拼装 | 内置(上 Redis 即用) | 平台化(配 Apify/代理池) |
| JS 渲染 | 加装 scrapy-playwright | 需自行集成 | 换类名即可 |
| 数据导出 | 命令行一键导出 | Item 管道入库 | push_data 数据集 |
| 上手难度 | 中(项目结构多) | 低(像普通脚本) | 低(但需要懂 asyncio) |
| 适合场景 | 大规模站群采集 | 国内业务型、分布式 | 混合采集、要渲染的现代站点 |
选型没有标准答案:规模优先选 Scrapy/feapder,现代站点和渲染优先选 Crawlee——三个都写过一遍,你就有了「看菜下单」的能力,这正是本栈的目的。
常见翻车现场
| 症状 | 原因 | 修复 |
|---|---|---|
ImportError: cannot import ... |
Crawlee 版本迭代快,import 路径变过 | 以官方文档为准,重看该版本 API |
| 程序没反应直接退出 | 忘了 asyncio.run(main()) |
async 的 main 必须由它启动 |
| 抓的是旧数据/没抓新页面 | storage/ 缓存了上次队列 |
删掉 storage 目录重跑 |
| 抓太多停不下来 | 链接一直有新链接(日历类站点) | max_requests_per_crawl 限总量 |
| Playwright 报浏览器未装 | 只装了包没装内核 | playwright install chromium |
动手改
- 把
max_requests_per_crawl从 10 改成 20,观察它爬到第 2 页后自动停住 - 给 push_data 增加
tags字段(提示:quote.select("a.tag")循环取文本) - 用
enqueue_links(include=[r"/page/"])只入队翻页链接,对比抓取范围变化