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)- 导航到URLpage.screenshot(options)- 捕获截图page.content()- 获取HTML内容page.pdf(options)- 生成PDFpage.evaluate(fn)- 在页面上下文中执行JSbrowser.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/额外浏览器小时
成本优化提示:
- 使用
disconnect()而不是close()以重用会话 - 启用Keep-Alive(最多10分钟)
- 使用浏览器上下文进行标签池而不是多个浏览器
- 使用KV存储缓存认证状态
- 实现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(必需) - 目标网页URLwaitDelay- 等待时间(毫秒)(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)- 生成PDFpage.content()- 获取HTMLpage.evaluate(fn)- 执行JavaScript
Playwright关键方法
env.MYBROWSER.launch()- 启动浏览器browser.newPage()- 创建新页面browser.newContext(options)- 使用状态创建上下文page.goto(url, options)- 导航page.screenshot(options)- 捕获截图page.pdf(options)- 生成PDFpage.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">
部署清单
-
设置:
- [ ] 安装Wrangler:
npm install -g wrangler - [ ] 登录:
wrangler login - [ ] 创建项目:
npm create cloudflare@latest
- [ ] 安装Wrangler:
-
配置:
- [ ] 添加浏览器绑定到
wrangler.toml - [ ] 配置KV命名空间用于缓存(可选)
- [ ] 设置R2桶用于存储(可选)
- [ ] 定义Durable Objects如果使用持久会话
- [ ] 添加浏览器绑定到
-
测试:
- [ ] 本地测试:
wrangler dev - [ ] 验证会话管理
- [ ] 测试超时配置
- [ ] 验证错误处理
- [ ] 本地测试:
-
部署:
- [ ] 部署到生产:
wrangler deploy - [ ] 在Cloudflare仪表板中监控使用量
- [ ] 为速率限制设置警报
- [ ] 验证成本优化策略
- [ ] 部署到生产:
资源
- 官方文档: https://developers.cloudflare.com/browser-rendering/
- Puppeteer文档: https://pptr.dev/
- Playwright文档: https://playwright.dev/
- Workers文档: https://developers.cloudflare.com/workers/
- Wrangler CLI: https://developers.cloudflare.com/workers/wrangler/
实施工作流
实施Cloudflare浏览器渲染时:
-
选择集成方法:
- REST API用于简单、外部集成
- Workers + Puppeteer用于低级控制
- Workers + Playwright用于测试和高级功能
- MCP服务器用于AI代理集成
- Stagehand用于自然语言自动化
-
设置配置:
- 创建
wrangler.toml带适当绑定 - 安装依赖(
@cloudflare/puppeteer或@cloudflare/workers-playwright) - 根据需要配置KV、R2或Durable Objects
- 创建
-
实施核心逻辑:
- 浏览器生命周期管理(启动、断开连接、关闭)
- 导航和等待策略
- 内容提取或截图/PDF生成
- 错误处理和重试
-
优化成本:
- 使用
disconnect()实现会话重用 - 添加保持活跃以支持持续使用
- 在KV存储中缓存结果
- 使用Durable Objects用于持久会话
- 使用
-
部署和监控:
- 使用
wrangler dev本地测试 - 使用
wrangler deploy部署 - 在仪表板中监控使用量和成本
- 调整速率限制和缓存策略
- 使用
版本支持
- Puppeteer: v22.13.1
- Playwright: v1.55.0
- Node.js兼容性: Workers集成所需
- 浏览器版本: 基于Chromium(Cloudflare定期更新)