mcpField
C
cloudflare-browser-rendering

cloudflare-browser-rendering

github.com/jezweb云与 DevOps1.1k341 次安装3 周前更新
代码仓库

关于 cloudflare-browser-rendering

为使用 Cloudflare Browser Rendering 的开发者提供浏览器自动化参考,涵盖 Puppeteer 与 Playwright 的 Worker 绑定、会话管理和常见问题处理。

主要功能

  1. 01Puppeteer 与 Playwright 用法
  2. 02浏览器会话复用与隔离
  3. 03页面截图与 PDF 生成
  4. 04动态网页抓取与交互
  5. 05常见错误与限制说明

适用场景

  1. 01为网页生成截图或 PDF
  2. 02抓取动态网页内容
  3. 03自动化表单和前端测试

MCP 相关

文档提及 Playwright MCP 支持及 Playwright MCP 服务端,但本技能主要介绍 Workers 绑定下的浏览器自动化。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jezweb/claude-skills --skill cloudflare-browser-rendering

安装后在 Claude Code 中输入/cloudflare-browser-rendering确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
.claude-plugin/
plugin.json
references/
common-errors.md
pricing-and-limits.md
puppeteer-vs-playwright.md
session-management.md
rules/
cloudflare-browser-rendering.md
scripts/
check-versions.sh
templates/
ai-enhanced-scraper.ts
basic-screenshot.ts
pdf-generation.ts
playwright-example.ts
screenshot-with-kv-cache.ts
session-reuse.ts
web-scraper-basic.ts
web-scraper-batch.ts

详细介绍

Cloudflare Browser Rendering - 完整参考

用于构建 Cloudflare Browser Rendering 浏览器自动化工作流的生产就绪知识领域。

状态:生产就绪 ✅ 最后更新:2026-01-21 依赖项:cloudflare-worker-base(用于 Worker 设置) 最新版本:@cloudflare/[email protected]、@cloudflare/[email protected]、[email protected]

近期更新(2025):

  • 2025 年 9 月:Playwright v1.55 正式发布,支持 Stagehand 框架(Workers AI),/links 新增 excludeExternalLinks 参数
  • 2025 年 8 月:计费正式发布(8 月 20 日),本地开发中新增 /sessions 端点,新增 X-Browser-Ms-Used 标头
  • 2025 年 7 月:Playwright v1.54.1 + MCP v0.0.30,支持 Playwright 本地开发([email protected]+),同步 Puppeteer v22.13.1,/content 返回标题,/json 新增 custom_ai 参数,/screenshot 默认视口为 1920x1080
  • 2025 年 6 月:自动包含 Web Bot Auth 标头
  • 2025 年 4 月:推出 Playwright 支持,引入免费套餐

目录

  1. 快速入门(5 分钟)
  2. Browser Rendering 概述
  3. Puppeteer API 参考
  4. Playwright API 参考
  5. 会话管理
  6. 常见模式
  7. 定价与限制
  8. 已知问题的预防
  9. 生产环境检查清单

快速入门(5 分钟)

1. 添加 Browser 绑定

wrangler.jsonc:

{
  "name": "browser-worker",
  "main": "src/index.ts",
  "compatibility_date": "2023-03-14",
  "compatibility_flags": ["nodejs_compat"],
  "browser": {
    "binding": "MYBROWSER"
  }
}

为什么需要 nodejs_compat? Browser Rendering 需要 Node.js API 和 polyfill。

2. 安装 Puppeteer
npm install @cloudflare/puppeteer
3. 截取第一张屏幕截图
import puppeteer from "@cloudflare/puppeteer";

interface Env {
  MYBROWSER: Fetcher;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const { searchParams } = new URL(request.url);
    const url = searchParams.get("url") || "https://example.com";

    // Launch browser
    const browser = await puppeteer.launch(env.MYBROWSER);
    const page = await browser.newPage();

    // Navigate and capture
    await page.goto(url);
    const screenshot = await page.screenshot();

    // Clean up
    await browser.close();

    return new Response(screenshot, {
      headers: { "content-type": "image/png" }
    });
  }
};
4. 部署
npx wrangler deploy

在以下地址测试:https://your-worker.workers.dev/?url=https://example.com

重要提示:

  • 始终将 env.MYBROWSER 传递给 puppeteer.launch()(不能是 undefined)
  • 完成后始终调用 browser.close()(或使用 browser.disconnect() 以复用会话)
  • 使用 nodejs_compat 兼容性标志

Browser Rendering 概述

什么是 Browser Rendering?

Cloudflare Browser Rendering 在 Cloudflare 的全球网络上提供无头 Chromium 浏览器。使用 Puppeteer 和 Playwright 等熟悉的工具自动执行浏览器任务:

  • 屏幕截图 - 捕获网页的视觉快照
  • PDF 生成 - 将 HTML/URL 转换为 PDF
  • 网页抓取 - 从动态网站提取内容
  • 测试 - 自动执行前端测试
  • 爬取 - 浏览多页面工作流
两种集成方式
方法最适合复杂度
Workers 绑定复杂自动化、自定义工作流、会话管理高级
REST API简单的屏幕截图/PDF 任务简单

本文介绍 Workers 绑定(使用完整 Puppeteer/Playwright API 的高级方法)。

Puppeteer 与 Playwright
特性PuppeteerPlaywright
API 熟悉度最受欢迎采用率不断增长
软件包@cloudflare/[email protected]@cloudflare/[email protected]
会话管理✅ 高级 API⚠️ 基础
浏览器支持仅 Chromium仅 Chromium(暂不支持 Firefox/Safari)
最适合屏幕截图、PDF、抓取测试、前端自动化

建议:大多数情况下使用 Puppeteer。如果你已经在使用 Playwright 进行测试,它会是理想选择。


Puppeteer API 参考

核心 API(完整参考:https://pptr.dev/api/):

全局函数:

  • puppeteer.launch(env.MYBROWSER, options?) - 启动新浏览器(重要:必须传入绑定)
  • puppeteer.connect(env.MYBROWSER, sessionId) - 连接到现有会话
  • puppeteer.sessions(env.MYBROWSER) - 列出正在运行的会话
  • puppeteer.history(env.MYBROWSER) - 列出最近的会话(已打开 + 已关闭)
  • puppeteer.limits(env.MYBROWSER) - 检查账户限制

浏览器方法:

  • browser.newPage() - 创建新标签页(优先于启动新浏览器)
  • browser.sessionId() - 获取会话 ID 以供复用
  • browser.close() - 终止会话
  • browser.disconnect() - 保持会话运行,以供复用
  • browser.createBrowserContext() - 创建隔离的无痕上下文(使用独立的 Cookie/缓存)

页面方法:

  • page.goto(url, { waitUntil, timeout }) - 导航(对于动态内容,使用 "networkidle0")
  • page.screenshot({ fullPage, type, quality, clip }) - 捕获图像
  • page.pdf({ format, printBackground, margin }) - 生成 PDF
  • page.evaluate(() => ...) - 在浏览器中执行 JS(提取数据、XPath 变通方案)
  • page.content() / page.setContent(html) - 获取/设置 HTML
  • page.waitForSelector(selector) - 等待元素
  • page.type(selector, text) / page.click(selector) - 表单交互

重要模式:

// Must pass binding
const browser = await puppeteer.launch(env.MYBROWSER); // ✅
// const browser = await puppeteer.launch(); // ❌ Error!

// Session reuse for performance
const sessions = await puppeteer.sessions(env.MYBROWSER);
const freeSessions = sessions.filter(s => !s.connectionId);
if (freeSessions.length > 0) {
  browser = await puppeteer.connect(env.MYBROWSER, freeSessions[0].sessionId);
}

// Keep session alive
await browser.disconnect(); // Don't close

// XPath workaround (not directly supported)
const data = await page.evaluate(() => {
  return new XPathEvaluator()
    .createExpression("/html/body/div/h1")
    .evaluate(document, XPathResult.FIRST_ORDERED_NODE_TYPE)
    .singleNodeValue.innerHTML;
});

Playwright API 参考

状态:GA(2025 年 9 月)- 支持 Playwright v1.55、MCP v0.0.30,支持本地开发([email protected]+)

安装:

npm install @cloudflare/playwright

配置要求(2025 年更新):

{
  "compatibility_flags": ["nodejs_compat"],
  "compatibility_date": "2025-09-15"  // Required for Playwright v1.55
}

基本用法:

import { chromium } from "@cloudflare/playwright";

const browser = await chromium.launch(env.BROWSER);
const page = await browser.newPage();
await page.goto("https://example.com");
const screenshot = await page.screenshot();
await browser.close();

Puppeteer 与 Playwright 对比:

  • 导入:puppeteer 与来自 "@cloudflare/playwright" 的 { chromium }
  • 会话 API:Puppeteer 提供高级会话管理(sessions/history/limits),Playwright 提供基础管理
  • 自动等待:Playwright 内置自动等待,Puppeteer 需要手动调用 waitForSelector()
  • MCP 支持:Playwright MCP v0.0.30(2025 年 7 月),提供 Playwright MCP 服务
  • 最新版本:支持 Playwright v1.57(2026 年 1 月更新)

建议:会话复用时使用 Puppeteer。迁移现有测试或需要 MCP 集成时使用 Playwright。

官方文档:https://developers.cloudflare.com/browser-rendering/playwright/


会话管理

原因:启动新浏览器速度慢且会占用并发限额。复用会话可加快响应、降低并发用量并提高资源利用率。

会话复用模式(重要)
async function getBrowser(env: Env): Promise<Browser> {
  const sessions = await puppeteer.sessions(env.MYBROWSER);
  const freeSessions = sessions.filter(s => !s.connectionId);

  if (freeSessions.length > 0) {
    try {
      return await puppeteer.connect(env.MYBROWSER, freeSessions[0].sessionId);
    } catch (e) {
      console.log("Failed to connect, launching new browser");
    }
  }

  return await puppeteer.launch(env.MYBROWSER);
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const browser = await getBrowser(env);

    try {
      const page = await browser.newPage();
      await page.goto("https://example.com");
      const screenshot = await page.screenshot();

      await browser.disconnect(); // ✅ Keep alive for reuse

      return new Response(screenshot, {
        headers: { "content-type": "image/png" }
      });
    } catch (error) {
      await browser.close(); // ❌ Close on error
      throw error;
    }
  }
};

关键规则:

  • ✅ browser.disconnect() - 保持会话运行,以供复用
  • ❌ browser.close() - 仅在发生错误或确实不再需要会话时调用
  • ✅ 始终处理连接失败
浏览器上下文(Cookie/缓存隔离)

使用 browser.createBrowserContext() 共享浏览器,同时隔离 Cookie/缓存:

const browser = await puppeteer.launch(env.MYBROWSER);
const context1 = await browser.createBrowserContext(); // User 1
const context2 = await browser.createBrowserContext(); // User 2

const page1 = await context1.newPage();
const page2 = await context2.newPage();
// Separate cookies/cache per context
多标签页模式

❌ 不佳:为 10 个 URL 启动 10 个浏览器(浪费并发资源) ✅ 推荐:1 个浏览器,通过 Promise.all() + browser.newPage() 打开 10 个标签页

const browser = await puppeteer.launch(env.MYBROWSER);
const results = await Promise.all(
  urls.map(async (url) => {
    const page = await browser.newPage();
    await page.goto(url);
    const data = await page.evaluate(() => ({ title: document.title }));
    await page.close();
    return { url, data };
  })
);
await browser.close();

常见模式

使用 KV 缓存截图

缓存截图以减少浏览器用量并提升性能:

interface Env {
  MYBROWSER: Fetcher;
  CACHE: KVNamespace;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const { searchParams } = new URL(request.url);
    const url = searchParams.get("url");
    if (!url) return new Response("Missing ?url parameter", { status: 400 });

    const normalizedUrl = new URL(url).toString();

    // Check cache first
    let screenshot = await env.CACHE.get(normalizedUrl, { type: "arrayBuffer" });

    if (!screenshot) {
      const browser = await puppeteer.launch(env.MYBROWSER);
      const page = await browser.newPage();
      await page.goto(normalizedUrl);
      screenshot = await page.screenshot();
      await browser.close();

      // Cache for 24 hours
      await env.CACHE.put(normalizedUrl, screenshot, { expirationTtl: 60 * 60 * 24 });
    }

    return new Response(screenshot, { headers: { "content-type": "image/png" } });
  }
};
AI 增强型抓取

将 Browser Rendering 与 Workers AI 结合,以提取结构化数据:

interface Env {
  MYBROWSER: Fetcher;
  AI: Ai;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const { searchParams } = new URL(request.url);
    const url = searchParams.get("url");

    // Scrape page content
    const browser = await puppeteer.launch(env.MYBROWSER);
    const page = await browser.newPage();
    await page.goto(url!, { waitUntil: "networkidle0" });
    const bodyContent = await page.$eval("body", el => el.innerHTML);
    await browser.close();

    // Extract structured data with AI
    const response = await env.AI.run("@cf/meta/llama-3.1-8b-instruct", {
      messages: [{
        role: "user",
        content: `Extract product info as JSON from this HTML. Include: name, price, description.\n\nHTML:\n${bodyContent.slice(0, 4000)}`
      }]
    });

    return Response.json({ url, product: JSON.parse(response.response) });
  }
};

其他常见模式:生成 PDF(page.pdf())、抓取结构化数据(page.evaluate())、表单自动化(page.type() + page.click())。请参阅附带的 templates/ 目录。


定价与限制

计费正式推出:2025 年 8 月 20 日

免费套餐:每天 10 分钟、3 个并发浏览器、每分钟启动 3 次、超时 60 秒 付费套餐:每月包含 10 小时(超出后每小时 $0.09)、30 个并发浏览器(超出后每个浏览器 $2.00)、每分钟启动 30 次、超时 60 秒至 10 分钟

并发数计算:按每日峰值用量的月平均值计算(例如,平均 35 个浏览器 =(35 - 包含的 30 个)× $2.00 = 每月 $10.00)

速率限制:按固定的每秒填充速率执行(不支持突发请求)。每分钟 30 个请求 = 每 2 秒 1 个请求。即使配额尚未用完,也不能一次性发送全部 30 个请求。启动前请检查 puppeteer.limits(env.MYBROWSER):

const limits = await puppeteer.limits(env.MYBROWSER);
if (limits.allowedBrowserAcquisitions === 0) {
  const delay = limits.timeUntilNextAllowedBrowserAcquisition || 1000;
  await new Promise(resolve => setTimeout(resolve, delay));
}

已知问题预防

此技能可预防8 个已记录的问题:


问题 #1:不支持 XPath 选择器

错误:“不支持 XPath 选择器”或选择器失败 来源: https://developers.cloudflare.com/browser-rendering/faq/#why-cant-i-use-an-xpath-selector-when-using-browser-rendering-with-puppeteer 原因: XPath 会给 Workers 带来安全风险 **预防:**使用 CSS 选择器,或使用 page.evaluate() 和 XPathEvaluator

解决方案:

// ❌ Don't use XPath directly (not supported)
// await page.$x('/html/body/div/h1')

// ✅ Use CSS selector
const heading = await page.$("div > h1");

// ✅ Or use XPath in page.evaluate()
const innerHtml = await page.evaluate(() => {
  return new XPathEvaluator()
    .createExpression("/html/body/div/h1")
    .evaluate(document, XPathResult.FIRST_ORDERED_NODE_TYPE)
    .singleNodeValue.innerHTML;
});

问题 #2:未传递浏览器绑定(Fetcher 类型混淆)

错误:“无法读取未定义对象的属性(读取‘fetch’)”或“RPC 接收器未实现方法‘launch’” 来源:GitHub 问题 #10772,https://developers.cloudflare.com/browser-rendering/faq/#cannot-read-properties-of-undefined-reading-fetch **原因:**调用 puppeteer.launch() 时未传入浏览器绑定,或尝试直接调用 env.MYBROWSER.launch()。浏览器绑定是一个 Fetcher(REST API 封装器),而非浏览器实例。 **预防:**始终将 env.MYBROWSER 传递给 puppeteer.launch() 或 chromium.launch() 封装器

解决方案:

// ❌ Missing browser binding
const browser = await puppeteer.launch(); // Error!

// ❌ Wrong - trying to call launch() on Fetcher directly
const browser = await env.MYBROWSER.launch(); // "RPC receiver does not implement the method 'launch'"

// ✅ Pass binding to Puppeteer/Playwright wrapper
const browser = await puppeteer.launch(env.MYBROWSER);
// or for Playwright:
const browser = await chromium.launch(env.MYBROWSER);

TypeScript 类型说明:

interface Env {
  MYBROWSER: Fetcher; // It's a Fetcher, not a Browser!
}

问题 #3:浏览器超时(60 秒)

**错误:**浏览器在 60 秒后意外关闭 **来源:**https://developers.cloudflare.com/browser-rendering/platform/limits/#note-on-browser-timeout **原因:**默认超时为 60 秒无活动 **预防:**使用 keep_alive 选项将超时时间延长至最多 10 分钟

解决方案:

// Extend timeout to 5 minutes for long-running tasks
const browser = await puppeteer.launch(env.MYBROWSER, {
  keep_alive: 300000 // 5 minutes = 300,000 ms
});

**注意:**如果在指定时长内没有 devtools 命令,浏览器就会关闭。


问题 #4:达到并发限制

错误:“超出速率限制”或无法启动新浏览器 **来源:**https://developers.cloudflare.com/browser-rendering/platform/limits/、[2025-09-25 更新日志](https://developers.cloudflare.com/browser-rendering/changelog/) **原因:**超出并发浏览器限制(截至 2025 年 9 月,免费套餐为 3 个,付费套餐为 30 个) **预防:**复用会话,使用标签页而非多个浏览器,启动前检查限制,并将请求速率控制在每秒限制内

解决方案:

// 1. Check limits before launching
const limits = await puppeteer.limits(env.MYBROWSER);
if (limits.allowedBrowserAcquisitions === 0) {
  return new Response("Concurrency limit reached", { status: 429 });
}

// 2. Reuse sessions
const sessions = await puppeteer.sessions(env.MYBROWSER);
const freeSessions = sessions.filter(s => !s.connectionId);
if (freeSessions.length > 0) {
  const browser = await puppeteer.connect(env.MYBROWSER, freeSessions[0].sessionId);
}

// 3. Use tabs instead of multiple browsers
const browser = await puppeteer.launch(env.MYBROWSER);
const page1 = await browser.newPage();
const page2 = await browser.newPage(); // Same browser, different tabs

问题 #5:本地开发请求大小限制

**错误:**在 wrangler dev 中,超过 1MB 的请求会失败 **来源:**https://developers.cloudflare.com/browser-rendering/faq/#does-local-development-support-all-browser-rendering-features **原因:**本地开发限制 **预防:**本地开发时,在浏览器绑定中使用 remote: true

解决方案:

// wrangler.jsonc for local development
{
  "browser": {
    "binding": "MYBROWSER",
    "remote": true  // Use real headless browser during dev
  }
}

问题 #6:始终触发机器人防护

**错误:**网站将请求拦截为机器人流量 **来源:**https://developers.cloudflare.com/browser-rendering/faq/#will-browser-rendering-bypass-cloudflares-bot-protection **原因:**Browser Rendering 请求始终会被识别为机器人 **预防:**无法绕过;如果要抓取自己的区域,请创建 WAF 跳过规则(机器人管理需要 Enterprise 套餐)

解决方案:

// ❌ Cannot bypass bot protection
// Requests will always be identified as bots

// ✅ If scraping your own Cloudflare zone (Enterprise plan only):
// 1. Go to Security > WAF > Custom rules
// 2. Create skip rule with custom header:
//    Header: X-Custom-Auth
//    Value: your-secret-token
// 3. Pass header in your scraping requests

await page.setExtraHTTPHeaders({
  'X-Custom-Auth': 'your-secret-token'
});

// Note: Automatic headers are included:
// - cf-biso-request-id
// - cf-biso-devtools

**重要提示:**即使在自有网站上,免费、Pro 和 Business 套餐也无法绕过机器人检测。WAF 允许列表要求使用带有 Bot Management 的 Enterprise 套餐。


问题 #7:page.evaluate() 函数名注入(__name 错误)

错误: ReferenceError: __name is not defined 来源: GitHub 问题 #7107 原因: esbuild 压缩(wrangler 3.80.1+)会在包含嵌套函数声明的箭头函数中注入 __name() 辅助函数调用。这些调用会在浏览器上下文中运行,而该上下文中不存在此辅助函数。 预防方法: 保持 page.evaluate() 函数简洁,避免嵌套函数声明 适用范围: wrangler 3.80.1 - 3.83.0(已在 3.83.0+ 中修复)

解决方案:

// ❌ Avoid nested function declarations
const data = await page.evaluate(async () => {
  function toNumber(str: string | undefined): number | undefined {
    const num = typeof str === 'string' ? str.replaceAll('.', '').replaceAll(',', '.').match(/[+-]?([0-9]*[.])?[0-9]+/) : false;
    if (num) {
      return Number(num[0]);
    } else {
      return undefined;
    }
  }
  return toNumber('123.456');
});
// Error: ReferenceError: __name is not defined

// ✅ Inline the logic without nested functions
const data = await page.evaluate(async () => {
  const str = '123.456';
  const num = typeof str === 'string' ? str.replaceAll('.', '').replaceAll(',', '.').match(/[+-]?([0-9]*[.])?[0-9]+/) : false;
  return num ? Number(num[0]) : undefined;
});

// ✅ Or update to wrangler 3.83.0+
// npm install wrangler@latest

注意: 复杂回调中的 page.waitForSelector() 也会受此影响。已在 wrangler 3.83.0+ 中修复(2024 年 11 月)。


问题 #8:waitForSelector() 超时行为已更改

错误: 依赖无限等待的代码现在会超时 来源: 更新日志 2026-01-07 原因: 以前,当找不到选择器时,waitForSelector() 不会超时(会无限期挂起)。此问题已修复,现在会正确遵循超时值。 预防方法: 始终设置明确的超时时间,并处理超时错误 适用范围: 2026 年 1 月之前编写且依赖无限等待的所有代码

解决方案:

// ❌ Old behavior - would hang forever if selector not found
await page.waitForSelector('#dynamic-element');

// ✅ New behavior - properly times out (set explicit timeout)
try {
  await page.waitForSelector('#dynamic-element', { timeout: 5000 });
} catch (error) {
  if (error.name === 'TimeoutError') {
    console.log('Element not found within 5 seconds');
    // Handle missing element gracefully
  } else {
    throw error;
  }
}

// ✅ Use longer timeout for slow-loading elements
await page.waitForSelector('#slow-element', { timeout: 30000 }); // 30 seconds

注意: 这是一项具有破坏性的修复(行为更改)。依赖无限等待的代码现在会超时并抛出错误。务必妥善处理 TimeoutError。


生产环境检查清单

将 Browser Rendering Workers 部署到生产环境之前:

配置
  • 已在 wrangler.jsonc 中配置 Browser 绑定
  • 已启用 nodejs_compat 标志(Browser Rendering 必需)
  • 如果任务耗时超过 60 秒,已设置保活超时时间
  • 如有需要,已为本地开发启用远程绑定
错误处理
  • 已为速率限制实现重试逻辑
  • 已处理 page.goto() 的超时
  • 已在 try-finally 代码块中清理浏览器
  • 已在启动浏览器前检查并发限制
  • 浏览器不可用时可平稳降级
性能
  • 已为高流量路由实现会话复用
  • 使用多个标签页,而非多个浏览器
  • 使用无痕上下文隔离会话
  • 使用 KV 缓存重复生成的屏幕截图/PDF
  • 批量执行操作,以最大限度提高浏览器利用率
监控
安全
  • 验证 URL 输入(防止 SSRF)
  • 设置超时限制以防止滥用
  • 对公共端点进行速率限制
  • 为敏感的抓取端点启用身份验证
  • 如果抓取自己的区域,则配置 WAF 规则
测试
  • 使用各种页面尺寸测试屏幕截图捕获
  • 使用自定义 HTML 测试 PDF 生成
  • 使用动态内容(networkidle0)测试抓取
  • 测试错误场景(无效 URL、超时)
  • 对并发限制进行负载测试

错误处理最佳实践

生产环境模式——使用 try-catch 并妥善清理:

async function withBrowser<T>(env: Env, fn: (browser: Browser) => Promise<T>): Promise<T> {
  let browser: Browser | null = null;

  try {
    // 1. Check limits before launching
    const limits = await puppeteer.limits(env.MYBROWSER);
    if (limits.allowedBrowserAcquisitions === 0) {
      throw new Error("Rate limit reached");
    }

    // 2. Try session reuse first
    const sessions = await puppeteer.sessions(env.MYBROWSER);
    const freeSessions = sessions.filter(s => !s.connectionId);
    browser = freeSessions.length > 0
      ? await puppeteer.connect(env.MYBROWSER, freeSessions[0].sessionId)
      : await puppeteer.launch(env.MYBROWSER);

    // 3. Execute user function
    const result = await fn(browser);

    // 4. Disconnect (keep alive)
    await browser.disconnect();
    return result;
  } catch (error) {
    // 5. Close on error
    if (browser) await browser.close();
    throw error;
  }
}

关键原则:检查限制 → 复用会话 → 执行 → 成功时断开连接,出错时关闭


使用捆绑资源

模板(templates/)

常见模式的即用型代码模板:

  • basic-screenshot.ts - 最简截图示例
  • screenshot-with-kv-cache.ts - 使用 KV 缓存的截图
  • pdf-generation.ts - 从 HTML 或 URL 生成 PDF
  • web-scraper-basic.ts - 基本网页抓取模式
  • web-scraper-batch.ts - 批量抓取多个 URL
  • session-reuse.ts - 通过复用会话提升性能
  • ai-enhanced-scraper.ts - 使用 Workers AI 进行网页抓取
  • playwright-example.ts - Playwright 替代方案示例
  • wrangler-browser-config.jsonc - 浏览器绑定配置

用法:

# Copy template to your project
cp ~/.claude/skills/cloudflare-browser-rendering/templates/basic-screenshot.ts src/index.ts
参考文档(references/)

深入文档:

  • session-management.md - 完整的会话复用指南
  • pricing-and-limits.md - 详细的定价说明
  • common-errors.md - 所有已知问题及其解决方案
  • puppeteer-vs-playwright.md - 功能对比与迁移指南

加载时机: 实现高级模式或排查特定问题时参考。


依赖项

必需:

可选:

相关技能:

  • cloudflare-worker-base - 使用 Hono 设置 Worker
  • cloudflare-kv - 用于截图的 KV 缓存
  • cloudflare-r2 - 用于存储生成文件的 R2
  • cloudflare-workers-ai - AI 增强的网页抓取

官方文档


软件包版本(已于 2026-01-21 验证)

{
  "dependencies": {
    "@cloudflare/puppeteer": "^1.0.4"
  },
  "devDependencies": {
    "@cloudflare/workers-types": "^4.20251014.0",
    "wrangler": "^4.59.3"
  }
}

替代方案(Playwright):

{
  "dependencies": {
    "@cloudflare/playwright": "^1.1.0"
  }
}

注意: Playwright v1.1.0 支持 Playwright v1.57(2026 年 1 月)。Wrangler 3.83.0+ 修复了 page.evaluate() 的 __name 注入错误。


故障排除

问题:"Cannot read properties of undefined (reading 'fetch')"

解决方案: 将浏览器绑定传递给 puppeteer.launch():

const browser = await puppeteer.launch(env.MYBROWSER); // Not just puppeteer.launch()
问题:XPath 选择器不起作用

解决方案: 使用 CSS 选择器,或通过 XPathEvaluator 配合 page.evaluate() 使用 XPath(参见 Issue #1)

问题:浏览器在 60 秒后关闭

解决方案: 使用 keep_alive 延长超时时间:

const browser = await puppeteer.launch(env.MYBROWSER, { keep_alive: 300000 });
问题:达到速率限制

解决方案: 复用会话、使用标签页,并在启动前检查限制(参见 Issue #4)

问题:本地开发请求超过 1 MB 时失败

解决方案: 在 wrangler.jsonc 中启用远程绑定:

{ "browser": { "binding": "MYBROWSER", "remote": true } }
问题:网站将请求拦截为机器人流量

解决方案: 无法绕过。如果是你自己的区域,请创建 WAF 跳过规则(参见 Issue #6)


有疑问?遇到问题?

  1. 查看 references/common-errors.md 获取详细解决方案
  2. 查看 references/session-management.md 了解性能优化
  3. 确认已在 wrangler.jsonc 中配置浏览器绑定
  4. 查看官方文档:https://developers.cloudflare.com/browser-rendering/
  5. 确保已启用 nodejs_compat 兼容性标志

评论

支持 Markdown