在 AWS Lambda 上部署 Crawlee CheerioCrawler:无状态爬虫的完整改造与部署指南
【免费下载链接】crawleeCrawlee—A web scraping and browser automation library for Node.js to build reliable crawlers. In JavaScript and TypeScript. Extract data for AI, LLMs, RAG, or GPTs. Download HTML, PDF, JPG, PNG, and other files from websites. Works with Puppeteer, Playwright, Cheerio, JSDOM, and raw HTTP. Both headful and headless mode. With proxy rotation.项目地址: https://gitcode.com/GitHub_Trending/cr/crawlee
本篇指南以 Crawlee 的 Cheerio 爬虫(CheerioCrawler)为主体,完整讲解如何把本地开发好的 Crawlee 项目改造成可以在 AWS Lambda 上稳定运行的 Serverless 爬虫。你将掌握:为每个 Lambda 调用注入独立Configuration实例以隔离存储、通过persistStorage: false切换到内存存储以适配 Lambda 只读文件系统、用handler包装爬虫并返回抓取结果,以及用 zip 包与 Lambda Layers 两种方式部署依赖的完整流程。
为什么要在 AWS Lambda 上运行 Crawlee
本地开发时,一条npx crawlee create命令就能生成一个可直接运行的 Crawlee 项目。但把项目搬上 AWS Lambda 之前,必须意识到两者的运行模型有本质差异:Lambda 是无状态、事件驱动的 Serverless 函数,它的文件系统在运行时是只读的,且一次调用结束后执行环境会被 AWS 保留一段时间(用于减少冷启动),随后可能被任意实例复用。这种环境对"默认假设本地磁盘可写、默认全局共享存储"的 Crawlee 并不友好,因此需要做几处针对性改造。
官方为这一主题提供了完整的部署文档,本文所基于的版本位于 version-3.13 部署文档,当前主版本文档见 docs/deployment/aws-cheerio.md。
第一步:改造代码,让爬虫适配 Lambda 运行模型
1. 为爬虫注入独立的 Configuration 实例
Crawlee 的默认行为是:所有爬虫实例共享同一套存储(Request Queue、Dataset、Key-Value Store 等)。这在本地单进程开发时很方便,但在 Lambda 中却会埋下隐患——多个调用复用同一个执行环境时,如果爬虫共享了存储状态,Lambda 就会变得"有状态",产生极难排查的交叉污染问题。
解决办法是为每个爬虫传入一个独立创建的Configuration实例:
// For more information, see https://crawlee.dev/ import { CheerioCrawler, Configuration, ProxyConfiguration } from 'crawlee'; import { router } from './routes.js'; const startUrls = ['https://crawlee.dev']; const crawler = new CheerioCrawler({ requestHandler: router, }, new Configuration({ persistStorage: false, })); await crawler.run(startUrls);从源码可以确认,configuration是BasicCrawler(CheerioCrawler的基类链上游)构造函数显式支持的服务选项之一,见 basic-crawler.ts 中configuration: z.instanceof(Configuration).optional()的声明。不传该参数时,爬虫会回退到全局配置(Configuration.getGlobalConfiguration()),这正是共享存储的来源。
2. persistStorage: false:切换到内存存储
创建Configuration实例时,必须显式设置persistStorage: false。该选项告诉 Crawlee 关闭持久化存储、改用内存存储,因为Lambda 的文件系统是只读的,任何试图写入磁盘的持久化操作都会失败。
配置项的默认值与环境变量映射可以在源码中查到,configuration.ts 中定义:
persistStorage: field(coerceBoolean.default(true), 'CRAWLEE_PERSIST_STORAGE'),即默认值为true(默认持久化),对应的环境变量为CRAWLEE_PERSIST_STORAGE。在 Lambda 场景下,除了在构造Configuration时传persistStorage: false,你也可以通过设置环境变量CRAWLEE_PERSIST_STORAGE=false达到同样效果。配置值的解析优先级为:构造函数参数 > 环境变量 > crawlee.json > schema 默认值(见 configuration.ts 的注释说明),因此在 Lambda 控制台里配置环境变量是最省事的方式之一。
从底层实现看,这个开关决定的是存储后端的选择。在 service_locator.ts 中,getStorageBackend()的语义是:persistStorage启用时创建FileSystemStorageBackend,否则创建MemoryStorageBackend:
/** * Get the storage backend. * Creates a default storage backend if none has been set — `FileSystemStorageBackend` when * `persistStorage` is enabled (the default), `MemoryStorageBackend` otherwise. */也就是说,persistStorage: false不仅避免了向只读文件系统写数据,还把 Request Queue、Dataset 等存储全部落到内存中,天然契合 Serverless 的单次执行生命周期。
3. 用 handler 包装爬虫逻辑
接下来,把全部逻辑包进一个导出的handler异步函数。这就是 AWS 后续要执行的"Lambda 本体":
// For more information, see https://crawlee.dev/ import { CheerioCrawler, Configuration } from 'crawlee'; import { router } from './routes.js'; const startUrls = ['https://crawlee.dev']; export const handler = async (event, context) => { const crawler = new CheerioCrawler({ requestHandler: router, }, new Configuration({ persistStorage: false, })); await crawler.run(startUrls); };4. 保持 Lambda 无状态(Stateless)
:::tip 重要提示
务必为每一次 Lambda 调用都新建一个爬虫实例。AWS 在第一次执行结束后会保留执行环境一段时间(以减少冷启动),后续调用会复用同一个已运行过的爬虫实例。如果复用带状态的实例,会引发难以调试的脏数据问题。
TLDR:Keep your Lambda stateless.(保持 Lambda 无状态。)
:::
这一条与第 1 步的独立Configuration是配套的:新的爬虫实例 + 独立的内存存储,保证每个调用从零开始,互不干扰。
5. 返回抓取结果
爬虫跑完后,我们通常还希望 Lambda 把抓取到的数据作为响应返回。crawler.getData()正是为此设计的——它会读取当前默认 Dataset 中的数据并返回。最终完整的main.js如下:
// For more information, see https://crawlee.dev/ import { CheerioCrawler, Configuration } from 'crawlee'; import { router } from './routes.js'; const startUrls = ['https://crawlee.dev']; export const handler = async (event, context) => { const crawler = new CheerioCrawler({ requestHandler: router, }, new Configuration({ persistStorage: false, })); await crawler.run(startUrls); return { statusCode: 200, body: await crawler.getData(), } };getData()是Dataset类的核心读取方法,定义在 dataset.ts,它返回DatasetContent<Data>(包含items、total、limit、offset等字段的分页结果)。注意:getData()返回的是对象,若需要作为 HTTP 响应体发送,建议自行做 JSON 序列化。
router是src/routes.js中通过createRouter(或Router)定义的路由处理器,负责实际的页面解析与数据提取,其写法与本地开发完全一致,可参考 cheerio_crawler 示例。
第二步:部署到 AWS Lambda
方式一:直接打包上传(小依赖项目)
在项目根目录执行:
zip -r package.zip .该命令会把项目(包含node_modules目录)整体打包成 zip 归档,随后在 AWS Lambda 控制台把这个 zip 作为代码源上传即可。
方式二:用 Lambda Layers 拆分依赖(大依赖项目)
:::note node_modules 太大?
AWS 对直接上传有 50MB 的大小限制。通常 Crawlee 项目不会接近这个限制,但当依赖树较大时很容易超限。
更推荐的做法是使用Lambda Layers安装项目依赖:Layer 可以被多个 Lambda 共享,同时把 Lambda 的"代码本体"保持得尽可能精简。
创建 Lambda Layer 的步骤:
- 将
node_modules文件夹单独打包成 zip(归档内应只包含一个名为node_modules的文件夹); - 用这个归档创建 Lambda Layer——如果包体较大,需要先上传到 S3,再基于 S3 对象创建 Layer;
- 创建完成后,在 Lambda 函数配置中挂载该 Layer。
:::
这种"代码 zip + 依赖 Layer"的组合正是官方推荐的生产级方案。Crawlee 基于 Cheerio 的爬虫依赖树较小(cheerio、got-scraping/HTTP 客户端等),通常直接用方式一即可,但方式二的可维护性与多函数复用价值更高。浏览器版爬虫(Playwright/Puppeteer)由于要携带浏览器二进制,几乎必须走 S3 + Layer 路线,详见 docs/deployment/aws-browsers.md。
配置 Runtime Settings 中的 handler
上传代码后,在 Lambda 的Runtime Settings中指定 handler 指向运行爬虫的主函数。handler 字符串的规则是:用/表示目录层级,用.表示具名导出。我们的 handler 函数名为handler,从src/main.js导出,因此填写:
src/main.handler测试与事件参数化
配置完成后点击Test按钮,即可向新 Lambda 发送一个测试事件。事件的真实内容目前并不重要;如果想让爬虫运行可参数化,可以进一步解析 AWS 作为第一个参数传入 handler 的event对象,例如把起始 URL 列表、抓取深度等作为事件字段传入,再在handler内部映射为crawler.run()的参数。
第三步:调优 Lambda 资源配置
在 AWS Lambda 控制台的Configuration标签页中,可以配置 Lambda 的内存大小与临时存储(ephemeral storage)大小。其中内存大小会显著影响 Lambda 的执行速度:内存越大,AWS 分配的 CPU 算力也越强(两者按比例联动),抓取任务往往因此更快完成。官方文档说明性能与成本随内存增长的对应关系,建议根据预算与目标时延权衡。
针对 Cheerio 这类纯 HTTP + HTML 解析的爬虫,内存需求通常远低于浏览器版;如果是 Playwright/Puppeteer 这类需要启动完整浏览器的爬虫,则必须把内存设置到1024MB 或更高,并相应调大 Lambda 超时时间(可先本地测量爬虫运行耗时再设置),详见 docs/deployment/aws-browsers.md 中的内存设置提示。
部署清单速查
| 事项 | 操作 | 关键依据 |
|---|---|---|
| 存储隔离 | 为每个爬虫传入独立new Configuration({...}) | basic-crawler.ts |
| 关闭持久化 | persistStorage: false或环境变量CRAWLEE_PERSIST_STORAGE=false | configuration.ts |
| 内存存储 | 由persistStorage自动选择MemoryStorageBackend | service_locator.ts |
| 无状态 | 每次调用新建 crawler,不在模块顶层复用 | 官方部署文档提示 |
| 返回数据 | await crawler.getData() | dataset.ts |
| 打包 | zip -r package.zip .(含 node_modules) | 官方部署文档 |
| 大依赖 | 用 Lambda Layers 装 node_modules,代码单独 zip | 官方部署文档 |
| handler | 填写src/main.handler | 官方部署文档 |
延伸阅读
- Browsers on AWS Lambda(Playwright/Puppeteer 版):需要携带浏览器二进制、设置
executablePath与 GPU 相关参数、调大内存的完整方案; - Cheerio on GCP(Google Cloud Run):同样的 Cheerio 爬虫迁移到 GCP 的对照方案;
- 部署到 Apify 平台:如果不想自建 Serverless 基础设施,可直接部署到 Crawlee 同源的 Apify 平台;
- CheerioCrawler 入门示例:
router与requestHandler的完整写法; - Configuration 全部选项:内存、日志、浏览器路径、存储目录等更多可配置项及对应环境变量。
【免费下载链接】crawleeCrawlee—A web scraping and browser automation library for Node.js to build reliable crawlers. In JavaScript and TypeScript. Extract data for AI, LLMs, RAG, or GPTs. Download HTML, PDF, JPG, PNG, and other files from websites. Works with Puppeteer, Playwright, Cheerio, JSDOM, and raw HTTP. Both headful and headless mode. With proxy rotation.项目地址: https://gitcode.com/GitHub_Trending/cr/crawlee
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考