Playwright 第5章:脚本基础与浏览器控制
Playwright 采用 Browser / Context / Page 三层架构,提供强大的浏览器控制能力。本章深入讲解这三层架构的原理、BrowserContext 的隔离机制以及多页面管理技巧。
Browser/Context/Page 三层架构
架构层次
Browser(浏览器实例)
└── BrowserContext(浏览器上下文,隔离环境)
├── Page(页面标签页1)
├── Page(页面标签页2)
└── Page(页面标签页3)
- Browser:浏览器进程,管理整个浏览器实例
- BrowserContext:类似隐身会话,提供完全隔离的浏览环境
- Page:标签页,每个页面对象对应一个浏览器标签
Browser 管理
import { chromium, firefox, webkit } from 'playwright';
// 启动浏览器
const browser = await chromium.launch({
headless: false,
});
// 获取浏览器信息
console.log('浏览器版本:', browser.version());
console.log('是否连接:', browser.isConnected());
// 监听断开事件
browser.on('disconnected', () => {
console.log('浏览器已断开');
});
// 关闭浏览器
await browser.close();
BrowserContext 隔离机制
为什么需要 BrowserContext
BrowserContext 提供类似隐身模式的独立会话环境,每个 Context 拥有独立的:
- Cookies 和本地存储
- 浏览器缓存
- 会话数据
- 认证信息
创建和使用 Context
const browser = await chromium.launch();
// 创建默认上下文
const context1 = await browser.newContext();
// 创建带配置的上下文
const context2 = await browser.newContext({
viewport: { width: 1920, height: 1080 },
locale: 'zh-CN',
timezoneId: 'Asia/Shanghai',
permissions: ['geolocation'],
colorScheme: 'dark',
userAgent: 'Mozilla/5.0 (Custom UA)',
geolocation: { latitude: 39.91, longitude: 116.40 },
});
// 验证上下文隔离
const page1 = await context1.newPage();
const page2 = await context2.newPage();
await page1.goto('https://example.com');
await page1.evaluate(() => {
document.cookie = 'session=abc123';
});
// 不同 Context 的 Cookie 不共享
const cookies1 = await context1.cookies();
const cookies2 = await context2.cookies();
console.log('Context1 cookies:', cookies1.length); // 1
console.log('Context2 cookies:', cookies2.length); // 0
await browser.close();
Context 配置选项
| 配置项 | 类型 | 说明 |
|---|---|---|
viewport | {width, height} | 窗口视口大小 |
locale | string | 浏览器语言(如 zh-CN) |
timezoneId | string | 时区设置 |
geolocation | {latitude, longitude} | 地理位置 |
permissions | string[] | 权限列表 |
colorScheme | 'light'|'dark' | 主题配色 |
userAgent | string | 自定义 User-Agent |
deviceScaleFactor | number | 设备像素比 |
storageState | string | 持久化状态路径 |
持久化认证状态
const browser = await chromium.launch();
const context = await browser.newContext();
// 执行登录操作
const page = await context.newPage();
await page.goto('https://example.com/login');
await page.fill('#username', 'admin');
await page.fill('#password', 'password123');
await page.click('button[type="submit"]');
await page.waitForURL('https://example.com/dashboard');
// 保存认证状态
await context.storageState({ path: './auth.json' });
console.log('认证状态已保存');
await browser.close();
// 后续测试复用认证状态
const context2 = await browser.newContext({
storageState: './auth.json',
});
const page2 = await context2.newPage();
await page2.goto('https://example.com/dashboard');
// 已登录状态
多页面管理
在同一个 Context 中管理多个页面
const browser = await chromium.launch();
const context = await browser.newContext();
// 同时打开多个页面
const page1 = await context.newPage();
const page2 = await context.newPage();
const page3 = await context.newPage();
// 并行导航
await Promise.all([
page1.goto('https://example.com'),
page2.goto('https://google.com'),
page3.goto('https://github.com'),
]);
// 页面间切换
await page1.bringToFront();
await page1.click('.main-link');
// 获取所有页面
const allPages = context.pages();
console.log('当前页面数:', allPages.length);
// 关闭特定页面
await page2.close();
console.log('剩余页面:', context.pages().length);
await browser.close();
新标签页处理
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
// 监听新页面事件
context.on('page', async (newPage) => {
await newPage.waitForLoadState();
console.log('新页面打开:', newPage.url());
// 在新页面中操作
const title = await newPage.title();
console.log('新页面标题:', title);
});
// 触发新标签页打开
await page.goto('https://example.com');
await page.click('a[target="_blank"]');
// 等待新页面
const [newPage] = await Promise.all([
context.waitForEvent('page'),
page.click('a[target="_blank"]'),
]);
await newPage.waitForLoadState();
console.log('新页面URL:', newPage.url());
await newPage.close();
await browser.close();
浏览器启动选项
完整启动参数
const browser = await chromium.launch({
headless: false,
slowMo: 200, // 操作间延迟(调试用)
timeout: 30000, // 启动超时
channel: 'chrome', // 使用系统安装的 Chrome
args: [
'--disable-blink-features=AutomationControlled',
'--disable-features=IsolateOrigins,site-per-process',
'--disable-web-security',
'--disable-notifications',
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-infobars',
'--window-size=1920,1080',
],
proxy: {
server: 'http://proxy.example.com:8080',
},
downloadsPath: './downloads',
env: {
NODE_ENV: 'test',
},
});
使用系统安装的浏览器
// 使用系统 Chrome
const chromeBrowser = await chromium.launch({
channel: 'chrome', // 系统安装的 Chrome
});
// 使用 MS Edge
const edgeBrowser = await chromium.launch({
channel: 'msedge', // 系统安装的 Edge
});
// 使用 Playwright 内置浏览器
const pwBrowser = await chromium.launch(); // 默认使用 Playwright 内置 Chromium
| channel 值 | 说明 |
|---|---|
'chrome' | 系统安装的 Google Chrome |
'msedge' | 系统安装的 Microsoft Edge |
| 不指定 | 使用 Playwright 下载的 Chromium |
总结
理解 Browser / Context / Page 三层架构是掌握 Playwright 的关键。BrowserContext 提供了强大的环境隔离能力,使得多测试用例可以并行执行而互不干扰。利用 storageState 可以高效管理认证状态,多页面管理功能也适用于复杂业务场景的测试。合理配置浏览器启动参数能够满足不同的测试环境需求。