Crawlee 浏览器爬虫部署到 GCP Cloud Run 实战指南
【免费下载链接】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 官方文档,完整讲解如何在 Google Cloud Platform(GCP)的Cloud Run上运行带真实浏览器的 Crawlee 爬虫(如
PlaywrightCrawler)。你将掌握 Cloud Run 与 Cloud Functions/AWS Lambda 的差异、无状态 HTTP 包装模式、persistStorage配置原理,以及从npx crawlee create初始化到gcloud run deploy一键部署的完整流程。
为什么 GCP 上的浏览器爬虫需要 Cloud Run
在 GCP 上运行完整尺寸的浏览器(如 Chromium)与在 AWS Lambda 上运行略有不同:根据 Puppeteer 官方的排查文档,Google Cloud Functions 的最新运行时缺少运行 Chromium 所必需的若干系统依赖(如共享库),导致浏览器无法正常启动。因此,如果要在 GCP 上运行浏览器启用的 Crawlee 爬虫,就需要转向Cloud Run。
Cloud Run 是 GCP 的容器托管平台,核心特点包括:
- 按需拉起容器:GCP 会在收到请求时才启动你的容器,因此只按容器返回 HTTP 响应给客户端所花费的时间计费,空闲时零费用;
- 与 FaaS 高度相似:除了运行载体是 Docker 容器而非函数运行时之外,开发与部署模型与 Cloud Functions / AWS Lambda(几乎)一致;
- 本地可调试:你可以在本地运行、调试 Docker 容器,并确信云端环境与本地完全一致,相比普通 FaaS 拥有更好的开发体验。
从 Crawlee 仓库的部署文档结构也可以印证这一点:docs/deployment/目录下同时提供了 gcp-browsers.md(本文主题)、aws-browsers.md(AWS Lambda 版)与 gcp-cheerio.md(无浏览器场景),三份文档相互对照,正好覆盖了"有/无浏览器 × GCP/AWS"四种常见部署组合。
第一步:关闭存储持久化
在服务端(Serverless/容器平台)运行 Crawlee 爬虫时,第一步永远是给爬虫构造函数传入一个新的Configuration实例,并关闭存储持久化:
import { Configuration, PlaywrightCrawler } from 'crawlee'; import { router } from './routes.js'; const startUrls = ['https://crawlee.dev']; const crawler = new PlaywrightCrawler({ requestHandler: router, }, new Configuration({ persistStorage: false, })); await crawler.run(startUrls);persistStorage 背后的原理
从源码看,persistStorage是 Crawlee 配置体系中的一个标准字段,定义在 packages/core/src/configuration.ts:
persistStorage: field(coerceBoolean.default(true), 'CRAWLEE_PERSIST_STORAGE'),即:
- 默认值为
true:爬虫会把请求队列、数据集(Dataset)、键值存储(Key-Value Store)等状态持久化到本地磁盘(默认目录为./storage,对应storageDir字段); - 可配置为
false:关闭磁盘持久化,所有状态保存在内存中; - 环境变量:
CRAWLEE_PERSIST_STORAGE,支持字符串形式的'false'/'0'被解析为假值(见同文件中的coerceBoolean预处理逻辑)。
Configuration实例的取值优先级为:构造函数传入选项 > 环境变量 > crawlee.json > schema 默认值(见 configuration.ts 中的文档注释)。因此这里通过构造函数显式传入new Configuration({ persistStorage: false }),优先级最高。
为什么在容器/Serverless 中要关闭持久化?原因有二:
- 每个请求/实例拥有独立存储:每次请求都新建 crawler 实例并传入独立
Configuration,多个并发实例之间不会互相干扰(这与 AWS Lambda 场景的推荐做法完全一致,见 aws-browsers.md); - 容器文件系统不可靠:Cloud Run 的容器实例随时可能被回收,磁盘不是持久化介质;关闭持久化还能避免向只读/临时文件系统写入报错。
第二步:用 Express 包装为 HTTP 服务
Cloud Run 平台只看到一个"不透明的 Docker 容器",它并不知道里面跑的是什么服务。因此,我们需要自己用 HTTP 框架把爬虫包装成可被外部访问的 HTTP 接口。
首先安装 Express:
npm i express关键点:GCP 会通过环境变量PORT告诉你的容器应该监听哪个端口,GCP 会把这个端口暴露给外网。你的 HTTP 服务器必须监听该端口。
改造后的src/main.js最终形态如下:
import { Configuration, PlaywrightCrawler } from 'crawlee'; import { router } from './routes.js'; import express from 'express'; const app = express(); const startUrls = ['https://crawlee.dev']; app.get('/', async (req, res) => { const crawler = new PlaywrightCrawler({ requestHandler: router, }, new Configuration({ persistStorage: false, })); await crawler.run(startUrls); return res.send(await crawler.getData()); }); app.listen(parseInt(process.env.PORT) || 3000);代码要点解读
- 每次请求新建 crawler:爬虫在
app.get('/')的请求处理函数内部创建,保证每个 HTTP 请求对应一次独立的爬取任务; getData()读取结果:crawler.getData()是 Crawlee 内置的快捷方法,底层调用Dataset.getData(),从默认数据集读取爬取结果并以 JSON 形式返回给客户端。其实现位于 packages/basic-crawler/src/internals/basic-crawler.ts:
async getData(...args: Parameters<Dataset['getData']>): ReturnType<Dataset['getData']> { const dataset = await this.getDataset(); return dataset.getData(...args); }- 端口兜底:
parseInt(process.env.PORT) || 3000在本地开发时(没有PORT环境变量)默认监听 3000 端口。
无状态约束(Stateless)
:::tip 重要提示 与所有 FaaS 服务一样,必须把全部逻辑都放在请求处理函数内,请求处理必须是**无状态(stateless)**的。
不要在模块顶层创建并复用 crawler 实例、不要在模块作用域保存爬取状态或队列数据。Cloud Run 可能随时横向扩容出多个实例,也可能回收闲置实例;只有"请求进来 → 新建 crawler → 爬取 → 返回结果"这种纯无状态模式才能保证任意实例都能独立、正确地服务任意请求。 :::
关于无状态模式,仓库中的 running-in-web-server.mdx 指南还提到一个值得注意的细节:如果把长驻 crawler 与 HTTP 服务结合在一起(而非每次请求新建实例),请求队列会持续增长——每个进来的 HTTP 请求都会获得独立uniqueKey而无法去重,队列会保存每一个已处理过的请求记录。对于长时间运行的进程,更合理的做法是直接回收进程。这从侧面印证了"每次请求新建 crawler 实例"这种模式在 Cloud Run 按需实例场景下的优势。
第三步:部署到 GCP
准备 Dockerfile
如果你是用以下命令初始化项目的:
npx crawlee create初始化脚本会自动为你生成Dockerfile。以仓库中的 Playwright 模板为例,packages/templates/templates/playwright-ts/Dockerfile 展示了浏览器爬虫项目 Dockerfile 的标准结构:
# 基于预装 Playwright + Chrome 的基础镜像(多阶段构建) FROM apify/actor-node-playwright-chrome:24-1.58.2 AS builder # 先只拷贝 package 文件,充分利用 Docker 层缓存加速构建 COPY --chown=myuser package*.json ./ RUN npm install --include=dev --audit=false # 拷贝源码并构建 COPY --chown=myuser . ./ RUN npm run build # 最终镜像:只保留编译产物与生产依赖 FROM apify/actor-node-playwright-chrome:24-1.58.2 COPY --from=builder --chown=myuser /home/myuser/dist ./dist COPY --chown=myuser package*.json ./ RUN npm --quiet set progress=false \ && npm install --omit=dev \ && echo "Installed NPM packages:" \ && (npm list --omit=dev --all || true) COPY --chown=myuser . ./ # 运行:XVFB 脚本支持 headful 模式 CMD ./start_xvfb_and_run_cmd.sh && npm run start:prod --silent关于基础镜像的选型,可以参考仓库中的 docker_images.mdx 指南:
apify/actor-node-playwright-chrome:预装 Playwright + Chrome,支持PlaywrightCrawler,体积比全量浏览器镜像小,适合大多数 Playwright 场景;apify/actor-node-playwright:预装 Chromium、Chrome、Firefox、WebKit 全部浏览器,体积最大;apify/actor-node:最小镜像,不含任何浏览器,只适合CheerioCrawler;- 生产环境建议同时锁定 Node.js 版本与自动化库版本标签(如
24-1.58.2),并将package.json中的playwright版本与之对齐,避免浏览器与库版本不匹配导致的兼容性问题。
执行 gcloud 部署
准备工作就绪后,在包含Dockerfile的项目文件夹内执行:
gcloud run deploygcloudCLI 会以交互方式向你提问,典型问题包括:
- 部署到哪个区域(region):选择离你的目标网站更近或符合合规要求的区域;
- 是否允许未认证调用(public or private):如果爬虫接口是内部服务,选择 private;如果需要对外暴露,选择 public 并配置认证。
回答完这些问题后,gcloud会构建镜像、推送到 Artifact Registry 并创建 Cloud Run 服务。完成后你可以在 GCP 控制台的 Cloud Run 面板中看到你的应用,并使用面板中提供的访问链接直接触发爬虫。
首次运行失败的排查建议
:::tip 常见坑 如果首次运行新创建的 Cloud Run 服务失败,请编辑 Run 配置,重点调整以下两项:
- 内存(memory):设置为1GiB 或更高。完整尺寸的 Chromium 需要数百 MB 内存,默认的小内存配置很容易导致浏览器进程被 OOM 杀掉;
- 请求超时(request timeout):根据你抓取的目标网站规模调整。页面越大、请求越慢,爬取一个完整任务所需的时间越长,默认超时可能不够用。 :::
同样的约束在 AWS Lambda 场景也存在——aws-browsers.md 明确建议将 Lambda 内存设置为 1024 MB 以上,并据本地实测运行时间设置超时。Cloud Run 与 Lambda 在"浏览器需要大内存、需要足够超时"这一点上是完全一致的。
与其他部署路径的对比
为了帮助你在选型时做出判断,下表对比了 GCP 上的两条部署路径:
| 维度 | Cloud Run(本文) | Cloud Functions(见 gcp-cheerio.md) |
|---|---|---|
| 运行载体 | Docker 容器 | 函数运行时 |
| 浏览器支持 | 完整支持(Chromium 依赖齐全) | 不支持(缺少 Chromium 运行依赖) |
| 适用爬虫 | PlaywrightCrawler/PuppeteerCrawler等浏览器爬虫 | CheerioCrawler等无浏览器爬虫 |
| 代码包装方式 | Express 等 HTTP 框架 + 监听PORT | 导出handler(req, res)命名函数 |
| 本地调试 | 可本地运行同一 Docker 镜像 | 依赖模拟器 |
| 配置要点 | 内存 ≥ 1GiB、调大请求超时 | 设置入口函数(Entry point)与内存/超时 |
如果你只需要抓取静态 HTML,用CheerioCrawler走 Cloud Functions 更轻量、成本更低;一旦需要执行 JavaScript、渲染页面或模拟真实浏览器行为,就必须使用本文的 Cloud Run 方案。
总结
将 Crawlee 浏览器爬虫部署到 GCP Cloud Run,核心只有三步:
- 关闭持久化:给 crawler 传入
new Configuration({ persistStorage: false }),避免容器文件系统不可靠带来的问题; - HTTP 包装:用 Express 监听 GCP 注入的
PORT环境变量,把爬取任务包装成无状态的 HTTP 请求处理函数; - 容器化部署:利用
npx crawlee create生成的 Dockerfile,执行gcloud run deploy交互式部署,并保证内存 ≥ 1GiB、超时设置合理。
这一模式同样适用于 AWS Lambda(aws-browsers.md)等 Serverless 平台,核心思想一致:独立配置实例 + 无状态请求处理 + 按需计费。按此流程,你可以在几分钟内拥有一个通过 HTTPS 调用即可触发完整浏览器爬取任务的生产级服务。
【免费下载链接】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),仅供参考