news 2026/9/12 4:41:36

Crawlee v3 升级指南:从 Apify SDK v2 迁移到 Crawlee 的完整实践手册

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Crawlee v3 升级指南:从 Apify SDK v2 迁移到 Crawlee 的完整实践手册

Crawlee v3 升级指南:从 Apify SDK v2 迁移到 Crawlee 的完整实践手册

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

本文基于仓库中 docs/upgrading/upgrading_v3.md 整理而成,结合packages/下的源码与测试对各个变更点做源码级印证。Crawlee v3 是 Apify SDK v2 的"精神继承者",本次升级不仅是包名更替,更是一次 API 全面重构:Monorepo 拆分、TypeScript 全量重写、上下文感知助手、隐式 RequestQueue、批量入队、指纹伪装、内存存储等核心机制全部重做。读完本文你将掌握从 v2 平滑迁移到 v3 的完整改动清单,以及每个关键 API 在源码中的真实实现位置与行为细节。

:::info 阅读前提 本文面向已经熟悉 Apify SDK v2 的开发者。若你第一次接触 Crawlee,建议先阅读 docs/introduction/01-setting-up.mdx 了解基础概念,再回来看迁移细节。 :::

一、背景:为什么 Apify SDK v2 要变成 Crawlee v3

直到apify包的 v3 之前,Apify SDK 一直把爬虫工具Apify 平台辅助方法打包在同一个包里。随着两个领域各自的复杂度不断上升,v3 版本将整个项目拆分成两个独立部分:

  • Crawlee:全新的网页抓取库,以crawlee包发布到 NPM,本仓库即其源码;
  • Apify SDK:Apify 平台辅助工具(Actor类、存储、事件等),以apify包独立发布。

Crawlee 被命名为 v3 正是为了延续 Apify SDK 的版本号,作为其"精神继承者"(spiritual successor)。这一拆分让两个项目可以各自独立演进版本、独立发布,也让只想用爬虫能力的用户不必再引入平台相关的代码。

二、Crawlee Monorepo:一个元包,多个子包

crawlee包本身是一个 Monorepo,由若干独立发布、统一挂在@crawlee命名空间下的小包组成。原文档列出的核心子包如下:

子包导出内容 / 职责
@crawlee/core所有 Crawler 实现的基础,包含RequestRequestQueueRequestListDataset等类
@crawlee/cheerio导出CheerioCrawler
@crawlee/playwright导出PlaywrightCrawler
@crawlee/puppeteer导出PuppeteerCrawler
@crawlee/jsdom导出JSDOMCrawler
@crawlee/basic导出BasicCrawler
@crawlee/http导出HttpCrawler@crawlee/jsdom@crawlee/cheerio的基类)
@crawlee/browser导出BrowserCrawler@crawlee/playwright@crawlee/puppeteer的基类)
@crawlee/memory-storage@apify/storage-local的替代品
@crawlee/browser-poolbrowser-pool
@crawlee/utils工具方法
@crawlee/types主要存放关于StorageClient的 TS 接口

这些包在仓库中一一对应packages/目录下的同名子目录(如 packages/core、packages/cheerio-crawler、packages/browser-pool 等),并通过pnpm-workspace.yaml组织为工作区。

安装策略:按需安装单个子包或直接装元包

大部分 Crawlee 包是互相继承并 re-export的,所以通常只需要安装你要用的那一个。例如安装@crawlee/playwright时,它已经包含了@crawlee/browser的全部内容,而后者又包含@crawlee/basic,进而包含@crawlee/core的全部内容。

如果不介意多拉一点代码,可以直接使用crawlee元包(meta-package),它 re-export 了大部分@crawlee/*包,因此包含所有 Crawler 类:

npm install crawlee

如果只需要 Cheerio 支持,只装@crawlee/cheerio即可:

npm install @crawlee/cheerio

使用playwrightpuppeteer时,还需要显式安装浏览器库本身——这样用户可以完全控制浏览器库的版本:

npm install crawlee playwright # 或 npm install @crawlee/playwright playwright

提示:如果偶尔想用@crawlee/utils里的工具方法,可以额外安装它。这个包包含此前Apify.utils下的一部分工具;浏览器相关的工具方法则可以直接从 crawler 包(如@crawlee/playwright)里拿到。

三、全量 TypeScript 支持与项目配置

Crawlee 和 Apify SDK 都是全量 TypeScript 重写,因此包内自带最新类型的声明。官方推荐使用@apify/tsconfig包里预置的 TypeScript 配置,并且务必把moduletarget设为ES2022或更高,以便使用顶层 await(top level await):

{ "extends": "@apify/tsconfig", "compilerOptions": { "module": "ES2022", "target": "ES2022", "outDir": "dist", "lib": ["DOM"] }, "include": [ "./src/**/*" ] }

注意@apify/tsconfig默认开启了noImplicitAny。如果你在开发初期还有未使用的局部变量,它会直接导致构建失败,可以先临时关掉它,等代码稳定后再打开。

Docker 多阶段构建

对于Dockerfile,原文档推荐多阶段构建(multi-stage build),避免把 TypeScript 这类 devDependencies 装进最终镜像:

# 使用多阶段构建,因为需要 dev deps 来编译 TS 源码 FROM apify/actor-node:20 AS builder # 拷贝所有文件、安装全部依赖(含 dev deps)并构建 COPY . ./ RUN npm install --include=dev \ && npm run build # 创建最终镜像 FROM apify/actor-node:20 # 只拷贝必要文件 COPY --from=builder /usr/src/app/package*.json ./ COPY --from=builder /usr/src/app/README.md ./ COPY --from=builder /usr/src/app/dist ./dist COPY --from=builder /usr/src/app/apify.json ./apify.json COPY --from=builder /usr/src/app/INPUT_SCHEMA.json ./INPUT_SCHEMA.json # 只安装生产依赖 RUN npm --quiet set progress=false \ && npm install --only=prod --no-optional \ && echo "Installed NPM packages:" \ && (npm list --only=prod --no-optional --all || true) \ && echo "Node.js version:" \ && node --version \ && echo "NPM version:" \ && npm --version # 运行编译后的代码 CMD npm run start:prod

第一阶段(builder)安装全部依赖并执行npm run build产出dist;第二阶段只拷贝package*.jsonREADME.mddist以及平台配置文件,然后仅安装生产依赖,最大程度缩小镜像体积。

四、浏览器指纹:从stealth魔法到可配置指纹

v2 时代 Puppeteer Crawler 里有一个"神奇"的stealth选项,它开启了一系列模仿真实用户的技巧。它虽然有一定效果,但 Crawlee 决定用**生成的浏览器指纹(browser fingerprints)**来取代它——指纹比固定技巧更接近真实浏览器的多样性。

如果不想使用动态指纹,可以在browserPoolOptions中通过useFingerprints: false关闭:

const crawler = new PlaywrightCrawler({ browserPoolOptions: { useFingerprints: false, }, });

在 packages/browser-pool/src/browser-pool.ts 中可以印证:useFingerprints的默认值是truefingerprintOptions默认是空对象。同时源码显示指纹缓存相关配置:useFingerprintCache默认truefingerprintCacheSize默认10000(见同文件 L986-L990),指纹生成器FingerprintGenerator会在启用时被创建。

相关重命名:fingerprintsOptionsfingerprintOptions

v3 还顺带把选项名统一为单数:fingerprintsOptions改名为fingerprintOptionsfingerprintsfingerprint)。更重要的是,缓存策略从"按代理 URL 缓存"改成了"按会话缓存":

  • useFingerprintPerProxyCachefingerprintPerProxyCacheSize不再可用
  • 取而代之的是useFingerprintCachefingerprintCacheSize——因为缓存的指纹不再与代理 URL 绑定,而是与session绑定。

五、Session Cookie 方法重命名

此前要读写会话(session)的 cookie,需要调用session.getPuppeteerCookies()session.setPuppeteerCookies()。但这个方法并不只服务于PuppeteerCrawler,任何 Crawler 都可能用到,因此重命名为更通用的:

  • session.getPuppeteerCookies()session.getCookies()
  • session.setPuppeteerCookies()session.setCookies()

除此之外的用法完全一致,迁移时只需全局替换方法名。

六、默认存储改为内存存储(Memory Storage)

v3 默认使用@crawlee/memory-storage来存储数据与中间状态(例如RequestQueue持有的状态),它是对@apify/storage-local的替代:

  • 状态存在内存中(而@apify/storage-local用的是 SQLite 数据库);
  • 同时也会把状态转储到文件系统,方便观察;
  • 并且会尊重 KeyValueStore 里已存在的数据(例如INPUT.json文件)。

要在 Apify 平台上运行,需要使用Actor.initActor.main,它们会在检测到 Apify 平台环境时自动把存储客户端切换为ApifyClient(本地运行则保持默认的内存存储)。

如果仍想使用@apify/storage-local,需要先安装它(v2.1.0+ 才支持 Crawlee),再传给Actor.initActor.main的选项:

import { Actor } from 'apify'; import { ApifyStorageLocal } from '@apify/storage-local'; const storage = new ApifyStorageLocal(/* 例如 enableWalMode 等选项写在这里 */); await Actor.init({ storage });

七、默认存储清理行为反转

v2 时代本地多次运行之间状态会保留,必须靠apify-cli--purge参数手动清理。Crawlee 把这一行为反转Actor.init/main调用时默认自动清理存储。想退出这一行为,在Actor.init选项中传purge: false

await Actor.init({ purge: false });

从源码可以印证,仓库中对应的配置项是purgeOnStart,默认true,并可用环境变量CRAWLEE_PURGE_ON_START覆盖(见 packages/core/src/configuration.ts)。同时,StorageClient接口新增了可选的purge方法,清理逻辑被移动到了存储类本身,原先的purgeLocalStorage辅助函数被移除。

八、Crawler 选项与接口重命名

v3 重命名了一批选项,让它们更准确地表达含义。旧的参数名在运行时仍然支持,但在 TS 类型层面不再提供

旧名称新名称
handleRequestFunctionrequestHandler
handlePageFunctionrequestHandler
handleRequestTimeoutSecsrequestHandlerTimeoutSecs
handlePageTimeoutSecsrequestHandlerTimeoutSecs
requestTimeoutSecsnavigationTimeoutSecs
handleFailedRequestFunctionfailedRequestHandler

同时,爬取上下文(crawling context)的接口也按同一约定重命名:

  • CheerioHandlePageInputsCheerioCrawlingContext
  • PlaywrightHandlePageFunctionPlaywrightCrawlingContext
  • PuppeteerHandlePageFunctionPuppeteerCrawlingContext

九、上下文感知助手(Context-aware helpers)

此前挂在Apify.utils命名空间下的一部分工具,现在移动到了**爬取上下文(crawling context)**中,并变得"上下文感知"(context aware):部分参数会自动从上下文填充,例如当前的Request实例、当前的Page对象,或与 Crawler 绑定的RequestQueue

enqueueLinks:不再需要手动传参

最典型的例子是enqueueLinks。它是上下文感知的,不再需要传入requestQueuepage参数(Cheerio 场景下也不需要传入$)。它还提供3 种入队策略

策略枚举值字符串值匹配规则
EnqueueStrategy.All'all'匹配页面中找到的任何 URL
EnqueueStrategy.SameHostname'same-hostname'匹配与基准 URL同子域名的 URL(默认策略
EnqueueStrategy.SameDomain'same-domain'匹配与基准 URL同域名的 URL。例如基准 URL 为https://example.com时,https://wow.an.example.comhttps://example.com都会被匹配

这意味着甚至可以不带任何参数直接调用enqueueLinks()——默认会遍历当前页面所有链接,只保留指向同一子域名的那些。

从源码看,策略枚举定义在 packages/utils/src/internals/url.ts,除了上述三种,实际还存在第四种SameOrigin'same-origin',要求主机名与协议都相同)。enqueueLinks的实现位于 packages/core/src/enqueue_links/enqueue_links.ts,其中strategy默认值为EnqueueStrategy.SameHostname(L126)。

另外,还可以通过 glob 指定 URL 必须匹配的模式:

const crawler = new PlaywrightCrawler({ async requestHandler({ enqueueLinks }) { await enqueueLinks({ globs: ['https://crawlee.dev/*/*'], // 这里也可以使用 regexps 和 pseudoUrls }); }, });

错误日志变得更简洁

v2 中,request handler 抛出的错误会导致整个错误对象被完整打印。Crawlee 在确认请求会被重试的情况下,只把错误消息作为 warning 输出。想恢复 v2 那样详细的日志,设置环境变量CRAWLEE_VERBOSE_LOG即可。这一点在 packages/basic-crawler/src/internals/basic-crawler.ts 有源码印证:CRAWLEE_VERBOSE_LOG存在时打印完整error.stack,否则只输出error.message

sendRequest():处理上下文绑定的请求

v3 移除了requestAsBrowser,并新增context.sendRequest()助手,允许把上下文绑定的Request对象交给 got-scraping 处理:

const crawler = new BasicCrawler({ async requestHandler({ sendRequest, log }) { // 可以用 options 参数覆盖 gotScraping 的选项 const res = await sendRequest({ responseType: 'json' }); log.info('received body', res.body); }, });

关于sendRequest()的详细用法,见仓库中的 Got Scraping 指南。

十、隐式RequestQueue与批量入队

隐式 RequestQueue 实例

所有 Crawler 现在都通过crawler.getRequestQueue()方法自动获得RequestQueue实例——实例不存在时会自动创建。这意味着不再需要手动创建RequestQueue,直接使用下面的crawler.addRequests()方法即可。

仍然可以显式创建RequestQueue并通过 crawler 选项传入,crawler.getRequestQueue()会尊重它并返回你提供的实例。

源码实现在 packages/basic-crawler/src/internals/basic-crawler.ts,getRequestQueue()在不存在时自动创建队列。

crawler.addRequests():分批添加海量请求

新增的addRequests方法会批量添加请求:先入队前 1000 个请求并 resolve,剩余的在后台继续以 1000 个为一小批添加,从而避免触发 API 限流。这意味着爬取几乎立刻开始(最多几秒内),而这在 v2 中只有组合使用RequestQueueRequestList才能实现。

// 在前 1000 个请求入队后立即 resolve;requests 可以是数百万级 const result = await crawler.addRequests([/* many requests, can be even millions */]); // 如果想等所有请求都入队,await 这个 promise 即可 await result.waitForAllRequestsToBeAdded;

在源码中,addRequests是对隐式RequestQueueaddRequestsBatched()的别名(见 packages/basic-crawler/src/internals/basic-crawler.ts),返回的CrawlerAddRequestsResult上挂着waitForAllRequestsToBeAdded

十一、Request.label快捷方式

给请求打标签(labeling)v2 时代需要操作Request.userData对象。v3 提供了Request.label快捷方式,它是一对get/set访问器,读写的就是userData里的值。该快捷方式同样被加入到了enqueueLinks的选项接口中。

在 packages/core/src/request.ts 可以看到实现:label的 getter 返回this.userData.label,setter 写入this.userData.label。构造函数中若传入label,也会自动写入userData.label(同文件 L244-L246),因此它与userData完全等价。

async requestHandler({ request, enqueueLinks }) { if (request.label !== 'DETAIL') { await enqueueLinks({ globs: ['...'], label: 'DETAIL', }); } }

十二、移除requestAsBrowser:全面转向 got-scraping

v1 中requestAsBrowser的底层实现已经是got-scraping(一个尽力模仿真实浏览器的got扩展)的薄封装。v3 直接移除requestAsBrowser,鼓励直接使用got-scraping。为便于迁移,v3 新增了上一节提到的context.sendRequest()助手。

被移除的选项

  • useInsecureHttpParser:已移除,现在永久设为true,以更好地模仿浏览器行为;
  • useHttp2:已移除。got-scraping 会自动进行协议协商,HTTP/2 能力被设为true——如今 100% 的浏览器都支持 HTTP/2,Web 也在大规模使用它。

被重命名的选项

payloadbody/json

payload代表要发送的请求体,可以是字符串或Buffer。现在没有payload选项了,改用body;想发 JSON 则用json

// Before: await Apify.utils.requestAsBrowser({ …, payload: 'Hello, world!' }); await Apify.utils.requestAsBrowser({ …, payload: Buffer.from('c0ffe', 'hex') }); await Apify.utils.requestAsBrowser({ …, json: { hello: 'world' } }); // After: await gotScraping({ …, body: 'Hello, world!' }); await gotScraping({ …, body: Buffer.from('c0ffe', 'hex') }); await gotScraping({ …, json: { hello: 'world' } });
ignoreSslErrorshttps.rejectUnauthorized

ignoreSslErrors被重命名为https.rejectUnauthorized。默认值是false(即默认不校验,方便使用)。注意语义相反,所以取值也要取反:

// Before: await Apify.utils.requestAsBrowser({ …, ignoreSslErrors: false }); // After: await gotScraping({ …, https: { rejectUnauthorized: true } });
header-generator选项

useMobileVersionlanguageCodecountryCode三个选项都不存在了,需要直接使用headerGeneratorOptions

// Before: await Apify.utils.requestAsBrowser({ …, useMobileVersion: true, languageCode: 'en', countryCode: 'US', }); // After: await gotScraping({ …, headerGeneratorOptions: { devices: ['mobile'], // 或 ['desktop'] locales: ['en-US'], }, });
timeoutSecstimeout.request

设置超时改用timeout.request,并且单位是毫秒

// Before: await Apify.utils.requestAsBrowser({ …, timeoutSecs: 30, }); // After: await gotScraping({ …, timeout: { request: 30 * 1000, }, });
throwOnHttpErrorsthrowHttpErrors

该选项决定是否在非成功 HTTP 状态码(例如 404)时抛出异常。默认值为false

decodeBodydecompress

该选项控制是否解压响应体。默认true——除非你很清楚后果,否则不要改,否则很多网站会解析失败。

abortFunction的替代方案

这个函数曾经在返回true时让 promise 针对特定响应抛出异常,但实用性有限。现在的推荐做法是主动取消请求

const promise = gotScraping(…); promise.on('request', request => { // 注意:这里不是 Got 的 Request 实例,而是 Node 的 ClientRequest 实例 // https://nodejs.org/api/http.html#class-httpclientrequest if (request.protocol !== 'https:') { // 非安全请求,中止 promise.cancel(); // 如果设置了 isStream: true,请改用 stream.destroy() } }); const response = await promise;

十三、禁止混合浏览器池插件

v2 允许创建一个同时混用 Puppeteer 和 Playwright 插件(甚至自定义插件)的浏览器池。从 v3 起这不再被允许,创建此类池会抛出错误——所有将要使用的插件必须是同一类型。

:::info 别混淆了 这个变更只是禁止把 Puppeteer 与 Playwright 混在一个池里。你仍然可以创建使用多个 Playwright 插件(各自使用不同 launcher)的池。 :::

这一点与仓库中的测试相印证:test/browser-pool/下存在专门的no-hybrid-plugins.test.ts测试来验证混合插件被拒绝的行为。

十四、在浏览器之外处理请求

一个值得注意的小功能:浏览器型 Crawler 也可以在浏览器之外处理请求。做法是组合使用Request.skipNavigationcontext.sendRequest()

Request.skipNavigation在 packages/core/src/request.ts 中实现:置为true时,crawler 会跳过浏览器导航、直接处理请求,此时上下文里将没有导航结果(responsebodycontentType$request.loadedUrl等),访问这些属性会抛出NavigationSkippedError

完整的示例见 跳过导航示例 以及配套代码 skip-navigation.ts。

十五、日志系统与上下文日志

Crawlee 把默认的log实例作为命名导出直接暴露。同时,爬取上下文中提供了一个带作用域的log实例——它打印的日志会带上前缀 crawler 名称,在 request handler 内部做日志时应优先使用它:

const crawler = new CheerioCrawler({ async requestHandler({ log, request }) { log.info(`Opened ${request.loadedUrl}`); }, });

十六、自动保存的爬虫状态useState()

每个 Crawler 实例都有useState()方法,返回一个状态对象。当persistState事件触发时它会被自动保存。值是被缓存的,因此可以多次调用此方法并拿到完全相同的引用——不需要操心保存,会自动完成:

const crawler = new CheerioCrawler({ async requestHandler({ crawler }) { const state = await crawler.useState({ foo: [] as number[] }); // 直接改值即可,无需关心保存 state.foo.push(123); }, });

从源码看,useState的实现位于 packages/basic-crawler/src/internals/basic-crawler.ts:它打开 KeyValueStore 并调用getAutoSavedValue()获取自动保存的值;多个 crawler 实例同时使用useState()且未指定显式id时,会共享同一个状态对象并收到警告,建议为每个实例传入唯一id

十七、Apify SDK:平台辅助工具的新家

Apify 平台相关的辅助方法现在统一在 Apify SDK(apifyNPM 包)中,它导出Actor类,提供以下静态助手:

  • ApifyClient快捷方式:addWebhook()call()callTask()metamorph()
  • 在 Apify 平台运行的助手:init()exit()fail()main()isAtHome()createProxyConfiguration()
  • 存储支持:getInput()getValue()openDataset()openKeyValueStore()openRequestQueue()pushData()setValue()
  • 事件支持:on()off()
  • 其他工具:getEnv()newClient()reboot()

Actor.main只是语法糖

Actor.main本质上是Actor.init()+ 用户函数 +Actor.exit()的语法糖(外加 try/catch 包裹)。所有方法都是 async 的且应被 await——Node 16 及以上可以用顶层 await。以下两种写法等价:

import { Actor } from 'apify'; await Actor.init(); // your code await Actor.exit('Crawling finished!');
import { Actor } from 'apify'; await Actor.main(async () => { // your code }, { statusMessage: 'Crawling finished!' });

Actor.init()会在 Apify 平台上把 Crawlee 的存储实现条件性地切换为ApifyClient,否则保留默认的内存存储实现;同时会订阅 websocket 事件(本地运行时则模拟它们)。Actor.exit()负责清理资源并调用process.exit(),确保进程不会因某些原因无限挂起。

事件系统:从Apify.eventsEventManager

Apify SDK v2 导出Apify.events,它是一个EventEmitter实例。Crawlee 中事件改由EventManager类管理。可以通过Actor.eventManagergetter 访问,或者直接使用Actor.on/Actor.off快捷方式:

-Apify.events.on(...); +Actor.on(...);

也可以通过Configuration.getEventManager()拿到EventManager实例。

在既有事件之外,现在还有一个exit事件,在调用Actor.exit()时触发(Actor.main()结束时也会调用它)。这个事件允许你在Actor.exit被调用时优雅地关停任何资源。

十八、更小/内部级别的破坏性变更清单

原文档还列出了一批较小的破坏性变更,迁移时请逐一核对:

  • Apify.call()现在是ApifyClient.actor(actorId).call(input, options)的快捷方式,同时会考虑环境变量中的 token;
  • Apify.callTask()现在是ApifyClient.task(taskId).call(input, options)的快捷方式,同时会考虑环境变量中的 token;
  • Apify.metamorph()现在是ApifyClient.task(taskId).metamorph(input, options)的快捷方式,同时会考虑环境变量中的ACTOR_RUN_ID
  • Apify.waitForRunToFinish()已移除,改用ApifyClient.waitForFinish()
  • Actor.main/init默认清理存储(可用purge: false退出);
  • 移除purgeLocalStorage辅助函数,清理逻辑移到存储类本身:StorageClient接口新增可选purge方法;清理通过Actor.init()自动发生;
  • QueueOperationInfo.request不再可用;
  • Request.handledAt现在是 ISO 格式的字符串日期(源码中可见其定义与校验,见 packages/core/src/request.ts);
  • Request.inProgressRequest.reclaimed从 POJO 变成了Set
  • puppeteer utils 中的injectUnderscore已移除;
  • APIFY_MEMORY_MBYTES不再被考虑,改用CRAWLEE_AVAILABLE_MEMORY_RATIO(默认值0.25,见 packages/core/src/configuration.ts);
  • 部分AutoscaledPool选项不再可用:
    • cpuSnapshotIntervalSecsmemorySnapshotIntervalSecs被顶层配置systemInfoIntervalMillis取代(默认1000ms,见 packages/core/src/configuration.ts);
    • maxUsedCpuRatio被移到顶层配置;
  • ProxyConfiguration.newUrlFunction可以是 async 的,.newUrl().newProxyInfo()现在返回 promise;
  • prepareRequestFunctionpostResponseFunction选项被移除,改用 navigation hooks;
  • gotoFunctiongotoTimeoutSecs被移除;
  • 移除了对旧/损坏的(含 nullRequest属性的)请求队列的兼容性修复;
  • fingerprintsOptions改名为fingerprintOptionsfingerprintsfingerprint);
  • fingerprintOptions现在接受useFingerprintCachefingerprintCacheSize(取代已不再可用的useFingerprintPerProxyCachefingerprintPerProxyCacheSize),因为缓存的指纹不再关联代理 URL,而是关联session

十九、迁移检查清单

把以上所有变更归纳成一份可直接对照的检查清单:

  1. 依赖:按需安装crawlee元包或@crawlee/*子包;使用 Playwright/Puppeteer 时记得显式安装对应浏览器库;
  2. TypeScript 配置extends: "@apify/tsconfig"module/target设为ES2022+以支持顶层 await;
  3. 选项重命名handlePageFunctionrequestHandlerhandlePageTimeoutSecsrequestHandlerTimeoutSecsrequestTimeoutSecsnavigationTimeoutSecshandleFailedRequestFunctionfailedRequestHandler
  4. 上下文接口CheerioHandlePageInputsCheerioCrawlingContextPlaywrightHandlePageFunctionPlaywrightCrawlingContextPuppeteerHandlePageFunctionPuppeteerCrawlingContext
  5. 存储:默认使用@crawlee/memory-storage;在 Apify 平台用Actor.init/main自动切换ApifyClient;需要时可用@apify/storage-localv2.1.0+;本地默认自动清理存储(purge: false退出);
  6. Session cookiegetPuppeteerCookies()/setPuppeteerCookies()getCookies()/setCookies()
  7. 入队:直接用上下文感知的enqueueLinks()(默认SameHostname策略);大批量请求用crawler.addRequests()并配合waitForAllRequestsToBeAdded
  8. 标签:用Request.label代替手动操作userData.label
  9. HTTP 请求:移除requestAsBrowser,改用got-scrapingcontext.sendRequest(),并按上文映射表重命名各选项;
  10. 浏览器池:禁止混用 Puppeteer 与 Playwright 插件;
  11. 状态:用crawler.useState()获取自动保存的状态;
  12. 平台代码Apify.eventsActor.on/offEventManagerwaitForRunToFinishApifyClient.waitForFinish()
  13. 环境变量APIFY_MEMORY_MBYTESCRAWLEE_AVAILABLE_MEMORY_RATIO;需要详细错误日志时设置CRAWLEE_VERBOSE_LOG

延伸阅读

  • 完整的 v2 → v3 官方升级文档(本文的原始依据)
  • Crawlee 版本迁移总览
  • Got Scraping 指南:sendRequest()的详细用法
  • 跳过导航示例:skipNavigation+sendRequest()
  • 内存存储实现
  • 浏览器池实现(指纹、插件等)
  • Request 类实现(labelskipNavigationuniqueKey等)

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 4:40:41

SSE技术与Quart框架实现实时数据推送

1. 实时事件流与SSE技术概述当我们需要在Web应用中实现服务器向客户端主动推送数据时,传统的HTTP请求-响应模式就显得力不从心了。这正是Server-Sent Events(SSE)技术大显身手的地方。与WebSocket不同,SSE是建立在HTTP协议之上的轻量级解决方案&#xff…

作者头像 李华
网站建设 2026/9/12 4:40:02

3步跑通Stable Baselines3强化学习训练:实战指南与新手避坑清单

3步跑通Stable Baselines3强化学习训练:实战指南与新手避坑清单 【免费下载链接】stable-baselines3 PyTorch version of Stable Baselines, reliable implementations of reinforcement learning algorithms. 项目地址: https://gitcode.com/GitHub_Trending/st…

作者头像 李华
网站建设 2026/9/12 4:39:29

Rocky Linux部署ELK+Redis构建高可用日志收集系统

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 4:37:09

AI五大核心能力:数学、工程、产品、伦理、系统集成实战路径

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 4:34:44

海光DCU接入K8s:整卡、共享与vDCU调度实战解析

如果你接过“把一批海光 DCU 节点接进 Kubernetes,再让上层 AI 平台和 DeepSeek 推理服务跑起来”这种需求,第一反应大概率是:装个驱动、部署个 Device Plugin 不就行了?真动手之后才会发现,整卡、共享、vDCU 虚拟化是…

作者头像 李华