Crawlee 实战:将 CheerioCrawler 项目部署到 AWS Lambda 的完整指南
2026/9/12 16:24:08 网站建设 项目流程

Crawlee 实战:将 CheerioCrawler 项目部署到 AWS Lambda 的完整指南

【免费下载链接】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

在本地用npx crawlee create创建 Crawlee 项目并跑通爬虫很容易,但当你想把 CheerioCrawler 项目部署到 AWS Lambda 这个无服务器环境时,会遇到两个核心障碍:Lambda 的文件系统是只读的(无法落盘持久化存储),以及Lambda 实例会被复用(容器在首次执行后会存活一段时间以降低冷启动延迟)。本文基于 Crawlee 3.16 的官方部署文档,结合仓库源码,完整讲解如何改造代码、打包并成功部署到 AWS Lambda,最终通过handler函数返回爬取结果。

为什么在 Lambda 上运行 Crawlee 需要改代码

Crawlee 默认行为是:所有 crawler 实例共享同一套存储(Request Queue、Dataset、Key-Value Store 默认落在本地./storage目录下,对应配置项storageDir的默认值,见 configuration.ts)。这在本地单进程场景下很方便,但在 AWS Lambda 中却会带来隐患:

  • 文件系统只读:Lambda 的执行环境只有/tmp目录可写,项目根目录是只读的,默认的基于文件系统的存储后端会直接失败。
  • 实例复用导致"有状态":AWS 会在一次执行结束后让环境保持存活一段时间,后续请求会复用同一个容器。如果 crawler 和存储被共享,上一次运行遗留的数据和状态会泄漏到下一次调用,产生极难排查的 bug。

因此核心改造思路就两条:每次调用都创建独立的Configuration实例,并把persistStorage设为false,让 Crawlee 改用纯内存存储。

第一步:创建 Crawlee 项目

在本地终端执行:

npx crawlee create

按提示选择 Cheerio 模板完成项目初始化。此时项目的入口文件是src/main.js(Crawlee 模板默认结构),后续所有改造都在该文件内进行。

第二步:给爬虫传入独立的 Configuration 实例

在实例化CheerioCrawler时,把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);

关于这两个关键点,可以从源码层面进一步理解:

  • persistStorage是什么:它被定义在 configuration.ts,默认值为true,对应的环境变量是CRAWLEE_PERSIST_STORAGE。设为false即告诉 Crawlee 不要持久化存储。
  • false时底层发生什么:存储后端由服务定位器按配置惰性创建。在 service_locator.ts 中可以看到,getStorageBackend()会根据persistStorage决定使用FileSystemStorageBackend(持久化到磁盘)还是MemoryStorageBackend(纯内存)。Lambda 只读文件系统的场景下,persistStorage: false会让 Crawlee 自动选择内存后端,无需你再做任何存储适配。
  • 为什么必须传独立实例Configuration默认存在一个全局单例(Configuration.getGlobalConfiguration())。不传第二个参数时,crawler 会使用全局配置,也就意味着所有 crawler 共享同一存储后端;传入独立实例后,每个 Lambda 调用里的 crawler 都有自己隔离的存储空间,互不干扰。

第三步:把逻辑包装进 handler 函数

AWS Lambda 需要导出一个约定的入口函数,也就是 Lambda 将要执行的 "handler"。改造方式是把爬虫逻辑整体移入一个异步函数并导出:

// 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); };

重要提示:保持 Lambda 无状态(stateless)

务必在每次 Lambda 调用时都新建 crawler 实例。因为 AWS 会在首次执行后让环境存活一段时间以减少冷启动,后续调用会访问到已使用过的 crawler 实例,跨调用复用状态会引发难以调试的问题。核心原则一句话:每次调用都是全新的 crawler。

第四步:让 Lambda 返回爬取结果

爬虫运行结束后,通过crawler.getData()获取本次爬取的数据并封装成 Lambda 的标准响应结构返回。getData()对应 dataset.ts 中 Dataset 的读取 API,返回 Dataset 中的数据项。

最终src/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(), } };

这里event是 AWS 传入的第一个参数,包含触发事件的全部信息。你可以通过解析event对象(例如其中的查询参数或请求体)来进一步参数化爬虫运行。

第五步:打包并部署到 AWS Lambda

在项目目录下执行:

zip -r package.zip .

该命令会把整个项目(包含node_modules目录)压缩为一个 zip 包,然后在 AWS Lambda 控制台将package.zip上传为代码源。

node_modules 太大怎么办:使用 Lambda Layers

AWS 对直接上传有50MB 大小限制。Crawlee 项目通常远小于此,但依赖树庞大时很容易超限。更优雅的方案是使用Lambda Layers

  1. node_modules目录单独打包成一个 zip(压缩包内需包含名为node_modules的顶层文件夹);
  2. 由于该 zip 体积可能接近 50MB 限制,建议先上传到 AWS S3,再基于该对象创建 Lambda Layer;
  3. 在 Lambda 函数配置中挂载这个 Layer,这样多个 Lambda 可以共享依赖,函数本体(代码部分)保持尽可能精简。

值得对照的是,仓库中另一份部署文档 aws-browsers.md 展示了浏览器版(PlaywrightCrawler)的同类做法:浏览器二进制同样通过@sparticuz/chromium打包进 Layer,并在代码中用aws_chromium.executablePath()指定可执行文件路径,同时传入aws_chromium.args适配 Lambda 缺少 GPU 加速的硬件环境。这印证了 Layer 是 Lambda 部署大型依赖的标准姿势。

配置 Runtime Settings

上传代码后,在 Lambda 的Runtime Settings中设置handler指向入口函数:

  • /描述目录层级,用.表示命名导出;
  • 我们的函数名为handler,从src/main.js导出,因此 handler 名填写:
src/main.handler

内存、超时与临时存储配置

在 AWS Lambda 控制台的Configuration标签页中,可以配置 Lambda 的内存大小和临时存储(ephemeral storage)大小:

  • 内存大小:会显著影响 Lambda 的执行速度(内存越大分配的 CPU 计算能力越强)。CheerioCrawler 属于轻量级 HTTP 爬虫,但若并发或请求量大,建议预留充足内存。
  • 临时存储:如果爬虫有写/tmp的需求(例如下载文件、浏览器二进制解压),需要相应调大。

部署完成后,点击Test按钮发送一个测试事件即可验证。测试事件的具体内容目前不影响运行,后续可以根据需要解析event对象来参数化爬取目标。

常见问题与排错要点

问题原因与解决
存储写入报错 / 无法创建 storage 目录未设置persistStorage: false,Crawlee 尝试写只读文件系统;确认每个 crawler 都传入独立的Configuration
多次调用结果串数据在 Lambda 容器复用时复用了全局单例配置;务必在 handler 内每次新建 crawler 实例
上传 zip 超过 50MB改用 Lambda Layers 承载node_modules,代码包只保留业务源码
Lambda 直接超时在 Configuration 中调大超时与内存,先本地测量爬虫执行耗时再据此设置
handler 找不到检查 Runtime Settings 的 handler 是否与导出路径一致(src/main.handler),以及package.jsonmain字段指向src/main.js

小结

将 Crawlee 的 CheerioCrawler 项目部署到 AWS Lambda,本质上是围绕两个环境约束做适配:只读文件系统→ 通过persistStorage: false切换到内存存储后端;容器复用→ 每次调用创建独立Configuration与 crawler 实例,保持 Lambda 无状态。改造完成后,zip打包(必要时配合 Lambda Layers)上传代码、设置src/main.handler入口即可运行。同样的思路也适用于 GCP Cloud Functions 部署指南——那边同样要求persistStorage: false并导出 handler 函数,区别仅在于 GCP 按package.jsonmain字段定位入口、打包时排除node_modules由平台自行安装依赖。

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询