Cloudflare浏览器渲染自动化Skill cloudflare-browser-rendering

这个技能用于通过Cloudflare的浏览器渲染API实现无头浏览器自动化,包括截图捕获、PDF生成、网页内容提取和自动化测试。它支持多种集成方式,如REST API、Workers绑定(使用Puppeteer或Playwright)、MCP服务器和AI驱动的自动化,适用于网页爬虫、数据提取、应用测试和AI代理控制。关键词:浏览器自动化、截图、PDF生成、网页爬虫、Cloudflare Workers、无头浏览器、自动化测试、AI驱动。

Serverless 0 次安装 0 次浏览 更新于 3/18/2026

name: cloudflare-browser-rendering description: 实现Cloudflare浏览器渲染的指南 - 一个用于截图、PDF生成、网页爬虫和测试的无头浏览器自动化API。在自动化浏览器、截图、生成PDF、爬取动态内容、提取结构化数据或测试网页应用时使用。支持REST API、Workers绑定(Puppeteer/Playwright)、MCP服务器和AI驱动的自动化。(项目)

Cloudflare浏览器渲染

通过Cloudflare的Workers浏览器渲染API控制无头浏览器。自动化任务、截图、将页面转换为PDF、提取数据并测试网页应用。

何时使用此技能

在需要时使用Cloudflare浏览器渲染:

  • 截图网页(PNG、JPEG、WebP)
  • 从HTML/CSS或网页生成PDF
  • 爬取需要JavaScript执行的动态内容
  • 从网站提取结构化数据(JSON-LD、Schema.org、Open Graph)
  • 将网页转换为Markdown或提取链接
  • 自动化浏览器交互以进行测试或工作流
  • 与Cloudflare Workers集成浏览器自动化
  • 使用Workers AI构建AI驱动的网页爬虫
  • 部署MCP服务器用于LLM代理浏览器控制
  • 使用Queues集成创建网页爬虫

集成方法

1. REST API(简单,无需Worker)

使用HTTP端点快速集成。适用于一次性任务或外部服务集成。

可用端点:

  • /screenshot - 捕获PNG/JPEG/WebP截图
  • /pdf - 生成PDF文档
  • /content - 提取完全渲染的HTML
  • /markdown - 将页面转换为Markdown
  • /scrape - 通过CSS选择器提取数据
  • /links - 提取和分析页面链接
  • /json - 提取JSON-LD、Schema.org元数据
  • /snapshot - 通过多步浏览器状态调试

认证:

curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/browser-rendering/screenshot" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}'

速率限制:

  • 60请求/分钟
  • 10并发请求
  • 100突发每5分钟

2. Workers绑定与Puppeteer(低级控制)

在Cloudflare Workers内完全访问Puppeteer API以实现最大控制。

设置(wrangler.toml):

name = "browser-worker"
main = "src/index.ts"
compatibility_date = "2024-01-01"

browser = { binding = "MYBROWSER" }

[[kv_namespaces]]
binding = "KV"
id = "your-kv-namespace-id"

基本截图Worker:

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const browser = await puppeteer.launch(env.MYBROWSER);
    const page = await browser.newPage();

    await page.goto("https://example.com", { waitUntil: "networkidle2" });
    const screenshot = await page.screenshot({ type: "png" });

    await browser.close();

    return new Response(screenshot, {
      headers: { "Content-Type": "image/png" }
    });
  }
};

关键Puppeteer方法:

  • puppeteer.launch(binding) - 启动新浏览器
  • browser.newPage() - 创建新页面
  • page.goto(url, options) - 导航到URL
  • page.screenshot(options) - 捕获截图
  • page.content() - 获取HTML内容
  • page.pdf(options) - 生成PDF
  • page.evaluate(fn) - 在页面上下文中执行JS
  • browser.disconnect() - 断开连接保持会话活跃
  • browser.close() - 关闭并结束会话
  • puppeteer.connect(binding, sessionId) - 重新连接到会话

会话重用(成本优化关键):

// 断开连接而不是关闭以保持会话活跃
await browser.disconnect();

// 检索并重新连接到现有会话
const sessions = await puppeteer.sessions(env.MYBROWSER);
const freeSession = sessions.find(s => !s.connectionId);

if (freeSession) {
  const browser = await puppeteer.connect(env.MYBROWSER, freeSession.sessionId);
}

3. Workers绑定与Playwright(测试焦点)

Playwright提供高级测试功能、断言和调试。

设置:

npm create cloudflare@latest -- browser-worker
cd browser-worker
npm install
wrangler dev  # 本地测试
wrangler deploy  # 生产

高级Playwright Worker:

import { Hono } from "hono";

const app = new Hono<{ Bindings: Env }>();

app.get("/screenshot/:url", async (c) => {
  const browser = await c.env.MYBROWSER.launch();
  const page = await browser.newPage();

  await page.goto(c.req.param("url"));
  await page.waitForLoadState("networkidle");

  const screenshot = await page.screenshot({ fullPage: true });
  await browser.close();

  return c.body(screenshot, 200, {
    "Content-Type": "image/png"
  });
});

export default app;

Playwright特定功能:

  • 使用KV的存储状态持久化
  • 调试跟踪
  • 高级断言(expect(page).toHaveTitle()
  • 网络拦截
  • 多上下文用于标签池

存储状态缓存:

// 保存认证状态
const state = await page.context().storageState();
await env.KV.put("auth-state", JSON.stringify(state));

// 恢复认证状态
const savedState = await env.KV.get("auth-state", "json");
const context = await browser.newContext({ storageState: savedState });

4. MCP服务器(AI代理集成)

部署模型上下文协议服务器用于LLM代理浏览器控制。

功能:

  • 无需视觉模型(使用可访问性树)
  • 简单自然语言指令
  • 基于Playwright和浏览器渲染构建
  • 预配置服务器模板可用

用例: 使AI代理能够使用结构化可访问性数据而不是截图与网页交互。

5. Stagehand(AI驱动自动化)

AI支持的自然语言浏览器自动化。

示例:

import { Stagehand } from "@stagehand-ai/stagehand";

const stagehand = new Stagehand(env.MYBROWSER);
await stagehand.init();

// 自然语言指令
await stagehand.act("点击登录按钮");
await stagehand.act("填写邮箱为user@example.com");
const data = await stagehand.extract("获取所有产品价格");

await stagehand.close();

配置模式

Wrangler配置(浏览器绑定)

基本设置:

name = "my-browser-worker"
main = "src/index.ts"
compatibility_date = "2024-01-01"

browser = { binding = "MYBROWSER" }

高级设置与Durable Objects和R2:

browser = { binding = "MYBROWSER" }

[[durable_objects.bindings]]
name = "BROWSER"
class_name = "Browser"

[[r2_buckets]]
binding = "BUCKET"
bucket_name = "my-screenshots"

[[migrations]]
tag = "v1"
new_classes = ["Browser"]

超时配置

默认超时:

  • goToOptions.timeout: 30秒(最大60秒)
  • waitForSelector: 最多60秒
  • actionTimeout: 最多5分钟
  • Workers CPU时间: 30秒(标准), 15分钟(无绑定)

自定义超时示例:

// Puppeteer
await page.goto(url, {
  timeout: 60000,  // 60秒
  waitUntil: "networkidle2"
});

await page.waitForSelector(".content", { timeout: 45000 });

// Playwright
await page.goto(url, {
  timeout: 60000,
  waitUntil: "networkidle"
});

await page.locator(".element").click({ timeout: 10000 });

视口和截图选项

// 设置视口大小
await page.setViewport({ width: 1920, height: 1080 });

// 截图选项
const screenshot = await page.screenshot({
  type: "png",           // "png" | "jpeg" | "webp"
  quality: 90,           // 仅JPEG/WebP, 0-100
  fullPage: true,        // 捕获完整可滚动页面
  clip: {                // 裁剪到特定区域
    x: 0, y: 0,
    width: 800,
    height: 600
  }
});

PDF生成选项

const pdf = await page.pdf({
  format: "A4",
  printBackground: true,
  margin: {
    top: "1cm",
    right: "1cm",
    bottom: "1cm",
    left: "1cm"
  },
  displayHeaderFooter: true,
  headerTemplate: "<div>Header</div>",
  footerTemplate: "<div>Footer</div>"
});

限制和定价

免费计划

  • 使用量: 10分钟/天
  • 并发: 最多3个浏览器
  • 速率限制: 3新浏览器/分钟, 6请求/分钟
  • 成本: 免费

付费计划(Workers付费)

  • 使用量: 10小时/月包括
  • 并发: 最多30个浏览器
  • 速率限制: 30新浏览器/分钟, 180请求/分钟
  • 超支定价:
    • 额外使用量: $0.09/小时
    • 额外并发: $2.00/并发浏览器

REST API定价

  • 免费: 100请求/天
  • 付费: 10,000请求/月包括
  • 超支: $0.09/额外浏览器小时

成本优化提示:

  1. 使用disconnect()而不是close()以重用会话
  2. 启用Keep-Alive(最多10分钟)
  3. 使用浏览器上下文进行标签池而不是多个浏览器
  4. 使用KV存储缓存认证状态
  5. 实现Durable Objects用于持久会话

常见用例

1. 截图捕获与缓存

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

    // 检查缓存
    const cached = await env.KV.get(targetUrl, "arrayBuffer");
    if (cached) {
      return new Response(cached, {
        headers: { "Content-Type": "image/png" }
      });
    }

    // 生成截图
    const browser = await puppeteer.launch(env.MYBROWSER);
    const page = await browser.newPage();
    await page.goto(targetUrl);
    const screenshot = await page.screenshot();
    await browser.close();

    // 缓存24小时
    await env.KV.put(targetUrl, screenshot, {
      expirationTtl: 86400
    });

    return new Response(screenshot, {
      headers: { "Content-Type": "image/png" }
    });
  }
};

2. PDF证书生成器

async function generateCertificate(name: string, env: Env) {
  const browser = await puppeteer.launch(env.MYBROWSER);
  const page = await browser.newPage();

  const html = `
    <!DOCTYPE html>
    <html>
      <head>
        <style>
          body { font-family: Arial; text-align: center; padding: 50px; }
          h1 { color: #2c3e50; font-size: 48px; }
        </style>
      </head>
      <body>
        <h1>成就证书</h1>
        <p>授予:<strong>${name}</strong></p>
      </body>
    </html>
  `;

  await page.setContent(html);
  const pdf = await page.pdf({
    format: "A4",
    printBackground: true
  });

  await browser.close();
  return pdf;
}

3. AI驱动网页爬虫

import { Ai } from "@cloudflare/ai";

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // 使用浏览器渲染渲染页面
    const browser = await puppeteer.launch(env.MYBROWSER);
    const page = await browser.newPage();
    await page.goto("https://news.ycombinator.com");
    const content = await page.content();
    await browser.close();

    // 使用Workers AI提取数据
    const ai = new Ai(env.AI);
    const response = await ai.run(
      "@hf/thebloke/deepseek-coder-6.7b-instruct-awq",
      {
        messages: [
          {
            role: "system",
            content: "提取前5篇文章标题和URL作为JSON数组"
          },
          {
            role: "user",
            content: content
          }
        ]
      }
    );

    return Response.json(response);
  }
};

4. 带Queues的网页爬虫

export default {
  async queue(batch: MessageBatch<any>, env: Env): Promise<void> {
    const browser = await puppeteer.launch(env.MYBROWSER);

    for (const message of batch.messages) {
      const page = await browser.newPage();
      await page.goto(message.body.url);

      // 提取链接
      const links = await page.evaluate(() => {
        return Array.from(document.querySelectorAll("a"))
          .map(a => a.href);
      });

      // 排队新链接
      for (const link of links) {
        await env.QUEUE.send({ url: link });
      }

      await page.close();
    }

    await browser.close();
  }
};

5. Durable Objects用于持久会话

export class Browser {
  state: DurableObjectState;
  browser: any;
  lastUsed: number;

  constructor(state: DurableObjectState, env: Env) {
    this.state = state;
    this.lastUsed = Date.now();
  }

  async fetch(request: Request, env: Env) {
    // 首次请求时初始化浏览器
    if (!this.browser) {
      this.browser = await puppeteer.launch(env.MYBROWSER);
    }

    // 设置保持活跃警报
    this.lastUsed = Date.now();
    await this.state.storage.setAlarm(Date.now() + 10000);

    const page = await this.browser.newPage();
    await page.goto(new URL(request.url).searchParams.get("url"));
    const screenshot = await page.screenshot();
    await page.close();

    return new Response(screenshot, {
      headers: { "Content-Type": "image/png" }
    });
  }

  async alarm() {
    // 如果空闲60秒则关闭浏览器
    if (Date.now() - this.lastUsed > 60000) {
      await this.browser?.close();
      this.browser = null;
    } else {
      await this.state.storage.setAlarm(Date.now() + 10000);
    }
  }
}

最佳实践

1. 会话管理

  • 始终使用disconnect()而不是close() 以保持会话活跃重用
  • 实现会话池以减少并发成本
  • 设置保持活跃到最大(10分钟)以支持持续工作流
  • 跟踪会话ID和连接状态

2. 性能优化

  • 在KV存储中缓存频繁访问的内容
  • 使用浏览器上下文进行标签池而不是多个浏览器
  • 实现Durable Objects用于持久、可重用会话
  • 选择适当的waitUntil策略(load、networkidle0、networkidle2)
  • 设置现实超时以避免不必要等待

3. 错误处理

  • 实现Retry-After意识以处理429速率限制错误
  • 优雅处理超时错误并采用回退策略
  • 在尝试重新连接前检查会话可用性
  • 在缓存或返回数据前验证响应

4. 成本管理

  • 通过Cloudflare仪表板监控使用量
  • 使用会话重用显著降低并发成本
  • 实现智能缓存策略
  • 考虑批量处理多个URL
  • 为Durable Objects清理设置适当警报间隔

5. 安全

  • 在导航前验证所有用户提供的URL
  • 为Workers端点实施适当认证
  • 使用Web Bot Auth签名以增加保护
  • 在处理前净化提取的内容
  • 设置适当的CORS头

故障排除

常见问题

超时错误:

  • 增加超时: page.goto(url, { timeout: 60000 })
  • 更改waitUntil: { waitUntil: "domcontentloaded" }
  • 检查网络条件和目标站点性能

速率限制(429)错误:

  • 实现带有Retry-After头的指数退避
  • 降低请求频率
  • 升级到付费计划以获得更高限制

会话连接失败:

  • 在连接前检查会话可用性
  • 使用try-catch处理竞争条件
  • 验证浏览器未超时(10分钟保持活跃限制)

内存问题:

  • 完成后关闭页面: await page.close()
  • 正确断开浏览器: await browser.disconnect()
  • 实现Durable Objects清理警报

字体渲染问题:

  • 使用支持的字体(100+预安装)
  • 通过CDN或base64注入自定义字体
  • 检查CSS中的font-family声明

API参考快速查找

REST API全局参数

  • url(必需) - 目标网页URL
  • waitDelay - 等待时间(毫秒)(0-30000)
  • goto.timeout - 导航超时(0-60000毫秒)
  • goto.waitUntil - 等待策略(load、domcontentloaded、networkidle)

Puppeteer关键方法

  • puppeteer.launch(binding) - 启动浏览器
  • puppeteer.connect(binding, sessionId) - 重新连接到会话
  • puppeteer.sessions(binding) - 列出活动会话
  • browser.newPage() - 创建新页面
  • browser.disconnect() - 断开连接保持会话活跃
  • browser.close() - 关闭并终止会话
  • page.goto(url, options) - 导航
  • page.screenshot(options) - 捕获截图
  • page.pdf(options) - 生成PDF
  • page.content() - 获取HTML
  • page.evaluate(fn) - 执行JavaScript

Playwright关键方法

  • env.MYBROWSER.launch() - 启动浏览器
  • browser.newPage() - 创建新页面
  • browser.newContext(options) - 使用状态创建上下文
  • page.goto(url, options) - 导航
  • page.screenshot(options) - 捕获截图
  • page.pdf(options) - 生成PDF
  • page.locator(selector) - 查找元素
  • page.waitForLoadState(state) - 等待加载
  • context.storageState() - 获取认证状态

支持的字体

预安装字体包括:

  • 系统字体: Arial, Verdana, Times New Roman, Georgia, Courier New
  • 开源字体: Noto Sans, Noto Serif, Roboto, Open Sans, Lato
  • 国际化字体: Noto Sans CJK(中文、日文、韩文), Noto Sans Arabic, Hebrew, Thai
  • 表情符号: Noto Color Emoji

自定义字体注入:

<link href="https://fonts.googleapis.com/css2?family=Poppins" rel="stylesheet">

部署清单

  1. 设置:

    • [ ] 安装Wrangler: npm install -g wrangler
    • [ ] 登录: wrangler login
    • [ ] 创建项目: npm create cloudflare@latest
  2. 配置:

    • [ ] 添加浏览器绑定到wrangler.toml
    • [ ] 配置KV命名空间用于缓存(可选)
    • [ ] 设置R2桶用于存储(可选)
    • [ ] 定义Durable Objects如果使用持久会话
  3. 测试:

    • [ ] 本地测试: wrangler dev
    • [ ] 验证会话管理
    • [ ] 测试超时配置
    • [ ] 验证错误处理
  4. 部署:

    • [ ] 部署到生产: wrangler deploy
    • [ ] 在Cloudflare仪表板中监控使用量
    • [ ] 为速率限制设置警报
    • [ ] 验证成本优化策略

资源

实施工作流

实施Cloudflare浏览器渲染时:

  1. 选择集成方法:

    • REST API用于简单、外部集成
    • Workers + Puppeteer用于低级控制
    • Workers + Playwright用于测试和高级功能
    • MCP服务器用于AI代理集成
    • Stagehand用于自然语言自动化
  2. 设置配置:

    • 创建wrangler.toml带适当绑定
    • 安装依赖(@cloudflare/puppeteer@cloudflare/workers-playwright
    • 根据需要配置KV、R2或Durable Objects
  3. 实施核心逻辑:

    • 浏览器生命周期管理(启动、断开连接、关闭)
    • 导航和等待策略
    • 内容提取或截图/PDF生成
    • 错误处理和重试
  4. 优化成本:

    • 使用disconnect()实现会话重用
    • 添加保持活跃以支持持续使用
    • 在KV存储中缓存结果
    • 使用Durable Objects用于持久会话
  5. 部署和监控:

    • 使用wrangler dev本地测试
    • 使用wrangler deploy部署
    • 在仪表板中监控使用量和成本
    • 调整速率限制和缓存策略

版本支持

  • Puppeteer: v22.13.1
  • Playwright: v1.55.0
  • Node.js兼容性: Workers集成所需
  • 浏览器版本: 基于Chromium(Cloudflare定期更新)