Playwright自动化测试与爬虫实战:从入门到精通

发布时间:2026/6/30 20:06:17
Playwright自动化测试与爬虫实战:从入门到精通 1. 项目概述为什么是 Playwright如果你正在寻找一个能横跨现代浏览器、支持多语言、并且对自动化测试和网页爬虫都极其友好的工具那么 Playwright 绝对值得你投入时间。它不是一个新概念但自微软开源以来其设计理念和工程实现让它迅速成为许多开发者和测试工程师的首选。简单来说Playwright 是一个用于自动化 Chromium、Firefox 和 WebKit 浏览器的 Node.js 库通过一个统一的 API 让你能够用 JavaScript、TypeScript、Python、.NET 和 Java 编写脚本模拟真实用户的操作。我最初接触它是因为厌倦了 Selenium 在某些复杂单页应用SPA上的不稳定以及 Puppeteer 只局限于 Chromium 的“单打独斗”。Playwright 的出现相当于提供了一个“浏览器自动化全家桶”。它原生支持无头模式执行速度快能自动等待元素出现减少了大量编写显式等待的烦恼还能录制操作生成代码对新手极其友好。更重要的是它的网络拦截、文件上传下载、地理位置模拟等高级功能让处理复杂场景变得轻而易举。无论你是想为你的 Web 应用做端到端测试还是需要稳定高效地抓取动态渲染的网页数据Playwright 都能提供一个坚实可靠的解决方案。2. 核心设计理念与优势解析2.1 架构设计超越单个浏览器引擎Playwright 的核心优势首先体现在其架构上。与 Puppeteer主要驱动 Chromium不同Playwright 从一开始就设计为支持多浏览器引擎。它通过为每个浏览器Chromium、Firefox、WebKit提供特定的“浏览器驱动”来实现这一点。当你执行playwright install时它实际上是在后台为你下载并配置这些浏览器的一个特定版本这个版本与 Playwright API 深度集成确保了跨浏览器行为的一致性。这种设计带来了几个直接好处。第一是一致性。你写的同一套脚本可以在三个主流渲染引擎上运行这对于确保 Web 应用跨浏览器兼容性测试至关重要。第二是可靠性。Playwright 团队维护的浏览器版本经过了大量测试避免了因用户本地浏览器版本差异导致的不稳定问题。第三是功能完整性。Playwright 的 API 并非浏览器原生 API 的简单封装而是进行了大量增强例如自动等待、丰富的选择器引擎和强大的网络控制。2.2 自动等待告别“Flaky Tests”的利器“Flaky Tests”不稳定的测试是自动化测试的噩梦其根源常常在于时机问题——脚本执行速度远快于页面加载或元素渲染速度。传统的解决方案是到处插入sleep或显式等待但这既低效又脆弱。Playwright 内置的自动等待机制从根本上解决了这个问题。当执行如page.click(‘button#submit’)这样的操作时Playwright 会执行一系列检查而不仅仅是发送一个点击事件。它会等待这个按钮1) 存在于 DOM 中2) 可见3) 可交互未被禁用、未被其他元素遮挡4) 稳定例如不再有动画效果。只有所有这些条件都满足后才会执行点击。这意味着你绝大多数情况下完全不需要手动编写等待逻辑脚本的稳定性和可读性都大大提升。注意自动等待主要针对如click,fill,check等“动作类”API。对于断言你可能仍需使用expect配合 Playwright Test 的自动等待能力或使用page.waitForSelector等显式等待方法。2.3 强大的选择器引擎定位元素是自动化的基础。Playwright 提供了极其灵活和强大的选择器引擎远不止于 CSS 和 XPath。文本选择器page.click(‘text登录’)可以直接点击包含“登录”文本的元素这对于没有稳定 ID 或 Class 的按钮非常有用。CSS 与 XPath支持所有标准语法。React 与 Vue 组件选择器如果你测试的是基于 React 或 Vue 的应用可以直接通过组件名和属性来定位例如page.click(‘_reactSubmitButton[enabledtrue]’)这大大提升了测试代码与前端组件结构的耦合度使测试更健壮。层叠选择器你可以组合使用多种选择器来精确定位例如page.click(‘#nav-bar text设置’)意思是先在#nav-bar内再找包含“设置”文本的元素。这种丰富的选择策略让你总能找到一种稳定可靠的方式来定位目标元素避免了因前端微小改动如调整 CSS Class 名而导致整个测试套件崩溃的风险。3. 环境准备与安装全攻略3.1 语言运行时选择Node.js 还是 PythonPlaywright 支持多语言但最原生、更新最快的版本是基于 Node.js 的。对于前端团队或主要使用 JavaScript/TypeScript 的开发者选择 Node.js 版本是顺理成章的。它的生态丰富与现代前端工具链集成无缝。Python 版本则更适合数据科学家、后端开发或测试团队他们可能更熟悉 Python 的语法和生态如 pytest。Python 版的 API 与 Node.js 版几乎完全一致只是遵循了 Python 的命名规范如snake_case。我个人建议如果你的项目栈以 JS/TS 为主或需要进行复杂的、与前端构建流程结合的自动化选 Node.js。如果你主要做爬虫、数据分析或者团队测试框架基于 Python如 pytest Playwright那么选 Python。本章节将以Node.js环境为例进行演示因为这是最通用的路径但核心概念完全互通。3.2 Node.js 环境下的详细安装步骤假设你已经在系统上安装了 Node.js版本建议 14 或更高和 npm。步骤一初始化项目与安装 Playwright 库首先为你自动化项目创建一个独立的目录是个好习惯这有助于依赖管理。# 1. 创建项目目录并进入 mkdir my-playwright-project cd my-playwright-project # 2. 初始化 npm 项目生成 package.json npm init -y # 3. 安装 Playwright 核心库 npm install playwright这里安装的是playwright库它包含了控制浏览器的 API。步骤二安装浏览器二进制文件Playwright 需要特定的浏览器版本才能工作。安装库之后你需要下载这些浏览器。# 使用 npx 运行 Playwright 自带的 CLI 工具安装浏览器 npx playwright install这条命令会下载 Chromium、Firefox 和 WebKit 的最新兼容版本。下载的文件体积较大约 1.5 GB请确保网络通畅。实操心得在国内网络环境下直接下载可能会非常慢甚至失败。有两个解决方案使用镜像在运行安装命令前设置环境变量。# Windows (PowerShell) $env:PLAYWRIGHT_DOWNLOAD_HOST “https://npmmirror.com/mirrors/playwright” npx playwright install # macOS/Linux PLAYWRIGHT_DOWNLOAD_HOSThttps://npmmirror.com/mirrors/playwright npx playwright install跳过不需要的浏览器如果你暂时只用 Chromium可以只安装它节省时间和磁盘空间。npx playwright install chromium步骤三验证安装创建一个简单的测试脚本来验证一切是否就绪。新建一个文件test.jsconst { chromium } require(‘playwright’); (async () { const browser await chromium.launch({ headless: false }); // 启动有头浏览器方便观察 const page await browser.newPage(); await page.goto(‘https://example.com’); console.log(await page.title()); // 应该输出 ‘Example Domain’ await page.screenshot({ path: ‘example.png’ }); await browser.close(); })();然后在终端运行node test.js如果成功打开浏览器访问网页打印出标题并截图那么恭喜你Playwright 环境已经准备就绪。3.3 集成开发环境IDE配置建议好的工具能提升效率。我强烈推荐使用Visual Studio Code配合以下扩展Playwright Test for VSCode官方扩展提供测试树视图、一键运行、追踪查看器Trace Viewer集成等强大功能。CodeGPT 或 Cursor这些具备 AI 辅助编程能力的编辑器在你编写 Playwright 脚本时可以快速生成代码片段、解释 API 或调试错误非常高效。例如你可以描述“用 Playwright 登录某个网站”AI 能帮你生成大致的代码框架。在 VSCode 中你还可以配置调试启动项.vscode/launch.json方便对 Playwright 脚本进行断点调试这对于排查复杂交互逻辑中的问题至关重要。4. 从零编写第一个自动化脚本4.1 基础脚本结构剖析让我们从一个更贴近真实场景的例子开始自动在搜索引擎中搜索并获取结果。我们将分解每一步。// search-demo.js const { chromium } require(‘playwright’); (async () { // 1. 启动浏览器 const browser await chromium.launch({ headless: false, // 开发阶段设为 false 便于观察 slowMo: 500, // 将每个操作放慢 500 毫秒像慢动作方便调试 }); // 2. 创建浏览器上下文 (Context) const context await browser.newContext({ viewport: { width: 1920, height: 1080 }, // 设置视口大小 userAgent: ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36…’, // 可选自定义 UA }); // 3. 创建新页面 (Page) const page await context.newPage(); try { // 4. 导航到目标网站 await page.goto(‘https://www.bing.com’, { waitUntil: ‘networkidle’ }); // 等待到网络空闲 // 5. 定位搜索框并输入关键词 // 使用 CSS 选择器定位 input 的 name 属性 await page.fill(‘input[name“q”]’, ‘Playwright 自动化测试’); // 6. 定位搜索按钮并点击 // 这里使用 ID 选择器也可以使用 text‘搜索’ await page.click(‘#search_icon’); // 7. 等待结果页面加载 await page.waitForSelector(‘.b_results’, { state: ‘attached’ }); // 8. 获取第一页所有搜索结果的标题 const titles await page.$$eval(‘.b_algo h2 a’, (links) links.map((link) link.innerText) ); console.log(‘第一页搜索结果标题’); titles.forEach((title, index) { console.log(${index 1}. ${title}); }); // 9. 截图保存证据 await page.screenshot({ path: ‘search-results.png’, fullPage: true }); } catch (error) { console.error(‘脚本执行出错’, error); // 出错时也截图有助于排查 await page.screenshot({ path: ‘error.png’ }); } finally { // 10. 关闭浏览器释放资源 await browser.close(); } })();关键点解析Browser, Context, Page这是 Playwright 的三个核心层级。Browser代表一个浏览器实例Context相当于一个独立的会话隔离的 cookies、localStorage可以创建多个互不干扰的上下文Page就是具体的标签页。这种设计非常适合做多用户场景测试或并行任务。page.fill()和page.click()这些是“动作 API”它们会自动等待目标元素可交互。page.waitForSelector()这是一个显式等待我们用它来确保搜索结果区域已经加载到 DOM 中。page.$$eval()这是一个非常有用的方法它允许你在浏览器环境中执行一个函数并将匹配选择器的元素集合作为参数传入。这里我们用它来提取所有链接的文本。4.2 处理常见交互点击、输入、下拉框除了基础的点击和输入网页中还有更复杂的交互控件。处理下拉选择框Select// 通过 value 选择 await page.selectOption(‘select#country’, ‘CN’); // 通过标签文本选择 await page.selectOption(‘select#country’, { label: ‘中国’ }); // 选择多个值multiple select await page.selectOption(‘select#colors’, [‘red’, ‘blue’]);处理文件上传 Playwright 处理文件上传非常优雅无需模拟复杂的点击文件选择对话框的操作。// 定位文件 input 元素并设置文件路径 await page.setInputFiles(‘input[type“file”]’, ‘./my-file.pdf’); // 上传多个文件 await page.setInputFiles(‘input[type“file”]’, [‘./file1.pdf’, ‘./file2.jpg’]); // 清除已选文件 await page.setInputFiles(‘input[type“file”]’, []);处理弹窗与对话框// 监听并接受 alert 对话框 page.on(‘dialog’, async dialog { console.log(对话框类型: ${dialog.type()}, 信息: ${dialog.message()}); await dialog.accept(); // 点击“确定” // 也可以 dialog.dismiss() 取消 }); // 触发一个会产生 alert 的操作 await page.click(‘button#trigger-alert’);鼠标与键盘高级操作// 鼠标悬停 await page.hover(‘nav .menu-item’); // 拖放操作 await page.dragAndDrop(‘#source’, ‘#target’); // 按下组合键 await page.keyboard.press(‘ControlA’); // 全选 await page.keyboard.type(‘Hello World!’); // 模拟打字4.3 断言与验证确保操作结果符合预期自动化不仅仅是执行步骤更重要的是验证结果。虽然你可以用 Node.js 的原生assert模块但 Playwright Test一个基于 Playwright 的测试运行器提供了更强大的断言库专为异步和 Web 状态设计。即使你不运行测试框架了解其断言思想也很有帮助。// 假设我们使用 Playwright Test 的 expect const { expect } require(‘playwright/test’); // 检查页面标题 await expect(page).toHaveTitle(‘Example Domain’); // 检查 URL 包含特定字符串 await expect(page).toHaveURL(/.*search.*/); // 检查元素可见/包含文本 await expect(page.locator(‘.status’)).toBeVisible(); await expect(page.locator(‘.success-message’)).toHaveText(‘操作成功’); await expect(page.locator(‘.list-item’)).toHaveCount(10); // 检查元素属性 await expect(page.locator(‘input#email’)).toHaveAttribute(‘type’, ‘email’);这些断言都内置了智能等待会在超时时间内不断重试直到条件满足这比简单的if判断要可靠得多。5. 高级功能与实战技巧5.1 网络请求拦截与模拟Mocking这是 Playwright 的王牌功能之一。你可以监听、修改或阻断任何网络请求这对于测试和爬虫场景极其有用。监听所有请求和响应page.on(‘request’, request { console.log( ${request.method()} ${request.url()}); }); page.on(‘response’, response { console.log( ${response.status()} ${response.url()}); });拦截并修改请求// 拦截所有对图片的请求并阻止加载加速页面打开 await page.route(‘**/*.{png,jpg,jpeg,svg,gif}’, route route.abort()); // 拦截特定 API 请求并返回模拟数据 await page.route(‘https://api.example.com/user/**’, async route { const mockData { name: ‘Mock User’, id: 123 }; await route.fulfill({ status: 200, contentType: ‘application/json’, body: JSON.stringify(mockData), }); }); // 修改请求头 await page.route(‘**/*’, route { const headers { …route.request().headers(), ‘X-Custom-Header’: ‘my-value’ }; route.continue({ headers }); });5.2 处理 iframe 和多页面多标签页现代网页中 iframe 很常见Playwright 可以轻松切入。// 通过 name 或 URL 定位 iframe 元素 const frame page.frame({ name: ‘chat-widget’ }); // 或者通过选择器定位 iframe 元素再获取其 contentFrame const frameElement await page.$(‘iframe.payment’); const frame await frameElement.contentFrame(); // 在 iframe 上下文中操作 await frame.fill(‘input’, ‘data inside iframe’); await frame.click(‘button’);处理新打开的标签页// 监听新页面标签页打开事件 const [newPage] await Promise.all([ page.context().waitForEvent(‘page’), // 等待新页面事件 page.click(‘a[target“_blank”]’), // 触发打开新页面的操作 ]); // 现在可以操作 newPage 了 await newPage.waitForLoadState(); console.log(await newPage.title());5.3 使用追踪查看器Trace Viewer进行调试当测试失败时光看日志和截图往往不够。Playwright 的 Trace Viewer 可以录制测试的完整过程像电影一样回放查看每个时间点的 DOM 快照、控制台日志、网络请求和脚本执行情况。在脚本中启用追踪const browser await chromium.launch(); const context await browser.newContext(); // 启动追踪 await context.tracing.start({ screenshots: true, snapshots: true }); const page await context.newPage(); // … 执行你的自动化操作 … // 停止追踪并保存文件 await context.tracing.stop({ path: ‘trace.zip’ }); await browser.close();执行后会产生一个trace.zip文件。使用 Playwright CLI 打开它npx playwright show-trace trace.zip一个图形化界面会打开你可以逐操作查看发生了什么是排查疑难杂症的终极武器。5.4 并行执行与性能优化对于需要处理大量页面的爬虫任务串行执行效率低下。Playwright 支持利用多个浏览器上下文或页面进行并行操作。const { chromium } require(‘playwright’); const urlsToProcess [‘url1’, ‘url2’, ‘url3’, …]; (async () { const browser await chromium.launch(); // 限制并发数为5避免资源耗尽 const concurrency 5; const promises []; for (let i 0; i Math.min(concurrency, urlsToProcess.length); i) { promises.push(processTask(browser, i)); } await Promise.all(promises); await browser.close(); })(); async function processTask(browser, workerId) { // 每个任务使用独立的上下文完全隔离 const context await browser.newContext(); const page await context.newPage(); while (urlsToProcess.length 0) { const url urlsToProcess.pop(); if (!url) break; console.log(Worker ${workerId} processing ${url}); try { await page.goto(url); // … 处理该页面的逻辑 … } catch (error) { console.error(Worker ${workerId} failed on ${url}:, error); } // 重要每个任务完成后清理页面状态比如清除 cookies避免串数据 await context.clearCookies(); } await context.close(); }注意事项并行化会显著增加内存和 CPU 占用。你需要根据机器性能调整并发数。另外目标网站可能有反爬机制过高的并发请求可能导致 IP 被暂时封锁需要合理设置延迟或使用代理池。6. 常见问题排查与性能调优6.1 元素定位失败原因与解决方案这是新手最常遇到的问题。脚本报错TimeoutError: page.click: Timeout 30000ms exceeded。排查步骤确认选择器是否正确使用浏览器的开发者工具F12检查元素。确保你使用的选择器能唯一定位到目标元素。优先使用稳定的属性如>await page.route(‘**/*.{png,jpg,jpeg,svg,gif,css,woff2,js}’, route { if (route.request().url().includes(‘ads’)) { route.abort(); } else { route.continue(); } });复用浏览器上下文如果一系列操作是在同一个网站会话下尽量复用browser和context而不是每个任务都启动关闭这能节省大量启动时间。并行执行如 5.4 节所述对于独立任务使用并行化。6.3 应对反爬虫机制一些网站会检测自动化脚本。Playwright 提供了一些特性来让你的脚本更像真人。使用有头模式并降低速度headless: false, slowMo: 100。慢动作模拟了人的操作间隔。提供完整的 User-Agent 和视口创建 context 时设置一个常见的桌面版 UA 和合理的视口大小。注入真实的鼠标移动轨迹Playwright 的page.mouse.move(x, y)是直线移动容易被检测。可以自己实现一个带随机曲线的移动函数。使用代理 IP在创建浏览器上下文时配置代理。const context await browser.newContext({ proxy: { server: ‘http://my-proxy.com:8080’ } });谨慎使用page.evaluate在页面上下文中执行脚本可能会暴露一些自动化特征。非必要不滥用。6.4 内存泄漏与资源管理长时间运行脚本可能导致内存增长。及时关闭页面和上下文await page.close(),await context.close()。一个常见的模式是在try...finally块中确保资源被关闭。避免循环引用在page.evaluate中传递大型对象时要小心。监控内存可以使用 Node.js 的process.memoryUsage()定期打印内存使用情况。定期重启浏览器实例对于需要 24 小时运行的守护进程可以设定在运行若干小时后完全关闭并重启浏览器实例以释放可能积累的内存碎片。7. 项目组织与持续集成当你的 Playwright 脚本从一个简单的 demo 演变成包含几十上百个测试用例或爬虫任务的项目时良好的组织结构至关重要。7.1 使用 Playwright Test 测试运行器对于自动化测试场景强烈建议直接使用playwright/test框架而不是裸用 Playwright 库。它提供了测试结构、断言、夹具Fixtures、并行化、报告等一站式解决方案。安装与基础使用npm init playwrightlatest这个命令会引导你完成安装、创建配置文件、示例测试和 GitHub Actions 集成配置。一个基本的测试文件// tests/example.spec.js const { test, expect } require(‘playwright/test’); test(‘basic test’, async ({ page }) { // ‘page’ fixture 自动提供 await page.goto(‘https://example.com’); await expect(page).toHaveTitle(‘Example Domain’); }); test(‘click test’, async ({ page }) { await page.goto(‘https://example.com’); await page.click(‘a’); // … });使用npx playwright test运行所有测试。它自动并行执行、生成 HTML 报告、支持重试失败用例管理起来比自行编排脚本要专业和轻松得多。7.2 配置文件playwright.config.js详解Playwright Test 的核心是配置文件它允许你集中管理所有设置。// playwright.config.js const { defineConfig, devices } require(‘playwright/test’); module.exports defineConfig({ timeout: 30000, // 每个测试的超时时间 retries: process.env.CI ? 2 : 0, // CI 环境下失败重试 2 次 workers: process.env.CI ? 2 : 4, // 并行工作进程数CI 环境少一些 reporter: [[‘html’, { outputFolder: ‘playwright-report’ }]], // 生成 HTML 报告 use: { baseURL: ‘https://my-app.com’, // 基础 URL测试中可用相对路径 headless: true, // 全局无头模式 viewport: { width: 1280, height: 720 }, ignoreHTTPSErrors: true, screenshot: ‘only-on-failure’, // 失败时自动截图 video: ‘retain-on-failure’, // 失败时保留录像 trace: ‘on-first-retry’, // 首次重试时记录追踪 }, projects: [ // 定义多项目例如在不同浏览器上运行测试 { name: ‘chromium’, use: { …devices[‘Desktop Chrome’] }, }, { name: ‘firefox’, use: { …devices[‘Desktop Firefox’] }, }, ], });7.3 集成到 CI/CD 流水线以 GitHub Actions 为例自动化测试只有在持续集成中自动运行才有最大价值。以下是一个简单的 GitHub Actions 工作流配置# .github/workflows/playwright.yml name: Playwright Tests on: [push, pull_request] jobs: test: timeout-minutes: 60 runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - uses: actions/setup-nodev4 with: node-version: ‘18’ - name: Install dependencies run: npm ci - name: Install Playwright Browsers run: npx playwright install --with-deps chromium # CI 中通常只安装一个浏览器 - name: Run Playwright tests run: npx playwright test - uses: actions/upload-artifactv4 if: always() # 无论测试成功与否都上传报告 with: name: playwright-report path: playwright-report/ retention-days: 30这个工作流会在每次代码推送或拉取请求时自动安装环境、运行测试并将详细的 HTML 报告上传供下载查看。从安装一个库到编写第一个脚本再到组织一个健壮的项目并集成到自动化流程中Playwright 提供的是一整套从入门到精通的完整工具链。它的设计始终围绕着稳定、快速和开发者友好。无论是应对复杂的单页应用测试还是需要处理 JavaScript 渲染的爬虫任务花时间掌握 Playwright 的细节都能让你在未来的自动化工作中事半功倍。在实际项目中我最深的体会是前期在元素选择器稳定性、页面等待条件和错误处理上多花一点心思后期维护脚本的成本就会指数级下降。