)
Crawlee 历史演进Apify SDK v1 升级完全指南Crawling Context、BrowserPool 与 LaunchContext 迁移【免费下载链接】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本文基于当前仓库中存档的历史迁移文档 version-3.10 升级指南 编写对应 Apify SDK 从 v0.x 跨入 v1.0.0 时代的大版本升级。指南详细拆解了本次升级的两大核心目标——稳定性承诺与多浏览器支持Firefox / WebKit并逐一给出 Installation、Crawling Context、BrowserPool、PuppeteerCrawlerOptions 与 Launch 函数的迁移示例。读完本文你将掌握从旧版PuppeteerPool生态迁移到BrowserPoolLaunchContext生态的全部破坏性变更点并能结合当前仓库源码理解这些 API 的底层设计逻辑。升级背景为什么需要 v1.0.0经过 3.5 年的快速迭代Apify SDK 积累了大量的破坏性变更与弃用 APIv1.0.0 正是对这些变更的集中收束。本次发布有两个明确目标稳定性StabilitySDK 已被大量爬虫与自动化项目采用官方承诺在 v1 之后每年只通过一次新的 major 版本引入破坏性变更为开发者提供稳定的工作环境。更多浏览器支持通过引入 Playwright将浏览器自动化能力从仅 Puppeteer 扩展到 Chromium、Firefox、WebKitSafari。实现多浏览器支持的底层手段是用全新的browser-pool库取代旧的PuppeteerPool。BrowserPool继承了PuppeteerPool的核心思想并抽象出统一的浏览器管理接口Puppeteer 用户仍然可以继续使用Playwright 则作为新的平级选项加入。另一个重要的破坏性变更SDK v1 不再内置puppeteer或playwright。用户需要自行安装所选模块与版本换来的是安装体积更小、选择更自由也为未来接入更多自动化库留出空间。Playwright 的加入带来了全新的PlaywrightCrawler它与PuppeteerCrawler高度相似可按偏好选择。相应地launchPuppeteerFunction选项被移除launchPuppeteerOptions被launchContext取代handlePageFunction的参数结构也发生了变化——这些正是本文迁移指南的重点。注v1 是历史版本节点。当前仓库为 Crawleev3/v4 时代BasicCrawler.crawlingContexts映射等 v1 特性在后来的版本中已被重构详见仓库根目录的 MIGRATIONS.md 与 docs/upgrading 系列文档。本文按 v1 迁移文档原貌展开并在涉及处补充当前仓库源码中的对应实现作为对照。安装方式的变化旧版 SDK 捆绑了puppeteer包用户无需单独安装。v1 同时支持 Playwright为了不强迫用户同时安装两个库必须显式声明依赖# 使用 Puppeteer与旧版本行为一致 npm install apify puppeteer # 使用 Playwright npm install apify playwright官方在 v1 首发时提示大部分核心功能已就绪但仍可能存在部分工具函数或选项仅 Puppeteer 支持、尚未覆盖 Playwright 的情况迁移时需留意。从当前仓库的实现看不捆绑自动化库这一设计被完整保留browser-pool的 BrowserPlugin 明确指出 BrowserPlugindoes not include the library. You can choose any version or fork of the library. It also keepsbrowser-poolinstallation small.即自动化库本身由使用者注入browser-pool只定义launch(opts)、newPage(...)等最小公共接口CommonLibrary/CommonBrowser/CommonPage。在 Apify Platform 上运行若要在 Apify Platform 上使用 Playwright必须选用支持 Playwright 的 Docker 镜像。官方已准备好对应的镜像清单可前往 Docker 镜像指南挑选合适的镜像对应当前仓库中的 docs/deployment/docker_images.mdx 及 docker_browser_js.txt 等部署资源。同时有一条硬性要求package.json中必须显式列出puppeteer和/或playwright作为 dependencies。如果未列出构建 Actor 时这些库会被从node_modules中卸载导致运行时找不到模块。处理器参数统一为 Crawling Context旧版中用户提供的各个处理器函数如handlePageFunction、handleFailedRequestFunction收到的参数是各自独立创建的对象导致跨函数调用时无法追踪共享值const handlePageFunction async (args1) { args1.hasOwnProperty(proxyInfo) // true } const handleFailedRequestFunction async (args2) { args2.hasOwnProperty(proxyInfo) // false } args1 args2 // falsev1 引入单一对象 Crawling Context所有处理器共享同一个上下文实例字段完全一致const handlePageFunction async (crawlingContext1) { crawlingContext1.hasOwnProperty(proxyInfo) // true } const handleFailedRequestFunction async (crawlingContext2) { crawlingContext2.hasOwnProperty(proxyInfo) // true } // 所有上下文是同一个对象 crawlingContext1 crawlingContext2 // true上下文 Mapcrawler.crawlingContexts与上下文 ID既然所有上下文都是同一对象就可以用新的crawlingContext.id属性追踪所有运行中的上下文实现跨上下文访问——例如在子页面中操作主页面let masterContextId; const handlePageFunction async ({ id, page, request, crawler }) { if (request.userData.masterPage) { masterContextId id; // 准备主页面 } else { const masterContext crawler.crawlingContexts.get(masterContextId); const masterPage masterContext.page; const masterRequest masterContext.request; // 现在可以在另一个 handlePageFunction 中操作主页面数据 } }版本提示crawler.crawlingContexts是 v1 时代的跨上下文通道。在后续大版本中该映射已调整——docs/upgrading/upgrading_v4.md 明确记录 The protectedBasicCrawler.crawlingContextsmap is removed。当前 Crawlee 中跨上下文协作主要通过共享存储Dataset / KeyValueStore或自定义上下文扩展实现。autoscaledPool移入crawlingContext.crawler为避免上下文对象臃肿、同时方便访问关键对象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 // 正确用法 }用BrowserPool取代PuppeteerPoolBrowserPool在PuppeteerPool的基础上扩展出管理多种浏览器自动化库的能力API 相似但不完全相同。当前仓库中该库位于 packages/browser-pool核心类即 browser-pool.ts 中的BrowserPool。访问运行中的BrowserPool只有PuppeteerCrawler与PlaywrightCrawler使用BrowserPool可通过crawler对象访问const crawler new Apify.PlaywrightCrawler({ handlePageFunction: async ({ page, crawler }) { crawler.browserPool // ----- } }); crawler.browserPool // -----页面拥有 ID页面 ID 与crawlingContext.id相等这让生命周期钩子能通过页面 ID 反查完整上下文const pageId browserPool.getPageId配置与生命周期钩子BrowserPool最重要的新增能力是生命周期钩子lifecycle hooks通过browserPoolOptions在两类 Crawler 中配置。从源码看钩子体系覆盖浏览器与页面的完整生命周期钩子调用时机参数preLaunchHooks浏览器启动前可动态修改启动选项(pageId, launchContext)postLaunchHooks浏览器启动后(pageId, browserController)prePageCreateHooks新页面创建前(pageId, browserController, pageOptions)postPageCreateHooks新页面创建完成、内部动作结束后(page, browserController)prePageCloseHooks页面即将关闭前可保存快照/更新状态(page, browserController)postPageCloseHooks页面关闭后用于清理与日志(pageId, browserController)对应接口定义见 browser-pool.ts 的BrowserPoolHooks。钩子示例——按请求需求动态切换有头模式并通过crawler.crawlingContexts.get(pageId)取回触发该浏览器启动的请求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; } } ] } })源码中retireBrowserAfterPageCount的默认值为100见 browser-pool.ts 第 36 行即浏览器累计处理页面数达到阈值后即被回收用于防止内存膨胀在 browser-pool.ts 第 636 行 的页面关闭逻辑中会检查browserController.totalPages this.retireBrowserAfterPageCount触发浏览器退役。引入BrowserControllerBrowserController是browser-pool中负责浏览器管理的类目的是为 Puppeteer 与 Playwright 提供统一的浏览器操作 API。它平时在后台自动工作当你需要正确关闭浏览器时应通过browserController而非直接操作 pageconst handlePageFunction async ({ page, browserController }) { // 错误用法绕过 BrowserPool可能产生反效果 await page.browser().close(); // 正确用法允许优雅关闭 await browserController.close(); const cookies [/* 一些 cookie 对象 */]; // 错误用法仅 Puppeteer 可用Playwright 不兼容 await page.setCookies(...cookies); // 正确用法两种库都适用 await browserController.setCookies(page, cookies); }BrowserController还携带浏览器的关键信息例如启动时所用的上下文——这在 v1 之前很难获取const handlePageFunction async ({ browserController }) { // 浏览器使用的代理信息 browserController.launchContext.proxyInfo // 浏览器使用的会话 browserController.launchContext.session }launchContext的底层结构可在当前仓库的 launch-context.ts 中查看它记录了id等于触发启动的页面 ID、browserPlugin、launchOptions、useIncognitoPages、userDataDir、proxyUrl、browserPerProxy、ignoreProxyCertificate等字段并提供extend(fields)方法用于安全地附加自定义状态保留字段会被拒绝覆盖。其中proxyUrl的 setter 会标准化 URL去除 pathname/search/hash注释还引用了 Chromium 网络设置文档。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保持一致选项结构做了系统更新。移除gotoFunction可配置的gotoFunction概念并不理想——尤其是 SDK 内部使用了改造过的gotoExtended用户在覆写gotoFunction时若想扩展默认行为必须了解这些内部实现细节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, gotoOptions)postNavigationHooks只接收crawlingContext。const preNavigationHooks [ async ({ page }) makePageStealthy(page) ]; const postNavigationHooks [ async ({ page }) page.evaluate(() { window.foo bar }) ] const crawler new Apify.PuppeteerCrawler({ preNavigationHooks, postNavigationHooks, // ... })当前仓库 browser-crawler.ts 完整继承了这一设计导航阶段被组织为prepareNavigation → preNavigationHooks → navigate → postNavigationHooks → finalizeNavigation的 pipeline见 第 491-506 行且文档明确指出 a slow hook eats into the same budget——导航超时navigationTimeoutSecs由预处理、导航与后处理钩子共享同一时间窗口这一点在 v1 迁移后依然成立可帮助你在调试慢钩子导致的TimeoutError时快速定位。launchPuppeteerOptions→launchContext旧选项总是让人困惑因为它把 Apify 自定义选项与 Puppeteer 的launchOptions混在一个对象里const launchPuppeteerOptions { useChrome: true, // Apify 选项 headless: false, // Puppeteer 选项 }新的launchContext显式界定launchOptions子对象launchPuppeteerOptions被移除const crawler new Apify.PuppeteerCrawler({ launchContext: { useChrome: true, // Apify 选项 launchOptions: { headless: false // Puppeteer 选项 } } })LaunchContext本身也是browser-pool的类型见 launch-context.ts结构完全一致SDK 只是在上面追加额外选项。移除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钩子还能访问触发启动请求的上下文const preLaunchHooks [ async function maybeLaunchChrome(pageId, launchContext) { const { request } crawler.crawlingContexts.get(pageId); if (request.userData.useHeadful true) { launchContext.launchOptions.headless false; } } ]Launch 函数launchPuppeteer与launchPlaywright除Apify.launchPuppeteer()外v1 新增Apify.launchPlaywright()。参数更新沿用前面launchContext的规范启动选项对象被重构// 旧 await Apify.launchPuppeteer({ useChrome: true, headless: true, }) // 新 await Apify.launchPuppeteer({ useChrome: true, launchOptions: { headless: true, } })自定义模块launcher选项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的设计同样延续到了当前仓库browser-pool的BrowserPlugin支持传入任意launch(opts)实现例如new PlaywrightPlugin(playwright.chromium)见 browser-pool.ts 的类注释示例你甚至可以传入库的 fork 版本。迁移自查清单完成从 SDK v0 到 v1 的迁移后可按以下清单快速自查依赖声明package.json已显式包含puppeteer或playwrightApify Platform 构建不会卸载它们。处理器参数handlePageFunction/handleFailedRequestFunction等统一使用crawlingContext单一对象跨上下文协作改用crawlingContext.idcrawler.crawlingContextsv1 语义。启动选项launchPuppeteerOptions已改写为launchContext.launchOptionslaunchPuppeteerFunction已改写为browserPoolOptions.preLaunchHooks。导航定制gotoFunction已拆分到preNavigationHooks可修改gotoOptions与postNavigationHooks。浏览器管理回收/关闭浏览器统一走browserController与browserPool.retireBrowserByPage(page)不再直接操作page.browser()。Playwright 起步Apify.launchPlaywright({ launcher: playwright.chromium })或直接使用PlaywrightCrawler。当前仓库的 MIGRATIONS.md 与 docs/upgrading 目录完整记录了从 v1 一路到 v4 的后续演进如crawlingContexts映射的移除、launchContext的持续扩展若你需要将老项目升级到现代 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创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考