一、Playwright概述
1.1 诞生背景
Playwright 是由微软在2020年初推出的开源跨浏览器自动化框架,由原 Puppeteer 团队成员开发。它继承了 Puppeteer 的优秀设计理念,但更进一步实现了对 Chromium、Firefox 和 WebKit 三大浏览器引擎的统一支持。与 Selenium 等传统自动化框架不同,Playwright 在设计之初就瞄准了现代 Web 应用的特点——SPA 单页应用、AJAX 动态渲染、Shadow DOM 等,提供了更加原生和可靠的自动化能力。自发布以来,Playwright 迅速成为 Web 自动化领域的重要力量,被广泛应用于端到端测试、网页爬虫、数据采集、自动化运维等场景。
1.2 与 Selenium 核心对比
| 对比维度 | Playwright | Selenium |
| 浏览器支持 | Chromium + Firefox + WebKit(原生支持) | Chrome/Firefox/Safari/Edge(需对应 Driver) |
| 自动等待 | 内置自动等待机制,无需显式等待 | 需要显式/隐式等待,否则易报 NoSuchElement |
| API 风格 | 异步优先(Async/Await),同步模式可选 | 同步为主,异步需额外封装 |
| 网络拦截 | 原生 route API,支持 mock 和修改 | 需通过 DevTools Protocol 或代理 |
| 安装复杂度 | pip install 一键安装浏览器 | 需额外下载对应版本 Driver |
| 速度 | 更快(浏览器上下文隔离,无 Driver 通信开销) | 相对较慢 |
| 执行环境 | Browser Context 隔离,轻量级 | 每个实例相对重型 |
1.3 核心优势详解
自动等待(Auto-Waiting):Playwright 在执行操作前会自动等待元素可见、可用(如等待按钮可点击),开发者无需手动添加 time.sleep 或 WebDriverWait。这一机制大幅减少了一类常见的自动化脚本不稳定问题。
多浏览器统一 API:同一套 API 可以无缝运行在 Chromium、Firefox 和 WebKit 上,实现真正的跨浏览器兼容性测试。这对于需要确保 Web 应用在 Chrome、Edge、Safari 等不同浏览器中行为一致的场景尤为重要。
网络拦截与模拟:Playwright 提供了强大的网络控制能力,可以拦截、修改、模拟任意网络请求和响应,特别适合测试边界条件、模拟后端异常、在无后端情况下进行前端测试。
浏览器上下文:每个测试或任务可以在独立的浏览器上下文中运行,上下文之间完全隔离(缓存、Cookies、LocalStorage 互不干扰),无需启动多个浏览器实例。
1.4 安装
# 安装 Playwright 核心库
pip install playwright
# 安装 Chromium、Firefox、WebKit 浏览器(首次运行)
playwright install
# 仅安装 Chromium(节省磁盘空间)
playwright install chromium
安装完成后,可以使用以下代码验证版本并启动浏览器。
import playwright
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto("https://www.baidu.com")
print(page.title())
browser.close()
要点提示:Playwright 提供了 sync_api(同步)和 async_api(异步)两套 API。同步 API 适合快速上手和简单脚本;异步 API 适合高并发场景和与其他异步框架集成。建议初学者先掌握同步 API,再根据需求升级到异步版本。
二、环境与配置
2.1 浏览器安装与管理
Playwright 允许开发者按需安装特定浏览器,无须依赖系统已安装的浏览器版本。通过命令行工具可以精细控制安装内容。安装后的浏览器位于用户目录下的 playwright 文件夹中,与系统浏览器完全隔离,避免版本冲突。
# 查看已安装的浏览器
playwright install --list
# 安装所有支持的浏览器(约 500MB-1GB)
playwright install
# 安装特定浏览器
playwright install firefox
playwright install webkit
# 安装带系统依赖(Linux 环境)
playwright install-deps
2.2 同步与异步 API 选择
Playwright 的同步 API 是最直观的入口,适合大多数脚本化场景。异步 API 则面向需要并发处理多个浏览器实例或与其他异步 I/O 操作配合的场景。二者的核心概念(Browser、Context、Page)完全一致,仅在调用风格上有所不同。
# 同步 API(推荐初学者使用)
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://example.com")
content = page.content()
browser.close()
# 异步 API(适合高并发场景)
import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
await page.goto("https://example.com")
content = await page.content()
await browser.close()
asyncio.run(main())
2.3 Browser、Context 与 Page 模型
Playwright 采用三层架构:Browser(浏览器实例)→ BrowserContext(浏览器上下文)→ Page(页面标签页)。每个 Browser 可以创建多个独立的 Context,Context 之间完全隔离(Cookies、缓存、LocalStorage 互不影响),每个 Context 下又可以管理多个 Page。这一设计使得多用户、多会话场景的模拟变得异常简单。
# 浏览器上下文隔离示例
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
# 上下文 A(模拟用户 A)
context_a = browser.new_context()
page_a = context_a.new_page()
page_a.goto("https://example.com")
page_a.evaluate("localStorage.setItem('user', 'A')")
# 上下文 B(模拟用户 B,完全隔离)
context_b = browser.new_context()
page_b = context_b.new_page()
page_b.goto("https://example.com")
print(page_b.evaluate("localStorage.getItem('user')")) # 输出 None
browser.close()
2.4 浏览器启动选项
Playwright 提供了丰富的启动参数,可以控制浏览器的头模式、窗口大小、代理、调试端口等。这些选项在开发和调试过程中非常有用。
# 常用浏览器启动选项
browser = p.chromium.launch(
headless=False, # 有头模式(调试时可见)
slow_mo=500, # 操作间延迟 500ms(便于观察)
args=['--start-maximized'], # 启动参数
proxy={"server": "http://127.0.0.1:8080"}, # 代理设置
downloads_path="./downloads" # 下载目录
)
三、页面导航操作
3.1 基本页面导航
Playwright 的页面导航以 goto 方法为核心,支持完整的页面加载生命周期控制。开发者可以指定等待策略(DOMContentLoaded、Load、NetworkIdle),以适应不同场景。与 Selenium 不同,Playwright 的 goto 方法默认会等待页面加载完成再返回,避免了后续操作因页面未加载完全而失败。
# 基本导航与等待策略
page.goto("https://example.com")
page.goto("https://example.com", wait_until="domcontentloaded") # DOM 就绪即返回
page.goto("https://example.com", wait_until="networkidle") # 网络空闲后返回
3.2 前进后退与刷新
浏览器自动化中,模拟用户的前进、后退、刷新操作是最基础的能力。Playwright 将这些操作抽象为 page 的方法,调用方式直观且稳定。
# 导航历史操作
page.goto("https://example.com/page1")
page.goto("https://example.com/page2")
page.go_back() # 后退到 page1
page.go_forward() # 前进到 page2
page.reload() # 刷新当前页面
# 获取当前页面信息
print(page.url) # 当前页面 URL
print(page.title()) # 当前页面标题
3.3 等待页面加载
页面加载状态的管理是自动化稳定性的关键。Playwright 提供了 wait_for_load_state 方法来精确控制页面加载阶段,同时支持自定义超时时间,确保脚本在慢速网络环境下也能稳定运行。
# 等待页面加载状态
page.goto("https://example.com")
# 等待特定加载状态
page.wait_for_load_state("networkidle") # 等待网络空闲
page.wait_for_load_state("domcontentloaded") # 等待 DOM 就绪
# 等待导航完成(点击链接后等待新页面加载)
with page.expect_navigation():
page.click("a.next-page")
# 等待新标签页打开
with page.expect_popup() as popup_info:
page.click("a[target='_blank']")
new_page = popup_info.value
new_page.wait_for_load_state()
3.4 多标签页管理
Playwright 通过 Context 下的多个 Page 对象管理多标签页。每个 Page 代表一个标签页,可以通过 pages 属性获取所有已打开的标签页,并通过 context 创建新的标签页。
# 多标签页管理
context = browser.new_context()
page1 = context.new_page()
page1.goto("https://example.com")
page2 = context.new_page()
page2.goto("https://python.org")
# 切换标签页
page1.bring_to_front()
print(page1.title())
# 列出所有标签页
for p in context.pages:
print(p.url, p.title())
四、元素定位
4.1 Locator API 核心概念
Playwright 引入了全新的 Locator(定位器)概念,与传统的 find_element 方式不同,Locator 是一种"惰性"定位策略——它不会在创建时立即查找元素,而是在执行操作时才会真正定位并交互。这种设计使得定位器可以在页面变化后仍然有效,同时内置了自动等待机制,大幅提升了脚本的健壮性。
4.2 CSS 选择器与 XPath
Playwright 完整支持 CSS 选择器和 XPath 定位方式,语法与 Selenium 中的用法一致,迁移成本极低。
# CSS 选择器定位
page.locator("button.submit").click()
page.locator("#username").fill("admin")
page.locator("div.content > p:first-child").text_content()
# XPath 定位
page.locator("xpath=//div[@class='header']//a").click()
page.locator("xpath=//input[@type='submit']").click()
# 复合条件定位
page.locator("input:has-text('搜索')").click()
page.locator("button:visible").first().click()
4.3 文本选择器
文本选择器是 Playwright 的特色功能,允许通过可见文本内容直接定位元素,特别适合没有稳定 CSS class 的国际化应用或内容密集型的页面。
# 文本选择器
page.get_by_text("登录").click() # 精确匹配可见文本
page.get_by_text("提交订单", exact=True).click() # 精确匹配(无子字符串)
page.locator("text=立即购买").click()
# 标签 + 文本组合
page.locator("button:has-text('确认')").click()
page.locator("div:has-text('温馨提示')").text_content()
# 包含特定文本的元素(用于等待)
page.get_by_text("操作成功").wait_for()
4.4 角色选择器(ARIA)
Playwright 支持通过 ARIA 角色和标签进行定位,更接近用户与页面的交互方式。这种定位方式对无障碍友好型应用尤为有效,也是 Playwright 推荐的定位方式之一。
# 角色选择器
page.get_by_role("button", name="提交").click()
page.get_by_role("link", name="更多").click()
page.get_by_role("heading", name="产品介绍").text_content()
page.get_by_role("textbox", name="用户名").fill("admin")
page.get_by_role("checkbox", name="同意协议").check()
4.5 层级定位与过滤
在实际页面中,元素往往嵌套在复杂的 DOM 结构中。Playwright 提供了链式调用和过滤机制,可以精确地在层级结构中定位目标元素。
# 层级链式定位
parent = page.locator(".product-list")
child = parent.locator(".product-item")
link = child.locator("a.view-detail")
# 过滤定位器
rows = page.locator("table tr")
rows.filter(has_text="待处理").locator(".action-btn").click()
# 获取多个匹配元素
items = page.locator(".list-item").all()
for item in items:
print(item.text_content())
# 按索引定位
page.locator(".item").first.click()
page.locator(".item").nth(2).click()
page.locator(".item").last.click()
五、交互操作
5.1 基本交互:点击、输入、选择
Playwright 提供了丰富且稳定的交互方法,所有交互操作都内置了自动等待机制。点击操作前会自动等待元素可见和可用,输入操作会自动清空原有内容并触发 input 事件,选择操作会自动等待选项加载完成。
# 点击操作
page.click("button#submit") # 普通点击
page.dblclick(".item") # 双击
page.click("button", button="right") # 右键
page.click(".target", click_count=3) # 三击
# 输入操作
page.fill("#username", "admin") # 清空后输入
page.type("#search", "Playwright", delay=100) # 模拟真实输入延迟
page.locator("#email").press_sequentially("test@example.com", delay=50)
5.2 下拉选择与复选框
# 下拉选择框
page.select_option("select#city", "shanghai") # 按 value 选择
page.select_option("select#city", label="上海") # 按标签选择
page.select_option("select#tags", index=2) # 按索引选择
page.select_option("select#tags", ["tag1", "tag3"]) # 多选
# 复选框和单选框
page.check("input#agree") # 勾选
page.uncheck("input#agree") # 取消勾选
page.set_checked("input#switch", True) # 强制设定状态
5.3 拖拽与文件上传
# 拖拽操作
source = page.locator("#drag-source")
target = page.locator("#drop-target")
source.drag_to(target) # 拖拽到目标
# 文件上传
page.set_input_files("input[type='file']", "photo.jpg") # 上传单个文件
page.set_input_files("input[type='file']", ["a.pdf", "b.pdf"]) # 上传多个文件
# 移除已选择的文件
page.set_input_files("input[type='file']", [])
5.4 键盘与焦点操作
# 键盘操作
page.keyboard.press("Enter") # 按回车键
page.keyboard.press("Control+A") # 全选
page.keyboard.press("Control+C") # 复制
page.keyboard.type("Hello World") # 逐字输入
page.keyboard.down("Shift") # 按住 Shift
page.keyboard.up("Shift") # 释放 Shift
# 焦点管理
page.focus("#search-input") # 设置焦点
page.locator("#search-input").focus()
page.locator("#search-input").blur() # 移除焦点
六、自动等待
6.1 自动等待机制原理
Playwright 的自动等待是它最显著的优势之一。当执行 click、fill 等操作时,Playwright 会自动检查一系列元素状态:元素是否附加到 DOM、是否可见、是否稳定(未持续变化)、是否没有被遮挡、是否启用。每种操作都对应一组等待条件,开发者无需手动调用等待方法,框架会自动轮询直到条件满足或超时。这一机制是 Playwright 脚本比 Selenium 脚本更加稳定的核心原因。
6.2 显式等待
尽管自动等待覆盖了大多数场景,但在某些复杂情况下(如等待特定文本出现、等待请求完成),仍需要显式等待方法。Playwright 提供了 wait_for 系列方法以应对这些场景。
# 显式等待元素
page.locator(".loading-finished").wait_for() # 等待元素出现在 DOM 中
page.locator(".result").wait_for(state="visible") # 等待元素可见
page.locator(".old-content").wait_for(state="hidden") # 等待元素隐藏
page.locator(".target").wait_for(state="attached") # 等待元素附加到 DOM
# 等待特定文本内容
page.get_by_text("加载完毕").wait_for()
page.locator(".status").wait_for(timeout=10000) # 自定义超时(毫秒)
6.3 状态检查方法
Playwright 提供了一系列断言检查方法,用于验证元素的当前状态。这些方法配合自动等待使用,可以在条件不满足时自动重试直到超时。
# 元素状态检查
locator = page.locator("#submit-btn")
locator.wait_for() # 等待元素就绪
locator.is_visible() # 检查是否可见
locator.is_enabled() # 检查是否启用
locator.is_checked() # 检查是否勾选
locator.is_editable() # 检查是否可编辑
# 状态断言(自动重试直到条件满足或超时)
from playwright.sync_api import expect
expect(locator).to_be_visible() # 断言元素可见
expect(locator).to_be_enabled() # 断言元素可用
expect(locator).to_have_text("提交成功") # 断言文本内容
expect(page).to_have_title("首页") # 断言页面标题
expect(page.locator("table tr")).to_have_count(5) # 断言元素数量
6.4 超时控制
# 超时控制
# 全局超时设置(影响所有操作)
browser = p.chromium.launch()
context = browser.new_context()
page = context.new_page()
page.set_default_timeout(15000) # 15秒超时
# 单次操作超时
page.click(".slow-element", timeout=30000) # 30秒超时
# 导航超时
page.goto("https://example.com", timeout=60000) # 60秒导航超时
要点提示:Playwright 的默认超时时间为 30 秒。如果脚本在不稳定的网络环境下运行,建议增加全局超时时间。同时,避免滥用显式等待——优先依赖自动等待机制,只在特殊场景下使用 wait_for 和 expect。
七、网络拦截
7.1 Route 拦截与修改请求
Playwright 的 route API 提供了完整的网络请求生命周期控制能力。通过拦截请求,开发者可以修改请求头、请求体、URL 参数,甚至可以完全阻止某些请求(如广告、统计脚本)。与 Selenium 的方案相比,Playwright 的网络拦截更加原生和高效,无需配置代理服务器。
# 拦截并修改请求
page.route("**/api/**", lambda route:
route.continue_(headers={
"Authorization": "Bearer fake-token"
})
)
# 拦截并中止特定请求
page.route("**/*.png", lambda route: route.abort())
page.route("**/analytics.js", lambda route: route.abort())
# 完整请求检查
def handle_request(route):
request = route.request
print(f"请求: {request.method} {request.url}")
print(f"请求头: {request.headers}")
route.continue_()
page.route("**", handle_request)
7.2 Mock API 响应
在前端开发与测试中,往往需要在不依赖真实后端的情况下模拟 API 响应。Playwright 可以拦截请求并直接返回自定义的 Mock 数据,极大提升了测试效率。
# Mock API 响应
page.route("**/api/users", lambda route:
route.fulfill(
status=200,
content_type="application/json",
body='{"users": [{"id": 1, "name": "张三"}]}'
)
)
# 模拟网络错误
page.route("**/api/checkout", lambda route:
route.fulfill(status=500, body='{"error": "服务器繁忙"}')
)
# 模拟延迟响应
import time
page.route("**/api/payments", lambda route:
(time.sleep(5), route.continue_())[1]
)
7.3 请求抓取与分析
Playwright 可以捕获页面发出的所有网络请求及其响应,这对于调试、性能分析和数据采集非常有价值。
# 请求捕获
requests = []
def on_request(request):
requests.append({
"url": request.url,
"method": request.method,
"headers": request.headers,
"resource_type": request.resource_type
})
page.on("request", on_request)
page.goto("https://example.com")
print(f"共捕获 {len(requests)} 个请求")
# 响应捕获
page.on("response", lambda response:
print(f"响应: {response.status} {response.url}")
)
7.4 模拟网络条件
在真实场景中,用户可能处于各种不同网络条件下。Playwright 允许模拟离线模式、慢速网络等场景,帮助开发者验证应用在弱网环境下的表现。
# 模拟网络条件
context = browser.new_context()
# 离线模式
context.set_offline(True)
# 模拟慢速网络(3G 条件)
context = browser.new_context(
extra_http_headers={
"Accept-Encoding": "gzip"
}
)
context.route("**", lambda route:
(time.sleep(1), route.continue_())[1]
)
# 模拟不同带宽(限制下载/上传速度,需配合代理使用)
# Playwright 本身不直接限制带宽,但可以通过全局延迟模拟
browser = p.chromium.launch(slow_mo=200) # 200ms 操作延迟
八、多浏览器与移动端
8.1 三大浏览器引擎
Playwright 最强大的特性之一是对 Chromium、Firefox 和 WebKit 三大浏览器引擎的统一支持。这意味着开发者可以使用同一套 API 在 Chrome、Edge、Safari 和 Firefox 上运行自动化脚本,特别适合跨浏览器兼容性测试场景。每个浏览器引擎的启动方式完全一致,只需修改 browser_type 名称即可切换。
# Chromium(Chrome、Edge 等)
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://example.com")
# Firefox
browser = p.firefox.launch()
page = browser.new_page()
page.goto("https://example.com")
# WebKit(Safari)
browser = p.webkit.launch()
page = browser.new_page()
page.goto("https://example.com")
8.2 移动设备模拟
Playwright 内置了丰富的移动设备描述符,可以精确模拟 iPhone、iPad、Android 等设备的浏览器行为。模拟内容包括视口大小、设备像素比、用户代理字符串、触控事件等,使开发者无需真实设备即可进行移动端测试。
# 移动设备模拟
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
# iPhone 13 模拟
iphone_13 = p.devices["iPhone 13"]
context = browser.new_context(
**iphone_13,
locale="zh-CN"
)
page = context.new_page()
page.goto("https://example.com")
page.screenshot(path="iphone13.png", full_page=True)
# 查看所有可用设备
print(list(p.devices.keys())[:10])
8.3 地理定位、时区与语言设置
Playwright 支持精细化配置浏览器上下文的地理位置、时区、语言偏好等参数,这在测试国际化应用和地理位置相关功能时非常有用。
# 地理位置模拟
context = browser.new_context(
geolocation={"latitude": 31.2304, "longitude": 121.4737}, # 上海
permissions=["geolocation"],
locale="zh-CN",
timezone_id="Asia/Shanghai"
)
page = context.new_page()
page.goto("https://maps.example.com")
# 多语言测试
en_context = browser.new_context(locale="en-US")
jp_context = browser.new_context(locale="ja-JP")
de_context = browser.new_context(locale="de-DE")
8.4 浏览器持久化与认证
# 保存登录状态
context = browser.new_context()
page = context.new_page()
page.goto("https://example.com/login")
page.fill("#username", "admin")
page.fill("#password", "123456")
page.click("button[type='submit']")
context.storage_state(path="login-state.json") # 保存 Cookies 和存储
# 复用登录状态
context = browser.new_context(storage_state="login-state.json")
page = context.new_page()
page.goto("https://example.com/dashboard") # 已登录状态
九、截图与录制
9.1 截图功能
Playwright 提供了强大的截图能力,支持对整个页面、特定元素进行截图,也可以生成全页截图(包含滚动内容)。截图输出为 PNG 格式,可以嵌入测试报告或作为调试证据使用。
# 全页截图
page.screenshot(path="fullpage.png", full_page=True)
# 元素截图
page.locator(".highlight-box").screenshot(path="element.png")
# 视口截图
page.screenshot(path="viewport.png") # 仅当前可见区域
# 截图质量控制
page.screenshot(
path="highres.png",
full_page=True,
scale="css", # 使用 CSS 像素
quality=90 # JPEG 质量(仅 JPEG 格式)
)
9.2 视频录制
Playwright 支持在浏览器上下文中录制视频,完整记录测试执行过程。这对于失败测试的复盘和非技术人员的沟通非常有用。视频录制需要安装 FFmpeg 支持。
# 录制测试视频
context = browser.new_context(
record_video_dir="videos/", # 视频输出目录
record_video_size={"width": 1280, "height": 720} # 视频尺寸
)
page = context.new_page()
page.goto("https://example.com")
page.click("button")
context.close() # 关闭上下文时停止录制
# 获取视频文件路径
video_path = page.video.path()
print(f"视频保存至: {video_path}")
9.3 PDF 生成
Playwright 可以将页面内容生成为 PDF 文件,这在生成报告、发票、存档等办公自动化场景中非常实用。PDF 生成功能目前仅在 Chromium(无头模式)下可用。
# PDF 生成
page.goto("https://example.com/report")
page.pdf(
path="report.pdf",
format="A4", # 纸张大小(A4/Letter 等)
margin={
"top": "20mm",
"bottom": "20mm",
"left": "15mm",
"right": "15mm"
},
print_background=True, # 打印背景颜色和图片
scale=0.9 # 缩放比例
)
9.4 追踪记录与交互录制
# 启用追踪记录(调试利器)
context = browser.new_context()
context.tracing.start(
screenshots=True,
snapshots=True,
sources=True
)
page.goto("https://example.com")
page.click("button")
context.tracing.stop(path="trace.zip") # 可在 Playwright Trace Viewer 中查看
# 使用代码生成器(命令行交互)
# playwright codegen https://example.com
# 以上命令会打开浏览器录制窗口,操作将自动生成 Python 代码
十、实战案例
10.1 案例一:电商价格监控
本案例演示如何使用 Playwright 定时抓取电商商品页面的价格信息,并在价格低于阈值时发送通知。该场景充分展示了 Playwright 在页面解析、数据提取和定时任务方面的综合能力。
# 电商价格监控
from playwright.sync_api import sync_playwright
import smtplib
from email.message import EmailMessage
def check_price(url, threshold):
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto(url, timeout=30000)
# 提取商品名称和价格
name = page.locator(".product-title").text_content()
price_text = page.locator(".price-current").text_content()
# "¥299.00" -> 299.00
price = float(price_text.replace("¥", "").strip())
print(f"商品: {name}, 当前价格: {price}")
if price <= threshold:
send_alert(name, price, threshold)
browser.close()
def send_alert(name, price, threshold):
msg = EmailMessage()
msg.set_content(f"商品 [{name}] 已降至 ¥{price},低于阈值 ¥{threshold}")
msg["Subject"] = "价格监控提醒"
msg["To"] = "user@example.com"
# 使用 SMTP 发送邮件(需配置)
# with smtplib.SMTP("smtp.example.com") as server:
# server.send_message(msg)
# 监控多个商品
products = [
("https://shop.example.com/product/001", 200),
("https://shop.example.com/product/002", 350),
]
for url, threshold in products:
check_price(url, threshold)
10.2 案例二:自动化 UI 回归测试
在前端项目迭代中,UI 回归测试是确保功能正常的重要手段。Playwright 提供了完整的测试框架集成,支持浏览器矩阵测试、截图对比和并行执行。
# UI 回归测试脚本
from playwright.sync_api import sync_playwright
import os
def test_login_flow(browser_type="chromium"):
with sync_playwright() as p:
browser = getattr(p, browser_type).launch()
context = browser.new_context(
viewport={"width": 1920, "height": 1080}
)
page = context.new_page()
# 测试步骤 1:访问登录页
page.goto("https://example.com/login")
assert page.title() == "登录"
# 测试步骤 2:错误密码提示
page.fill("#username", "admin")
page.fill("#password", "wrong")
page.click("button[type='submit']")
expect(page.get_by_text("密码错误")).to_be_visible()
# 测试步骤 3:成功登录
page.fill("#password", "correct123")
page.click("button[type='submit']")
expect(page).to_have_url("https://example.com/dashboard")
# 截图对比基准
page.screenshot(path=f"screenshots/{browser_type}_dashboard.png")
browser.close()
# 跨浏览器执行
for browser_type in ["chromium", "firefox", "webkit"]:
test_login_flow(browser_type)
print(f"{browser_type} 测试通过")
10.3 案例三:跨浏览器兼容性测试
Web 应用需要在不同浏览器上保持一致的表现。Playwright 的矩阵测试能力使得跨浏览器兼容性验证变得简单高效。
# 跨浏览器兼容性测试矩阵
from playwright.sync_api import sync_playwright
import json
def run_compatibility_check(url, test_cases):
results = {}
with sync_playwright() as p:
browsers = {
"Chrome": p.chromium.launch(),
"Firefox": p.firefox.launch(),
"Safari": p.webkit.launch(),
}
for name, browser in browsers.items():
print(f"正在测试: {name}")
page = browser.new_page()
page.goto(url)
results[name] = {}
for case_name, selector in test_cases.items():
try:
element = page.locator(selector)
results[name][case_name] = {
"exists": element.is_visible(),
"text": element.text_content()[:50] if element.is_visible() else None,
}
except Exception as e:
results[name][case_name] = {"error": str(e)}
browser.close()
with open("compatibility_report.json", "w", encoding="utf-8") as f:
json.dump(results, f, ensure_ascii=False, indent=2)
return results
# 执行测试
url = "https://example.com/product"
cases = {
"页面标题": "h1",
"购买按钮": "button.buy-now",
"商品列表": ".product-grid",
"搜索框": "input#search",
}
report = run_compatibility_check(url, cases)
print(json.dumps(report, ensure_ascii=False, indent=2))
知识回顾:Playwright 作为新一代跨浏览器自动化工具,其核心优势在于:自动等待机制大幅提升脚本稳定性;统一 API 支持 Chromium/Firefox/WebKit 三大引擎;Context 上下文隔离设计简洁高效;网络拦截 route API 强大灵活。建议在实际项目中逐步从 Selenium 迁移到 Playwright,以享受更流畅的开发体验和更高的运行效率。