
从 Apify SDK v0 升级到 v1Crawlee 前身的浏览器池与爬虫 API 演进指南【免费下载链接】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本指南面向需要维护或迁移基于 Apify SDK v0即当前 Crawlee 项目的前身编写的爬虫代码的开发者。文章以官网归档的 v1 升级指南 为主体骨架逐一梳理 v1.0.0 中引入的破坏性变更PuppeteerPool被browser-pool取代、处理器参数统一为 Crawling Context、launchPuppeteerOptions重构为launchContext、gotoFunction被导航钩子取代等并结合当前仓库源码说明这些设计如何在今天的 Crawlee 中落地。读完你将掌握把旧式handlePageFunction代码迁移到 Crawling Context、用生命周期钩子替代自定义启动函数、以及正确使用BrowserController进行浏览器生命周期管理的完整方法。版本背景为什么要发布 v1Apify SDK 在经历了三年半的快速迭代和大量破坏性变更后发布了 v1.0.0。这个版本有两个核心目标稳定性与支持更多浏览器。在 v0 时代SDK 只支持 Puppeteer浏览器实例由PuppeteerPool统一管理。v1 用新库browser-pool取代了PuppeteerPool在保留 Puppeteer 支持的同时新增了对 Playwright 的支持——后者与 Puppeteer 接口几乎一致但可以驱动 Firefox 和 WebKitSafari等浏览器并简化了一些常见操作。另一个重要变化是v1 不再捆绑puppeteer或playwright。用户必须自己安装并选择版本这让安装包更小、选择更自由也为未来支持更多自动化库留出了空间。官方承诺从 v1 起破坏性变更每年最多一次随新的大版本发布。对应到当前仓库这套设计理念在 v3 时代被完整继承并进一步拆分浏览器池独立为crawlee/browser-pool、浏览器爬虫抽象为crawlee/browser、具体的PuppeteerCrawler与PlaywrightCrawler则分别位于 puppeteer-crawler 与 playwright-crawler 包中。你可以对照 v3 升级指南 了解后续的拆分过程。安装方式的变化v0 版本的 SDK 直接捆绑了puppeteer用户无需单独安装。v1 起必须显式安装# 使用 Puppeteer与旧版本行为一致 npm install apify puppeteer # 使用 Playwright npm install apify playwright官方在 v1 发布时提示虽然已尽可能覆盖核心功能但仍有部分工具方法或选项只支持 Puppeteer 而不支持 Playwright迁移时需要注意。在 Apify Platform 上运行如果要在 Apify Platform 上使用 Playwright需要选用支持 Playwright 的 Docker 镜像详见当时的 Docker image guide。package.json必须将puppeteer和/或playwright列为依赖——否则构建 Actor 时这些库会被从node_modules中卸载。当前仓库中对应的部署文档位于 docs/deployment/apify_platform.mdx其中同样强调浏览器依赖需显式声明Docker 镜像的选择可参考 docs/guides/docker_images.mdx。处理器参数统一为 Crawling Contextv1 最核心的接口变更此前用户提供的处理器函数各自接收独立的对象作为参数导致跨函数追踪值非常困难const handlePageFunction async (args1) { args1.hasOwnProperty(proxyInfo) // true } const handleFailedRequestFunction async (args2) { args2.hasOwnProperty(proxyInfo) // false } args1 args2 // false原因在于每次调用都会创建一个全新的参数对象。v1 将之统一为单一的Crawling Contextconst handlePageFunction async (crawlingContext1) { crawlingContext1.hasOwnProperty(proxyInfo) // true } const handleFailedRequestFunction async (crawlingContext2) { crawlingContext2.hasOwnProperty(proxyInfo) // true } // 所有 Context 都是同一个对象 crawlingContext1 crawlingContext2 // true用id管理运行中的 Context既然所有对象统一了SDK 就能用新增的crawlingContext.id属性跟踪所有运行中的 Context实现跨 Context 的数据访问。这在大页面master page协作场景中非常实用let masterContextId; const handlePageFunction async ({ id, page, request, crawler }) { if (request.userData.masterPage) { masterContextId id; // 准备 master 页面 } else { const masterContext crawler.crawlingContexts.get(masterContextId); const masterPage masterContext.page; const masterRequest masterContext.request; // 现在可以在另一个 handlePageFunction 中操作 master 数据 } }autoscaledPool移入crawlingContext.crawler为减轻 Context 负担、方便访问关键对象v1 在处理器参数上暴露了crawler属性const handlePageFunction async ({ request, page, crawler }) { await crawler.requestQueue.addRequest({ url: https://example.com }); await crawler.autoscaledPool.pause(); }这意味着puppeteerPool、autoscaledPool这类快捷方式不再需要const handlePageFunction async (crawlingContext) { crawlingContext.autoscaledPool // 已不存在 crawlingContext.crawler.autoscaledPool // 这才是正确用法 }仓库印证在今天的 Crawlee 中BrowserCrawlingContext定义于 packages/browser-crawler/src/internals/browser-crawler.ts#L86-L122除了page、request、response外还包含gotoOptions、extractLinks、enqueueLinks等 Context 感知辅助方法而crawler.crawlingContexts的跨 Context 访问能力对应 v1 指南中的Map用法至今仍是浏览器爬虫实现的核心设施。PuppeteerPool被BrowserPool取代BrowserPool的设计目标是扩展PuppeteerPool使其能管理多种浏览器自动化库。API 相似但不完全相同。访问运行中的 BrowserPool只有PuppeteerCrawler和PlaywrightCrawler使用BrowserPool可通过crawler对象访问const crawler new Apify.PlaywrightCrawler({ handlePageFunction: async ({ page, crawler }) { crawler.browserPool // ----- } }); crawler.browserPool // -----页面有了 ID页面的 ID 与crawlingContext.id相等因此可以在钩子中通过 ID 取回完整的 Crawling Context。生命周期钩子Lifecycle HooksBrowserPool最重要的新增功能是生命周期钩子通过两个爬虫的browserPoolOptions配置const crawler new Apify.PuppeteerCrawler({ browserPoolOptions: { retireBrowserAfterPageCount: 10, preLaunchHooks: [ async (pageId, launchContext) { const { request } crawler.crawlingContexts.get(pageId); if (request.userData.useHeadful true) { launchContext.launchOptions.headless false; } } ] } })仓库印证BrowserPool的完整选项与钩子类型定义在 packages/browser-pool/src/browser-pool.ts#L82-L138并附带了默认值方便迁移时对照选项说明默认值maxOpenPagesPerBrowser单个浏览器同时打开的最大页面数超出后启动新浏览器20retireBrowserAfterPageCount浏览器处理页面数达到该值后自动退役并关闭由新浏览器接替100operationTimeoutSecs浏览器启动、开页等异步操作的最大等待秒数防止卡死15closeInactiveBrowserAfterSecs定期强制关闭非活动浏览器的间隔秒数300retireInactiveBrowserAfterSecs非活动浏览器被标记退役的检查间隔秒数10preLaunchHooks/postLaunchHooks浏览器启动前/后的钩子[]prePageCreateHooks/postPageCreateHooks页面创建前/后的钩子[]prePageCloseHooks/postPageCloseHooks页面关闭前/后的钩子[]useFingerprints是否注入生成的浏览器指纹truefingerprintOptions指纹生成器与虚拟会话缓存配置{}各钩子签名如下均定义于 packages/browser-pool/src/browser-pool.tspreLaunchHooks(pageId, launchContext)浏览器启动前执行适合动态修改启动选项postLaunchHooks(pageId, browserController)浏览器启动后立即执行prePageCreateHooks(pageId, browserController, pageOptions)创建新页面前执行pageOptions目前仅 Playwright 的隐身上下文支持postPageCreateHooks(pageId, browserController, page)页面创建并完成内部处理后执行。引入BrowserControllerBrowserController是browser-pool中负责浏览器管理的类为 Puppeteer 与 Playwright 提供统一 API。它自动在后台工作需要优雅关闭浏览器时应使用browserController而不是直接操作页面对象const handlePageFunction async ({ page, browserController }) { // 错误用法绕过了 BrowserPool可能引发问题 await page.browser().close(); // 正确用法优雅关闭 await browserController.close(); const cookies [/* 一些 cookie 对象 */]; // 错误用法只适用于 PuppeteerPlaywright 不兼容 await page.setCookies(...cookies); // 正确用法两种浏览器都兼容 await browserController.setCookies(page, cookies); }BrowserController还携带浏览器的重要信息比如启动时的上下文这在 v1 之前很难获得const handlePageFunction async ({ browserController }) { // 浏览器使用的代理信息 browserController.launchContext.proxyInfo // 浏览器使用的会话 browserController.launchContext.session }仓库印证当前 packages/browser-pool/src/abstract-classes/browser-controller.ts 中BrowserController仍保持同样的设计launchContext属性、close()、setCookies(page, cookies)与getCookies(page)方法分别定义在文件的第 59、71、76、82 行附近launchContext本体则由 packages/browser-pool/src/launch-context.ts 的LaunchContext类承载它暴露launchOptions、proxyUrl、useIncognitoPages、userDataDir等字段并提供一个extend(fields)方法用于附加浏览器级自定义状态如会话 ID且会保护内部保留字段不被覆盖。BrowserPool与PuppeteerPool的方法对照部分方法被移除延续更早的弃用节奏部分有所调整// 旧 await puppeteerPool.recyclePage(page); // 新 await page.close();// 旧 await puppeteerPool.retire(page.browser()); // 新 browserPool.retireBrowserByPage(page);// 旧 await puppeteerPool.serveLiveViewSnapshot(); // 新 // BrowserPool 中已没有 LiveView更新的PuppeteerCrawlerOptions为了让PuppeteerCrawler与PlaywrightCrawler保持一致v1 更新了两者的选项。移除gotoFunction改用导航钩子可配置的gotoFunction概念并不理想SDK 内部使用修改过的gotoExtended用户想扩展默认行为就必须了解其内部细节。看这个例子const gotoFunction async ({ request, page }) { // 预处理 await makePageStealthy(page); // 必须记得如何调用内部方法 const response await gotoExtended(page, request, {/* 必须记得默认值 */}); // 后处理 await page.evaluate(() { window.foo bar; }); // 绝不能忘记 return response; } const crawler new Apify.PuppeteerCrawler({ gotoFunction, // ... })v1 用preNavigationHooks与postNavigationHooks替代。preNavigationHooks接收两个参数crawlingContext与gotoOptionspostNavigationHooks只接收crawlingContextconst preNavigationHooks [ async ({ page }) makePageStealthy(page) ]; const postNavigationHooks [ async ({ page }) page.evaluate(() { window.foo bar }) ] const crawler new Apify.PuppeteerCrawler({ preNavigationHooks, postNavigationHooks, // ... })仓库印证这一设计延续至今。当前 packages/browser-crawler/src/internals/browser-crawler.ts 中BrowserCrawlingContext仍带有gotoOptions字段注释明确说明preNavigationHooks可以修改该对象或返回{ gotoOptions: ... }来影响导航行为v3升级指南website/versioned_docs/version-3.16/upgrading/upgrading_v3.md中也再次确认gotoFunction与gotoTimeoutSecs已被彻底移除导航钩子是唯一途径。launchPuppeteerOptions→launchContext旧选项一直令人困惑因为它把 Apify 自定义选项与 Puppeteer 的launchOptions混在一起const launchPuppeteerOptions { useChrome: true, // Apify 选项 headless: false, // Puppeteer 选项 }新方案使用显式区分launchOptions的launchContext对象launchPuppeteerOptions已被移除const crawler new Apify.PuppeteerCrawler({ launchContext: { useChrome: true, // Apify 选项 launchOptions: { headless: false // Puppeteer 选项 } } })LaunchContext也是browser-pool的类型结构完全相同SDK 只是额外增加了部分选项。仓库印证v1 的launchContext正是当前BrowserLaunchContext的雏形。在 packages/browser-crawler/src/internals/browser-launcher.ts#L25-L93 中BrowserLaunchContext在launchOptions之外还定义了proxyUrl须包含端口可带用户名密码、useChrome默认false为true时从CRAWLEE_CHROME_EXECUTABLE_PATH环境变量或系统典型路径加载完整版 Chrome、browserPerProxy、useIncognitoPages默认false、userDataDir、userAgent、ignoreProxyCertificate与launcher默认chromium可指定firefox、webkit等选项BrowserLauncher的createLaunchOptions()方法同文件第 262-286 行还会自动补充默认视口1366x768、无沙箱参数、默认 headless 策略与 Chrome 可执行路径可直接对照迁移。移除launchPuppeteerFunctionbrowser-pool引入生命周期钩子后自定义启动逻辑不必再包一层函数。旧写法const launchPuppeteerFunction async (launchPuppeteerOptions) { if (someVariable chrome) { launchPuppeteerOptions.useChrome true; } return Apify.launchPuppeteer(launchPuppeteerOptions); } const crawler new Apify.PuppeteerCrawler({ launchPuppeteerFunction, // ... })新的preLaunchHook写法const maybeLaunchChrome (pageId, launchContext) { if (someVariable chrome) { launchContext.useChrome true; } } const crawler new Apify.PuppeteerCrawler({ browserPoolOptions: { preLaunchHooks: [maybeLaunchChrome] }, // ... })这种方式更好它对 Puppeteer 和 Playwright 完全一致还能方便地组合预定义行为const preLaunchHooks [ maybeLaunchChrome, useHeadfulIfNeeded, injectNewFingerprint, ]配合新增的crawler.crawlingContexts钩子还能拿到触发启动的那个request的 Crawling Contextconst preLaunchHooks [ async function maybeLaunchChrome(pageId, launchContext) { const { request } crawler.crawlingContexts.get(pageId); if (request.userData.useHeadful true) { launchContext.launchOptions.headless false; } } ]启动函数Launch Functions除Apify.launchPuppeteer()外v1 新增了Apify.launchPlaywright()。更新后的参数启动选项也遵循launchContext的新结构// 旧 await Apify.launchPuppeteer({ useChrome: true, headless: true, }) // 新 await Apify.launchPuppeteer({ useChrome: true, launchOptions: { headless: true, } })自定义模块Apify.launchPuppeteer原本支持puppeteerModule选项引入 Playwright 后选项统一命名为launcher——因为playwright模块本身并不直接启动浏览器需要指定具体的浏览器产品const puppeteer require(puppeteer); const playwright require(playwright); await Apify.launchPuppeteer(); // 等价于 await Apify.launchPuppeteer({ launcher: puppeteer }) await Apify.launchPlaywright(); // 等价于 await Apify.launchPlaywright({ launcher: playwright.chromium })仓库印证在今天的仓库中launcher仍是BrowserLaunchContext的正式字段见 packages/browser-crawler/src/internals/browser-launcher.ts#L92且BrowserLauncher.requireLauncherOrThrow()同文件第 147-163 行会在模块缺失时给出明确提示先检查package.json依赖是否声明了对应库若在 Apify Platform 上则提醒必须使用对应的 Docker 镜像——这正是 v1 安装章节强调的“依赖必须显式声明”在源码层的落实。迁移检查清单综合 v1 指南与后续演进从 Apify SDK v0 迁移到 v1 时建议逐项核对依赖在package.json中显式添加puppeteer或playwright并执行npm install apify puppeteer或apify playwright处理器签名把handlePageFunction/handleFailedRequestFunction的参数改为统一的 Crawling Context 对象并通过解构获取{ request, page, crawler }等字段池与浏览器管理将puppeteerPool.recyclePage(page)改为page.close()将puppeteerPool.retire(browser)改为browserPool.retireBrowserByPage(page)浏览器关闭与 Cookie 操作一律经由browserController启动配置launchPuppeteerOptions拆分为launchContextlaunchContext.launchOptionslaunchPuppeteerFunction改写为browserPoolOptions.preLaunchHooks导航定制gotoFunction拆分为preNavigationHooks可修改gotoOptions与postNavigationHooks仅接收crawlingContext部署在 Apify Platform 上选用支持对应浏览器的 Docker 镜像并确保依赖在构建时不被清理。需要了解 v1 之后的演进路线如 v2 移除LiveViewServer、v3 全面拆分crawlee/*包并引入requestHandler命名可继续阅读同目录下的 upgrading_v2.md 与 upgrading_v3.md以及当前仓库的 MIGRATIONS.md。【免费下载链接】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),仅供参考