Playwright ↗ 是 Microsoft 开发的开源包,可执行浏览器自动化任务;常用于编写前端测试、创建屏幕截图或爬取页面。
Workers 团队 fork 了 Playwright 的一个版本 ↗,并进行了修改以兼容 Cloudflare Workers 和 Browser Run。
我们的版本已开源,可在 Cloudflare 的 Playwright fork ↗ 中找到。npm 包可从 npmjs ↗ 安装为 @cloudflare/playwright ↗:
npm i -D @cloudflare/playwrightyarn add -D @cloudflare/playwrightpnpm add -D @cloudflare/playwrightbun add -d @cloudflare/playwright在此示例 ↗中,你将在 Cloudflare Worker 中使用 todomvc ↗ 应用程序运行 Playwright 测试。
如果你想跳过步骤快速开始,请选择下方的 部署到 Cloudflare。
确保在 Wrangler 配置文件中配置了 browser 绑定:
{
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "cloudflare-playwright-example",
"main": "src/index.ts",
"workers_dev": true,
"compatibility_flags": ["nodejs_compat"],
// Set this to today's date
"compatibility_date": "2026-08-17",
"upload_source_maps": true,
"browser": {
"binding": "MYBROWSER",
},
}"$schema" = "./node_modules/wrangler/config-schema.json"
name = "cloudflare-playwright-example"
main = "src/index.ts"
workers_dev = true
compatibility_flags = [ "nodejs_compat" ]
# Set this to today's date
compatibility_date = "2026-08-17"
upload_source_maps = true
[browser]
binding = "MYBROWSER"安装 npm 包:
npm i -D @cloudflare/playwrightyarn add -D @cloudflare/playwrightpnpm add -D @cloudflare/playwrightbun add -d @cloudflare/playwright下面介绍一些 Playwright 用法示例:
使用浏览器自动化截取网页屏幕截图是常见用例。此脚本指示浏览器导航到 https://demo.playwright.dev/todomvc,创建一些条目,截取页面屏幕截图,并在响应中返回图像。 ↗
import { launch } from "@cloudflare/playwright";
export default {
async fetch(request: Request, env: Env) {
const browser = await launch(env.MYBROWSER);
const page = await browser.newPage();
await page.goto("https://demo.playwright.dev/todomvc");
const TODO_ITEMS = [
"buy some cheese",
"feed the cat",
"book a doctors appointment",
];
const newTodo = page.getByPlaceholder("What needs to be done?");
for (const item of TODO_ITEMS) {
await newTodo.fill(item);
await newTodo.press("Enter");
}
const img = await page.screenshot();
await browser.close();
return new Response(img, {
headers: {
"Content-Type": "image/png",
},
});
},
};Playwright trace 是工作流执行的详细日志,捕获用户点击和导航操作、页面屏幕截图以及生成的控制台消息等信息,用于调试。开发者可以获取 trace.zip 文件并在本地 ↗打开,或上传到 Playwright Trace Viewer ↗——一个帮助你探索数据的 GUI 工具。
以下是生成 trace 文件的 Worker 示例:
import fs from "fs";
import { launch } from "@cloudflare/playwright";
export default {
async fetch(request: Request, env: Env) {
const browser = await launch(env.MYBROWSER);
const page = await browser.newPage();
// Start tracing before navigating to the page
await page.context().tracing.start({ screenshots: true, snapshots: true });
await page.goto("https://demo.playwright.dev/todomvc");
const TODO_ITEMS = [
"buy some cheese",
"feed the cat",
"book a doctors appointment",
];
const newTodo = page.getByPlaceholder("What needs to be done?");
for (const item of TODO_ITEMS) {
await newTodo.fill(item);
await newTodo.press("Enter");
}
// Stop tracing and save the trace to a zip file
await page.context().tracing.stop({ path: "trace.zip" });
await browser.close();
const file = await fs.promises.readFile("trace.zip");
return new Response(file, {
status: 200,
headers: {
"Content-Type": "application/zip",
},
});
},
};Playwright 最常见的用例之一是软件测试。Playwright 在 API 中包含测试断言功能;详情请参阅 Playwright 文档中的 Assertions ↗。以下示例展示 Worker 对 todomvc ↗ 演示页面执行 expect() 测试断言:
import { launch } from "@cloudflare/playwright";
import { expect } from "@cloudflare/playwright/test";
export default {
async fetch(request: Request, env: Env) {
const browser = await launch(env.MYBROWSER);
const page = await browser.newPage();
await page.goto("https://demo.playwright.dev/todomvc");
const TODO_ITEMS = [
"buy some cheese",
"feed the cat",
"book a doctors appointment",
];
const newTodo = page.getByPlaceholder("What needs to be done?");
for (const item of TODO_ITEMS) {
await newTodo.fill(item);
await newTodo.press("Enter");
}
await expect(page.getByTestId("todo-title")).toHaveCount(TODO_ITEMS.length);
await Promise.all(
TODO_ITEMS.map((value, index) =>
expect(page.getByTestId("todo-title").nth(index)).toHaveText(value),
),
);
},
};Playwright 支持 storage state ↗ 以获取并持久化 cookies 和其他存储数据。在此示例中,你将使用 storage state 在 Workers KV 中持久化 cookies 和其他存储数据。
首先,确保你有一个 KV 命名空间。可以使用以下命令创建:
npx wrangler kv namespace create KV然后,将 KV 命名空间添加到 Wrangler 配置文件:
{
"name": "storage-state-examples",
"main": "src/index.ts",
"compatibility_flags": ["nodejs_compat"],
// Set this to today's date
"compatibility_date": "2026-08-17",
"browser": {
"binding": "MYBROWSER",
},
"kv_namespaces": [
{
"binding": "KV",
"id": "<YOUR-KV-NAMESPACE-ID>",
},
],
}name = "storage-state-examples"
main = "src/index.ts"
compatibility_flags = [ "nodejs_compat" ]
# Set this to today's date
compatibility_date = "2026-08-17"
[browser]
binding = "MYBROWSER"
[[kv_namespaces]]
binding = "KV"
id = "<YOUR-KV-NAMESPACE-ID>"现在,你可以使用 storage state 在 KV 中持久化 cookies 和其他存储数据:
// gets persisted storage state from KV or undefined if it does not exist
const storageStateJson = await env.KV.get("storageState");
const storageState = storageStateJson
? ((await JSON.parse(
storageStateJson,
)) as BrowserContextOptions["storageState"])
: undefined;
await using browser = await launch(env.MYBROWSER);
// creates a new context with storage state persisted in KV
await using context = await browser.newContext({ storageState });
await using page = await context.newPage();
// do some actions on the page that may update client-side storage
// gets updated storage state: cookies, localStorage, and IndexedDB
const updatedStorageState = await context.storageState({ indexedDB: true });
// persists updated storage state in KV
await env.KV.put("storageState", JSON.stringify(updatedStorageState));如果用户省略 browser.close() 语句,浏览器实例将保持打开,可随时再次连接并复用,但默认情况下会在 1 分钟不活动后自动关闭。用户可以选择使用 keep_alive 选项(以毫秒为单位)将空闲时间延长至最多 10 分钟:
const browser = await playwright.launch(env.MYBROWSER, { keep_alive: 600000 });使用上述配置,即使不活动,浏览器也会保持打开最多 10 分钟。
提升 Browser Run Worker 性能的最佳方式是复用会话——在完成使用后保持浏览器打开,并在每次有新请求时连接到该会话。Playwright 处理 browser.close ↗ 的方式与 Puppeteer 不同。在 Playwright 中,如果浏览器是通过 connect 会话获取的,会话将断开连接。如果浏览器是通过 launch 会话获取的,会话将关闭。
import { env } from "cloudflare:workers";
import { acquire, connect } from "@cloudflare/playwright";
async function reuseSameSession() {
// acquires a new session
const { sessionId } = await acquire(env.BROWSER);
for (let i = 0; i < 5; i++) {
// connects to the session that was previously acquired
const browser = await connect(env.BROWSER, sessionId);
// ...
// this will disconnect the browser from the session, but the session will be kept alive
await browser.close();
}
}要在 Playwright 中指定自定义 user agent,在使用 browser.newContext() 创建新浏览器上下文时在选项中设置。此后从此上下文创建的所有页面都将使用新的 user agent。当目标网站根据 user agent 提供不同内容时很有用。
const context = await browser.newContext({
userAgent:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
});使用 wrangler dev 或 vite dev 进行本地开发时,Chrome 默认以 headless 模式运行。要以可见(headful)模式启动 Chrome,请设置 X_BROWSER_HEADFUL 环境变量:
X_BROWSER_HEADFUL=true npx wrangler dev或使用 Cloudflare Vite 插件:
X_BROWSER_HEADFUL=true npx vite dev这将打开浏览器窗口,以便实时观看 Playwright 自动化,更易于调试导航、元素选择和页面交互。
为了便于浏览器会话管理,我们扩展了 Playwright API,添加了新方法:
playwright.sessions() 列出当前运行的会话。它将返回类似以下的输出:
[
{
"connectionId": "2a2246fa-e234-4dc1-8433-87e6cee80145",
"connectionStartTime": 1711621704607,
"sessionId": "478f4d7d-e943-40f6-a414-837d3736a1dc",
"startTime": 1711621703708
},
{
"sessionId": "565e05fb-4d2a-402b-869b-5b65b1381db7",
"startTime": 1711621703808
}
]注意会话 478f4d7d-e943-40f6-a414-837d3736a1dc 有活动的 worker 连接(connectionId=2a2246fa-e234-4dc1-8433-87e6cee80145),而会话 565e05fb-4d2a-402b-869b-5b65b1381db7 是空闲的。连接处于活动状态时,其他 worker 无法连接到该会话。
playwright.history() 列出最近的会话,包括打开和已关闭的。它有助于了解当前用量。
[
{
"closeReason": 2,
"closeReasonText": "BrowserIdle",
"endTime": 1711621769485,
"sessionId": "478f4d7d-e943-40f6-a414-837d3736a1dc",
"startTime": 1711621703708
},
{
"closeReason": 1,
"closeReasonText": "NormalClosure",
"endTime": 1711123501771,
"sessionId": "2be00a21-9fb6-4bb2-9861-8cd48e40e771",
"startTime": 1711123430918
}
]会话 2be00a21-9fb6-4bb2-9861-8cd48e40e771 由客户端显式调用 browser.close() 关闭,而会话 478f4d7d-e943-40f6-a414-837d3736a1dc 因达到最大空闲时间而关闭(查看限制)。
你也应该能够在仪表板中访问此信息,尽管可能略有延迟。
playwright.limits() 列出你的活动限制:
{
"activeSessions": [
{ "id": "478f4d7d-e943-40f6-a414-837d3736a1dc" },
{ "id": "565e05fb-4d2a-402b-869b-5b65b1381db7" }
],
"allowedBrowserAcquisitions": 1,
"maxConcurrentSessions": 2,
"timeUntilNextAllowedBrowserAcquisition": 0
}activeSessions列出当前打开会话的 IDmaxConcurrentSessions定义可同时打开多少个浏览器allowedBrowserAcquisitions指定根据当前速率限制是否可以打开新的浏览器会话timeUntilNextAllowedBrowserAcquisition定义启动新浏览器前的等待时间
完整的 Playwright API 可在 Playwright API 文档 ↗ 中找到。
以下功能尚未完全支持,但我们正在积极开发中:
- Playwright Test ↗(Assertions ↗ 除外)
- 组件测试(Components) ↗
- Firefox ↗、Android ↗ 和 Electron ↗,以及不同版本的 Chrome
- 视频录制(Videos) ↗
这不是详尽列表——随着我们努力与原始功能集实现更全面的对等支持,预计会有快速变化。你也可以查看最新测试结果 ↗,获取已完全支持功能的细粒度最新列表。