Crawlee v3 升级完全指南:从 Apify SDK v2 迁移的破坏性变更与实战迁移手册
【免费下载链接】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 v3 是 Apify SDK v2 的"精神继承者",它将原本捆绑在一起的爬虫工具与 Apify 平台辅助方法拆分为两个独立的 NPM 包,并引入了包名、TS 类型、存储机制、会话 Cookie API、爬虫选项、上下文辅助方法(context-aware helpers)等一系列破坏性变更。本文以 upgrading_v3.md 为骨架,结合当前仓库的源码实现与文档,系统梳理从 Apify SDK v2 迁移到 Crawlee v3 的完整变更清单,帮助你理解每个变更背后的设计动机,并给出可直接复制的迁移代码。
背景:为什么会有 v3
在apifyv3 之前,apify包同时包含爬虫相关工具与 Apify 平台辅助方法。v3 将整个项目拆分为两个主要部分:
- Crawlee:新的 Web 爬取库,以
crawlee包发布在 NPM 上; - Apify SDK:Apify 平台辅助方法,以
apify包发布在 NPM 上。
Crawlee 作为 Apify SDK 的"精神继任者"(spiritual successor),沿用了原有版本号体系,因此 Crawlee 的第一个正式版本就是 v3。
Crawlee monorepo:从单包到多包架构
crawlee包由若干更小的包组成,这些包以@crawlee命名空间独立发布:
| 包名 | 职责 |
|---|---|
@crawlee/core | 所有爬虫实现的基础,包含Request、RequestQueue、RequestList、Dataset等类 |
@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-pool | 原browser-pool包 |
@crawlee/utils | 工具方法(原Apify.utils下的一部分) |
@crawlee/types | 主要承载StorageClient等 TS 接口定义 |
当前仓库正是这一 monorepo 结构的落地实现,packages/目录下的core、basic-crawler、cheerio-crawler、http-crawler、jsdom-crawler、browser-crawler、playwright-crawler、puppeteer-crawler、browser-pool、utils、types等即对应上述各包。
安装 Crawlee
大多数 Crawlee 包之间是相互扩展和重导出的关系,因此只需安装你实际要用的那个包即可。例如,如果计划使用playwright,安装@crawlee/playwright就足够了——它已经包含@crawlee/browser包的全部内容,而@crawlee/browser又包含@crawlee/basic,@crawlee/basic又包含@crawlee/core。
如果不在乎多引入一些额外代码,可以直接使用crawlee元包(meta-package),它重导出了大部分@crawlee/*包,因此包含全部爬虫类:
npm install crawlee如果只需要 cheerio 支持,可以只安装@crawlee/cheerio:
npm install @crawlee/cheerio使用playwright或puppeteer时,仍需显式安装这些浏览器依赖——这样可以让用户自行控制使用哪个版本:
npm install crawlee playwright # 或 npm install @crawlee/playwright playwright有时你可能想使用
@crawlee/utils中的工具方法,也可以单独安装它。该包包含了一些之前在Apify.utils下的工具,浏览器相关的工具也可以在爬虫包中找到(如@crawlee/playwright)。
完整的 TypeScript 支持
Crawlee 和 Apify SDK 都是完整的 TypeScript 重写,因此包内自带最新的类型定义。对于 TypeScript 爬虫,官方推荐使用@apify/tsconfig包中预定义的类型配置。不要忘记将module和target设置为ES2022或以上,以便使用顶层await(top level await)。
{ "extends": "@apify/tsconfig", "compilerOptions": { "module": "ES2022", "target": "ES2022", "outDir": "dist", "lib": ["DOM"] }, "include": [ "./src/**/*" ] }
@apify/tsconfig配置启用了noImplicitAny。初始开发阶段你可能想禁用它,因为如果代码中残留未使用的局部变量,它会导致构建失败。
Docker 多阶段构建
对于Dockerfile,推荐使用多阶段构建,这样最终镜像中不会安装 TypeScript 等开发依赖:
# 使用多阶段构建,因为构建 TS 源码需要 dev 依赖 FROM apify/actor-node:20 AS builder # 复制所有文件,安装所有依赖(包括 dev 依赖)并构建项目 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浏览器指纹(Browser fingerprints)
v2 中 Puppeteer 爬虫有一个"魔法般的"stealth选项,通过若干技巧尽可能模拟真实用户。虽然它在一定程度上有效,但 v3 决定用生成的浏览器指纹(generated browser fingerprints)取代它。
如果不想要动态指纹,可以在browserPoolOptions中通过useFingerprints禁用此行为:
const crawler = new PlaywrightCrawler({ browserPoolOptions: { useFingerprints: false, }, });从当前仓库的实现看,指纹缓存从"按代理 URL 关联"演进为"按会话关联":fingerprintOptions现在接受useFingerprintCache和fingerprintCacheSize(取代了此前的useFingerprintPerProxyCache和fingerprintPerProxyCacheSize),因为缓存的指纹不再与代理 URL 绑定,而是与会话绑定(见下文"内部破坏性变更")。这与 avoid_blocking_playwright.ts 等反封锁示例中指纹的实际用途一致。
会话 Cookie 方法重命名
v2 中,要获取或添加本次请求将要使用的会话 Cookie,需要调用session.getPuppeteerCookies()或session.setPuppeteerCookies()。由于该方法可以用于任何爬虫(不只PuppeteerCrawler),v3 将它们重命名为session.getCookies()和session.setCookies(),用法完全一致。
从源码来看,会话 Cookie 的存取已成为通用能力:packages/browser-pool的BrowserController抽象类定义了getCookies(page)与setCookies(page, cookies)(见 browser-controller.ts),Playwright、Puppeteer 乃至 Stagehand 控制器分别实现,而浏览器爬虫在恢复会话状态时正是通过session.cookieJar.getCookies(request.url)来同步 Cookie(见 browser-crawler.ts)。
内存存储(Memory storage)
存储数据或中间状态(例如RequestQueue持有的状态)时,v3 默认使用@crawlee/memory-storage。它是@apify/storage-local的替代品,将状态保存在内存中(@apify/storage-local则使用 SQLite 数据库)。虽然状态保存在内存中,它也会转储到文件系统,以便观察数据,同时会尊重KeyValueStore中已有的数据(例如INPUT.json文件)。
在 Apify 平台上运行爬虫时,需要使用Actor.init或Actor.main,它们会在 Apify 平台上自动将存储客户端切换为ApifyClient。
仍然可以使用@apify/storage-local,先安装它,再传给Actor.init或Actor.main的选项:
使用 Crawlee 需要
@apify/storage-localv2.1.0+。
import { Actor } from 'apify'; import { ApifyStorageLocal } from '@apify/storage-local'; const storage = new ApifyStorageLocal(/* options like `enableWalMode` belong here */); await Actor.init({ storage });默认存储的清理(Purging)
v2 中本地多次运行之间的状态会被保留,必须使用apify-cli的--purge参数手动清理。在 Crawlee 中,自动清理成为默认行为:调用Actor.init/main时会自动清理存储。可以通过Actor.init选项中的purge: false选择退出。
重命名的爬虫选项与接口
部分选项被重命名以更准确地反映其用途。旧参数名仍然被支持(在运行时层面),但 TS 类型层面不再提供:
| 旧选项 | 新选项 |
|---|---|
handleRequestFunction | requestHandler |
handlePageFunction | requestHandler |
handleRequestTimeoutSecs | requestHandlerTimeoutSecs |
handlePageTimeoutSecs | requestHandlerTimeoutSecs |
requestTimeoutSecs | navigationTimeoutSecs |
handleFailedRequestFunction | failedRequestHandler |
爬取上下文接口也按同样的约定重命名,语义更加明确:
CheerioHandlePageInputs→CheerioCrawlingContextPlaywrightHandlePageFunction→PlaywrightCrawlingContextPuppeteerHandlePageFunction→PuppeteerCrawlingContext
上下文感知的辅助方法(Context aware helpers)
原本位于Apify.utils命名空间下的一部分工具现在被移入爬取上下文(crawling context),并且是上下文感知的:一些参数会自动从上下文中填充,例如当前的Request实例、当前的Page对象,或绑定到爬虫的RequestQueue。
链接入队(enqueueLinks)
最受关注的辅助方法是enqueueLinks。如上面所说,它是上下文感知的——不再需要传入requestQueue或page参数(也不需要 cheerio 的$)。除此之外,它现在提供 3 种入队策略:
EnqueueStrategy.All('all'):匹配找到的任何 URL;EnqueueStrategy.SameHostname('same-hostname'):匹配与基准 URL 具有相同子域的任何 URL(默认);EnqueueStrategy.SameDomain('same-domain'):匹配与基准 URL 具有相同域名的任何 URL。例如对于基准 URLhttps://example.com,https://wow.an.example.com和https://example.com都会被匹配。
这意味着甚至可以不带任何参数调用enqueueLinks()。默认情况下,它会遍历当前页面找到的所有链接,只过滤那些指向相同子域的链接。
在仓库中,策略枚举定义于 url.ts,其注释精确解释了 Domain / Hostname / Origin 的区别:crawlee.dev是https://example.crawlee.dev/的 domain,example.crawlee.dev是 hostname,https://example.crawlee.dev是 origin。策略的实际过滤逻辑位于 enqueue_links.ts,其中SameOrigin与SameHostname行为一致但会尊重 URL 的协议。
此外,还可以通过 glob 指定 URL 应匹配的模式:
const crawler = new PlaywrightCrawler({ async requestHandler({ enqueueLinks }) { await enqueueLinks({ globs: ['https://crawlee.dev/*/*'], // 也可以使用 `regexps` 和 `pseudoUrls` 键 }); }, });隐式RequestQueue实例
所有爬虫现在都可以通过crawler.getRequestQueue()方法自动获得RequestQueue实例。如果实例尚不存在,它会为你创建。这意味着不再需要手动创建RequestQueue,可以直接使用下面描述的crawler.addRequests()方法。
仍然可以显式创建
RequestQueue,crawler.getRequestQueue()方法会尊重通过爬虫选项传入的实例并返回它。
在 basic-crawler.ts 中可以看到getRequestQueue()的实现,它返回IRequestManager,在内部会根据配置懒加载创建请求管理器。
crawler.addRequests():批量添加请求
现在可以批量添加多个请求。新增的addRequests方法会处理一切:它先入队前 1000 个请求并立即 resolve,同时在后台继续处理剩余部分(同样是每批 1000 个),这样就不会触发任何 API 速率限制。这意味着爬取几乎会立即开始(最多几秒内),这在以前只有组合使用RequestQueue和RequestList才能做到。
// 在前 1000 个请求添加完成后立即 resolve const result = await crawler.addRequests([/* 很多请求,甚至可以是数百万个 */]); // 如果想等待所有请求都添加完成,可以 await `waitForAllRequestsToBeAdded` promise await result.waitForAllRequestsToBeAdded;从源码实现(basic-crawler.ts)看,addRequests还支持label/userData等选项应用到整批请求,并可通过include/exclude模式与strategy(默认EnqueueStrategy.All)结合过滤 URL——这与enqueueLinks的过滤语义保持一致。
更简洁的错误日志
v2 中,请求处理器内部抛出的错误会记录完整的错误对象。在 Crawlee 中,只要我们知道请求会被重试,就只把错误消息记录为警告。如果想像 v2 一样启用详细日志,请使用CRAWLEE_VERBOSE_LOG环境变量。
Request.label快捷方式
给请求打标签以前是通过Request.userData对象实现的。在 Crawlee 中,还可以使用Request.label快捷方式。它实现为一对get/set访问器,读写Request.userData中的值。enqueueLinks的选项接口也支持该快捷方式。
在 request.ts 中可以看到它的实现:get label()直接返回this.userData.label,set label(value)将其写回userData——因此它与旧写法完全兼容。
async requestHandler({ request, enqueueLinks }) { if (request.label !== 'DETAIL') { await enqueueLinks({ globs: ['...'], label: 'DETAIL', }); } }移除requestAsBrowser
v1 中,requestAsBrowser的底层实现被替换为对got-scraping的代理调用(got-scraping是got的自定义扩展,尽可能模拟真实浏览器)。v3 中移除了requestAsBrowser,鼓励直接使用got-scraping。
为了方便迁移,还新增了context.sendRequest()辅助方法,它允许通过got-scraping处理上下文绑定的Request对象:
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 指南。
被移除的选项
useInsecureHttpParser:已移除,永久设置为true,以更好地模拟浏览器行为。useHttp2:已移除。Got Scraping 会自动进行协议协商,且useHttp2已内置为true——如今 100% 的浏览器都支持 HTTP/2 请求,Web 上也有越来越多的站点在使用它。
被重命名的选项
在requestAsBrowser方案中,部分选项的命名不同。下面是重命名清单:
payload
该选项表示要发送的请求体,可以是string或Buffer。但现在已经没有payload选项了,需要使用body;如果要发送 JSON,则用json:
// 之前: await Apify.utils.requestAsBrowser({ …, payload: 'Hello, world!' }); await Apify.utils.requestAsBrowser({ …, payload: Buffer.from('c0ffe', 'hex') }); await Apify.utils.requestAsBrowser({ …, json: { hello: 'world' } }); // 之后: await gotScraping({ …, body: 'Hello, world!' }); await gotScraping({ …, body: Buffer.from('c0ffe', 'hex') }); await gotScraping({ …, json: { hello: 'world' } });ignoreSslErrors
重命名为https.rejectUnauthorized。默认设置为false(方便使用)。如果希望确保连接安全,可以这样做:
// 之前: await Apify.utils.requestAsBrowser({ …, ignoreSslErrors: false }); // 之后: await gotScraping({ …, https: { rejectUnauthorized: true } });注意:两者的语义是相反的,因此取值也需要反转。
header-generator选项
useMobileVersion、languageCode和countryCode不再存在,需要直接使用headerGeneratorOptions:
// 之前: await Apify.utils.requestAsBrowser({ …, useMobileVersion: true, languageCode: 'en', countryCode: 'US', }); // 之后: await gotScraping({ …, headerGeneratorOptions: { devices: ['mobile'], // 或 ['desktop'] locales: ['en-US'], }, });timeoutSecs
设置超时请使用timeout.request(现在是毫秒):
// 之前: await Apify.utils.requestAsBrowser({ …, timeoutSecs: 30, }); // 之后: await gotScraping({ …, timeout: { request: 30 * 1000, }, });throwOnHttpErrors
throwOnHttpErrors→throwHttpErrors。该选项在遇到不成功的 HTTP 状态码(例如404)时抛出错误。默认值为false。
decodeBody
decodeBody→decompress。该选项对响应体进行解压。默认为true——请不要更改它,否则网站会出错(除非你知道自己在做什么!)。
abortFunction
这个函数曾经在特定响应上返回true时让 promise 抛出错误,但它并没有那么有用。
更合适的做法是取消请求:
const promise = gotScraping(…); promise.on('request', request => { // 请注意这不是 Got 的 Request 实例,而是 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 起不再允许这样做,创建此类浏览器池会抛出错误(期望所有使用的插件都是同一类型)。
以示例说明:这一变更禁止混用 Puppeteer 与 Playwright。但你仍然可以创建使用多个 Playwright 插件、各自使用不同启动器的池!
这一限制在仓库的测试中也有体现,例如 no-hybrid-plugins.test.ts 专门覆盖了"禁止混用插件类型"的行为。
在浏览器之外处理请求
一个小而值得一提的特性是:可以用浏览器爬虫在浏览器之外处理请求。为此,可以组合使用Request.skipNavigation和context.sendRequest()。
可参考 跳过特定请求的导航 示例了解具体实现方式。
日志(Logging)
Crawlee 直接以命名导出的方式导出默认的log实例。爬取上下文中也提供了一个带作用域的log实例——它记录的消息会带有爬虫名称前缀,应优先用于请求处理器内部的日志记录。
const crawler = new CheerioCrawler({ async requestHandler({ log, request }) { log.info(`Opened ${request.loadedUrl}`); }, });自动保存的爬虫状态
每个爬虫实例现在都有useState()方法,返回一个可用的状态对象。当persistState事件发生时,它会自动保存。该值是缓存的,因此可以随意多次调用此方法并获得完全相同的引用。也无需担心保存问题,因为它是自动发生的。
const crawler = new CheerioCrawler({ async requestHandler({ crawler }) { const state = await crawler.useState({ foo: [] as number[] }); // 直接改值,无需关心保存 state.foo.push(123); }, });从源码看(basic-crawler.ts),useState()通过KeyValueStore.getAutoSavedValue()实现,默认状态键为CRAWLEE_STATE。值得注意的是:如果多个未显式设置id的爬虫实例都调用useState(),它们会共享同一个状态对象——源码会发出警告并建议为每个爬虫实例提供唯一的id选项(例如new BasicCrawler({ id: "my-crawler-1", ... }))。
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.init(),在结尾调用Actor.exit()(并将用户函数包裹在 try/catch 中)。所有这些方法都是异步的,应当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(),确保进程不会因某种原因无限挂起。
事件(Events)
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)的快捷方式,同时会考虑 env vars 中的 token;Apify.callTask()现在只是运行ApifyClient.task(taskId).call(input, options)的快捷方式,同时会考虑 env vars 中的 token;Apify.metamorph()现在只是运行ApifyClient.task(taskId).metamorph(input, options)的快捷方式,同时会考虑 env vars 中的ACTOR_RUN_ID;Apify.waitForRunToFinish()已移除,请改用ApifyClient.waitForFinish();Actor.main/init默认清理存储;- 移除了
purgeLocalStorage辅助方法,清理逻辑移入存储类本身:StorageClient接口现在有可选的purge方法;- 清理通过
Actor.init()自动发生(可以在init/main方法的选项中通过purge: false选择退出);
QueueOperationInfo.request不再可用;Request.handledAt现在是 ISO 格式的字符串日期;Request.inProgress和Request.reclaimed现在是Set而不是 POJO;- puppeteer utils 中的
injectUnderscore已移除; APIFY_MEMORY_MBYTES不再被考虑,请改用CRAWLEE_AVAILABLE_MEMORY_RATIO;- 部分
AutoscaledPool选项不再可用:cpuSnapshotIntervalSecs和memorySnapshotIntervalSecs被顶层配置systemInfoIntervalMillis取代;maxUsedCpuRatio移至顶层配置;
ProxyConfiguration.newUrlFunction可以是异步的,.newUrl()和.newProxyInfo()现在返回 promise;prepareRequestFunction和postResponseFunction选项已移除,请改用导航钩子(navigation hooks);gotoFunction和gotoTimeoutSecs已移除;- 移除了对旧/损坏请求队列(其
Request属性为 null)的兼容性修复; fingerprintsOptions重命名为fingerprintOptions(fingerprints→fingerprint);fingerprintOptions现在接受useFingerprintCache和fingerprintCacheSize(取代了不再可用的useFingerprintPerProxyCache和fingerprintPerProxyCacheSize)。这是因为缓存的指纹不再与代理 URL 关联,而是与会话关联。
迁移检查清单
综合以上全部变更,从 Apify SDK v2 迁移到 Crawlee v3 时,建议按以下顺序逐项核对:
- 替换导入:将
apify包中的爬虫相关导入改为crawlee(或对应@crawlee/*包),平台相关代码保留在apify的Actor中; - 更新爬虫选项:将
handleRequestFunction/handlePageFunction改为requestHandler,handleFailedRequestFunction改为failedRequestHandler,超时选项改为requestHandlerTimeoutSecs/navigationTimeoutSecs; - 更新类型名:将
CheerioHandlePageInputs等改为对应的*CrawlingContext; - 利用上下文辅助方法:删除
enqueueLinks调用中手动传入的requestQueue/page/$参数,改用三种EnqueueStrategy过滤;用crawler.getRequestQueue()取代手动创建队列; - 批量入队:用
crawler.addRequests()代替手动RequestQueue.addRequest循环,并按需await result.waitForAllRequestsToBeAdded; - 替换
requestAsBrowser:改用got-scraping或上下文中的sendRequest(),并按映射表迁移payload、ignoreSslErrors、timeoutSecs、throwOnHttpErrors、decodeBody、abortFunction及 header-generator 选项; - 迁移存储与会话 API:确认默认内存存储的行为、
Actor.init/main的自动清理语义,将session.getPuppeteerCookies()改为session.getCookies(); - 启用 TypeScript:使用
@apify/tsconfig并设置module/target为ES2022+,配合多阶段 Docker 构建。
相关阅读
- Apify 平台部署指南:了解
Actor.init/Actor.main在平台上的完整用法; - Got Scraping 指南:
sendRequest()与got-scraping的深入实践; - 跳过导航示例:
Request.skipNavigation+sendRequest()组合实战; - 会话管理指南:
session.getCookies()/setCookies()的完整使用场景; - 升级到 v4:了解后续版本的进一步演进。
【免费下载链接】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),仅供参考