一、什么是 Playwright
1.1 定义
Playwright 是一个 开源的浏览器自动化框架,由 Microsoft 开发并维护。它提供了一套统一的 API,可以用代码控制 Chromium、Firefox 和 WebKit 三大浏览器引擎,完成包括打开网页、点击、输入、数据抓取、截图、文件上传等在内的几乎所有浏览器操作。
1.2 一句话理解
Playwright = 用代码驱动浏览器,完成一切网页操作
1.3 核心能力一览
打开网页 ──→ 点击 / 输入 ──→ 数据抓取 ──→ 截图 / 录屏 ──→ 自动化测试
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
goto() click() / text_content() screenshot() assert /
fill() inner_text() video() expect()
1.4 发展历程
- 2020 年 1 月:Microsoft 正式开源 Playwright,首个版本支持 JavaScript/TypeScript
- 2020 年中:陆续发布 Python、Java、.NET 版本
- 2021 年:Playwright Test(Node.js 测试运行器)成熟,社区快速壮大
- 2022-2023 年:在全球开发者调查中,Playwright 使用率持续攀升,逐步成为最受推荐的浏览器自动化工具之一
- 2024-2025 年:持续迭代更新,生态日趋完善,支持组件测试、UI 模式、全局设置等特性
二、Playwright vs Selenium:为什么选择 Playwright
2.1 全方位对比
| 维度 | Selenium | Playwright |
|---|---|---|
| 诞生时间 | 2004 年 | 2020 年 |
| 维护方 | Selenium 社区 | Microsoft |
| 浏览器驱动 | 需要单独安装 WebDriver(如 chromedriver) | 内置浏览器二进制,无需额外驱动 |
| 自动等待 | 需手动编写显式等待(WebDriverWait) | 原生内置,所有操作自动等待 |
| 定位 器 | find_element 系列,基于 WebDriver 协议 | Locator API,支持链式、自动重试 |
| 并行执行 | 需要额外配置(Grid、pytest-xdist 等) | 原生支持并行,浏览器上下文天然隔离 |
| 调试工具 | 依赖第三方工具 | 内置 Trace Viewer、Codegen、UI 模式 |
| 移动模拟 | Appium(独立项目) | 内置设备描述符,可直接模拟 |
| 网络拦截 | 需要额外工具(如 mitmproxy) | 原生支持 route() 拦截请求/响应 |
| 学习曲线 | 资料丰富但 API 略显繁琐 | API 现代简洁,上手更快 |
| 社区生态 | 极为成熟,第三方库丰富 | 快速成长中,官方文档质量高 |
2.2 结论
Selenium:传统经典方案,生态成熟,适合已有项目维护
Playwright:现代首选方案,自动等待强、调试高效,适合新项目
2.3 如何选择
- 如果你是一个 全新项目 且团队没有历史包袱 → 优先选择 Playwright
- 如果你的项目 已经大量使用 Selenium,迁移成本高 → 可以继续使用 Selenium
- 如果你需要 高质量的 E2E 测试 和 CI/CD 集成 → 强烈推荐 Playwright
- 如果你需要 爬虫 / 数据采集 → Playwright 的网络拦截和自动等待更省心
三、支持的语言与浏览器
3.1 支持的编程语言
Playwright
├── Python ← 本文重点
├── JavaScript / TypeScript
├── Java
└── .NET (C#)
四套语言的 API 设计高度一致,核心概念完全通用,掌握一门后迁移到其他语言的成本很低。
3.2 支持的浏览器
Playwright 支持的浏览器引擎
├── Chromium (Chrome、Edge、Opera 等)
├── Firefox
└── WebKit (Safari 的内核)
注意:Playwright 不使用系统已安装的浏览器,而是自带经过适配的浏览器二进制文件,确保了行为的一致性和可靠性。
四、核心架构与执行流程
4.1 整体架构
用户代码(Python / JS / Java / .NET)
│
▼
Playwright API 层
│
▼
Browser Server(浏览器进程管理)
│
▼
┌─────────┬──────────┬──────────┐
▼ ▼ ▼ ▼
Chromium Firefox WebKit Edge(Chromium)
4.2 核心对象关系
Playwright 最顶层入口,负责启动浏览器实例
│
▼
Browser 一个浏览器实例(如一个 Chromium 进程)
│
▼
BrowserContext 浏览器上下文,相当于一个隐身会话,隔离 Cookie、LocalStorage
│ 一个 Browser 可以创建多个 Context,天然实现并行隔离
▼
Page 一个标签页 / 页面,是实际操作的主要对象
│
▼
Locator 元素定位,用于查找页面元素并执行操作
4.3 核心执行流程(五步法)
启动浏览器 ──→ 打开页面 ──→ 定位元素 ──→ 执行动作 ──→ 获取结果
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
launch() goto() locator() click() / text_content()
new_page() wait_for() get_by_*() fill() screenshot()
理解这五步是掌握 Playwright 的关键,所有自动化场景都是这五步的组合与扩展。
五、环境安装与验证
5.1 系统要求
- Python 3.8 及以上版本(推荐 3.10+)
- Windows、macOS、Linux 均支持
- 建议 4GB 以上内存(浏览器较占资源)
5.2 安装步骤
第一步:安装 Playwright Python 包
pip install playwright
第二步:安装浏览器二进制文件
playwright install
此命令会自动下载 Chromium、Firefox、WebKit 三大浏览器的适配版本,存储在本地缓存目录中。
如果只需要 Chromium,可以指定:
playwright install chromium
第三步:(可选)安装系统依赖(Linux 服务器环境)
playwright install-deps
该命令会安装浏览器运行所需的系统级依赖库(如 libglib2.0、libnss3 等),在 Linux 服务器或 Docker 环境中通常需要执行。
5.3 验证安装
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")
print("页面标题:", page.title())
browser.close()
如果输出了页面标题,说明安装成功。
5.4 国内加速建议
如果下载浏览器二进制时速度过慢,可设置镜像源:
# 设置 Playwright 镜像(Linux / macOS) export PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright # Windows PowerShell $env:PLAYWRIGHT_DOWNLOAD_HOST="https://npmmirror.com/mirrors/playwright" # 然后重新安装 playwright install
六、第一个程序:打开网页并截图
6.1 代码示例
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
# 启动 Chromium 浏览器(headless=False 表示有界面,方便观察)
browser = p.chromium.launch(headless=False)
# 创建一个新的页面
page = browser.new_page()
# 导航到目标网址
page.goto("https://playwright.dev/")
# 打印页面标题
print(f"页面标题:{page.title()}")
# 截取整页截图并保存
page.screenshot(path="playwright_homepage.png", full_page=True)
print("截图已保存!")
# 关闭浏览器
browser.close()
6.2 代码解析
sync_playwright() 获取 Playwright 实例(上下文管理器)
└─ p.chromium.launch() 启动 Chromium 浏览器
└─ browser.new_page() 创建空白标签页
└─ page.goto(url) 导航到目标网址
└─ page.screenshot() 截图保存
└─ browser.close() 关闭浏览器释放资源
6.3 headless 模式说明
# 有界面模式(调试时使用,可以看到浏览器操作过程) browser = p.chromium.launch(headless=False) # 无界面模式(正式运行时使用,速度快、资源省) browser = p.chromium.launch(headless=True)
6.4 异步版本
Playwright 同时支持同步和异步两种 API。在高并发场景下推荐使用异步版本:
import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=False)
page = await browser.new_page()
await page.goto("https://playwright.dev/")
print(f"页面标题:{await page.title()}")
await page.screenshot(path="async_screenshot.png")
await browser.close()
asyncio.run(main())
本文后续示例以同步 API 为主,异步版本只需在方法前加 await 即可,逻辑完全一致。
七、常用操作速查
7.1 导航操作
# 打开网页
page.goto("https://www.baidu.com")
# 刷新页面
page.reload()
# 后退 / 前进
page.go_back()
page.go_forward()
# 等待特定 URL 出现(支持通配符)
page.wait_for_url("**/dashboard")
7.2 点击操作
# 普通点击
page.locator("text=登录").click()
# 双击
page.locator("text=标题").dblclick()
# 右键点击
page.locator(".item").click(button="right")
# 按住 Shift 点击
page.locator("a").click(modifiers=["Shift"])
# 强制点击(即使元素被遮挡也尝试点击)
page.locator("#btn").click(force=True)
7.3 输入操作
# 清空并输入文本
page.locator("#username").fill("admin")
# 逐字符输入(模拟真实打字,适合有输入事件监听的场景)
page.locator("#search").press_sequentially("Playwright", delay=100)
# 按键操作
page.locator("input").press("Enter")
page.locator("input").press("Control+a") # 全选
# 输入文件
page.locator('input[type="file"]').set_input_files("test.pdf")
7.4 获取数据
# 获取元素文本内容
text = page.locator("h1").text_content()
# 获取元素内部文本(不含子元素标签)
inner = page.locator(".content").inner_text()
# 获取属性值
href = page.locator("a").get_attribute("href")
# 获取输入框的值
value = page.locator("input").input_value()
# 获取页面标题
title = page.title()
# 获取当前 URL
url = page.url
7.5 截图与录屏
# 元素截图
page.locator("#chart").screenshot(path="chart.png")
# 整页截图(包含滚动区域)
page.screenshot(path="full.png", full_page=True)
# 截取指定区域
page.screenshot(path="clip.png", clip={"x": 0, "y": 0, "width": 800, "height": 600})
录屏功能需在创建 BrowserContext 时启用:
context = browser.new_context(
record_video_dir="videos/",
record_video_size={"width": 1280, "height": 720}
)
page = context.new_page()
page.goto("https://example.com")
context.close() # 关闭上下文时视频自动保存
7.6 等待操作
# 等待元素出现
page.locator("#result").wait_for(timeout=10000) # 超时 10 秒
# 等待元素消失
page.locator(".loading").wait_for(state="hidden")
# 等待特定网络请求完成
with page.expect_response("**/api/data") as response_info:
page.click("#load-data")
response = response_info.value
八、元素定位详解
元素定位是 Playwright 最核心的能力,定位选对了,后续的点击、输入、断言都能水到渠成。
8.1 定位方式全景
元素定位方式
│
├── 推荐方式(语义化,稳定,抗页面变化)
│ ├── get_by_role() 按 ARIA 角色 + 名称定位
│ ├── get_by_text() 按可见文本定位
│ ├── get_by_label() 按表单 label 定位
│ ├── get_by_placeholder() 按 placeholder 定位
│ ├── get_by_alt_text() 按图片 alt 属性定位
│ ├── get_by_test_id() 按 data-testid 定位(推荐用于测试)
│ └── get_by_title() 按 title 属性定位
│
└── 通用方式(灵活,但稳定性略低)
├── CSS 选择器 page.locator("#id") / page.locator(".class")
└── XPath page.locator('//div[@class="xxx"]')
8.2 推荐定位方式详解
(1)get_by_role — 最推荐的方式
# 按按钮角色 + 名称
page.get_by_role("button", name="登录").click()
# 按链接角色
page.get_by_role("link", name="首页").click()
# 按文本框角色
page.get_by_role("textbox", name="用户名").fill("admin")
# 按复选框
page.get_by_role("checkbox", name="记住我").check()
# 按标题(heading)
page.get_by_role("heading", name="欢迎").click()
get_by_role 基于 WAI-ARIA 无障碍标准,定位语义明确、稳定性高,是 Playwright 官方最推荐的方式。
(2)get_by_text — 按文本定位
# 精确匹配
page.get_by_text("登录", exact=True).click()
# 模糊匹配(默认)
page.get_by_text("登录").click()
# 正则匹配
page.get_by_text(re.compile(r"第\d+页")).click()
(3)get_by_label — 按表单标签定位
# 根据 <label for="username">用户名</label> 定位输入框
page.get_by_label("用户名").fill("admin")
page.get_by_label("密码").fill("123456")
(4)get_by_placeholder — 按占位文本定位
page.get_by_placeholder("请输入用户名").fill("admin")
page.get_by_placeholder("请输入密码").fill("secret")
(5)get_by_test_id — 测试专用属性
HTML 中添加 data-testid 属性:
<button data-testid="login-btn">登录</button>
代码中定位:
page.get_by_test_id("login-btn").click()
这种方式完全不受页面样式和文本变化影响,是写自动化测试时最稳定的方式。需确保前端团队配合添加 data-testid 属性。
8.3 CSS 选择器定位
# ID 定位
page.locator("#username").fill("admin")
# 类名定位
page.locator(".btn-primary").click()
# 属性定位
page.locator('[type="submit"]').click()
# 组合定位
page.locator('div.container > button.submit').click()
# 伪类定位
page.locator("button:first-child").click()
page.locator("li:nth-child(3)").click()
8.4 XPath 定位
# 文本匹配
page.locator('//button[text()="提交"]').click()
# 包含某属性
page.locator('//input[@placeholder="用户名"]').fill("admin")
# 模糊匹配文本
page.locator('//div[contains(text(), "加载更多")]').click()
# 轴定位(父节点、兄弟节点等)
page.locator('//label[text()="用户名"]/following-sibling::input').fill("admin")
8.5 链式定位与过滤
# 链式缩小范围
page.locator("div.card").locator("button").click()
# 按文本过滤
page.locator("li").filter(has_text="Python").click()
# 按子元素过滤
page.locator("div.row").filter(has=page.locator("text=详情")).click()
# 按索引选取(第2个匹配元素)
page.locator("div.item").nth(1).click()
# 第一个 / 最后一个
page.locator("li").first.click()
page.locator("li").last.click()
# 获取匹配数量
count = page.locator("div.item").count()
8.6 定位优先级建议
最稳定 最灵活
│ │
▼ ▼
get_by_test_id → get_by_role → get_by_label → get_by_text → CSS → XPath
写自动化测试:优先 get_by_test_id / get_by_role
写采集:常用 CSS / XPath
九、自动等待机制深度解析
9.1 什么是自动等待
Playwright 的每一个操作(click、fill、text_content 等)在执行前,都会自动进行一系列检查,确保元素处于可操作状态后才执行动作。这是 Playwright 相比 Selenium 的核心优势之一。
9.2 自动等待的检查项
当你执行 page.locator("button").click() 时,Playwright 会在后台自动:
1. 等待元素出现在 DOM 中
│
▼
2. 等待元素可见(非 display:none / visibility:hidden)
│
▼
3. 等待元素稳定(不在动画中,位置不持续变化)
│
▼
4. 等待元素可接收事件(不被其他元素遮挡)
│
▼
5. 等待元素可交互(不是 disabled 状态)
│
▼
6. 执行 click()
每一步检查都有默认超时时间(默认 30 秒),超时后抛出 TimeoutError。
9.3 对比传统方式
Selenium 的写法(需要手动等待):
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By # 需要手动编写显式等待 wait = WebDriverWait(driver, 10) element = wait.until(EC.element_to_be_clickable((By.ID, "submit"))) element.click()
Playwright 的写法(自动等待):
# 一行搞定,自动等待元素可点击
page.locator("#submit").click()
9.4 少用 sleep,多用自动等待
# ❌ 不推荐:硬编码等待
import time
time.sleep(5)
page.locator("#result").click()
# ✅ 推荐:利用自动等待
page.locator("#result").click()
# ✅ 确实需要等待特定条件时,使用 Playwright 的等待 API
page.wait_for_selector("#result", state="visible")
page.wait_for_url("**/dashboard")
page.wait_for_load_state("networkidle")
page.wait_for_function("document.querySelector('#result') !== null")
9.5 load_state 说明
# 等待 DOM 加载完成(默认行为,通常够用)
page.goto("https://example.com", wait_until="domcontentloaded")
# 等待所有资源(图片、CSS、JS)加载完成
page.goto("https://example.com", wait_until="load")
# 等待网络空闲(没有超过 0.5 秒的网络请求,适合 SPA 应用)
page.goto("https://example.com", wait_until="networkidle")
十、浏览器上下文与页面管理
10.1 BrowserContext 的作用
BrowserContext 是 Playwright 中一个非常重要的概念,它相当于一个 独立的浏览器会话:
Browser(浏览器实例)
│
├── BrowserContext A ← Cookie、LocalStorage、权限设置完全独立
│ ├── Page 1(标签页)
│ └── Page 2(标签页)
│
└── BrowserContext B ← 与 Context A 完全隔离
└── Page 1(标签页)
10.2 多上下文场景
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
# 创建两个独立的上下文(模拟两个独立用户)
context_a = browser.new_context()
context_b = browser.new_context()
page_a = context_a.new_page()
page_b = context_b.new_page()
# 两个页面的登录状态互不影响
page_a.goto("https://example.com/login")
page_b.goto("https://example.com/login")
# ... 分别操作
context_a.close()
context_b.close()
browser.close()
10.3 设备模拟
# 使用内置的设备描述符模拟 iPhone 13
iphone = p.devices["iPhone 13"]
context = browser.new_context(**iphone)
page = context.new_page()
page.goto("https://example.com")
10.4 设置视窗大小与地理位置
context = browser.new_context(
viewport={"width": 1920, "height": 1080},
locale="zh-CN",
timezone_id="Asia/Shanghai",
geolocation={"longitude": 116.397128, "latitude": 39.916527},
permissions=["geolocation"],
color_scheme="dark"
)
十一、常见场景实战
11.1 登录场景
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://example.com/login")
# 方式一:直接选择器
page.fill('#username', 'admin')
page.fill('#password', '123456')
page.click('button[type="submit"]')
# 方式二:推荐定位方式
# page.get_by_label("用户名").fill("admin")
# page.get_by_label("密码").fill("123456")
# page.get_by_role("button", name="登录").click()
# 等待登录成功跳转
page.wait_for_url("**/dashboard")
print("登录成功,当前页面:", page.url)
browser.close()
11.2 保存与复用登录状态
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
# 第一次:手动登录并保存状态
context = browser.new_context()
page = context.new_page()
page.goto("https://example.com/login")
page.get_by_label("用户名").fill("admin")
page.get_by_label("密码").fill("123456")
page.get_by_role("button", name="登录").click()
page.wait_for_url("**/dashboard")
# 保存登录状态到文件
context.storage_state(path="auth_state.json")
print("登录状态已保存!")
context.close()
# 后续:直接加载状态,无需重复登录
context = browser.new_context(storage_state="auth_state.json")
page = context.new_page()
page.goto("https://example.com/dashboard")
print("已使用保存的状态直接进入后台:", page.title())
context.close()
browser.close()
11.3 文件上传
# 单文件上传
page.locator('input[type="file"]').set_input_files("test.pdf")
# 多文件上传
page.locator('input[type="file"]').set_input_files(["a.png", "b.png", "c.png"])
# 取消已选文件
page.locator('input[type="file"]').set_input_files([])
11.4 弹窗(Dialog)处理
# 方式一:自动接受所有弹窗
page.on("dialog", lambda dialog: dialog.accept())
# 方式二:根据弹窗类型分别处理
def handle_dialog(dialog):
if dialog.type == "confirm":
print(f"确认弹窗:{dialog.message}")
dialog.accept() # 点击确认
# dialog.dismiss() # 点击取消
elif dialog.type == "alert":
print(f"提示弹窗:{dialog.message}")
dialog.accept()
page.on("dialog", handle_dialog)
page.click("#delete-btn") # 触发弹窗
11.5 多标签页处理
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto("https://example.com")
# 点击会打开新标签页的链接时,捕获新页面
with page.expect_popup() as popup_info:
page.get_by_text("详情").click()
new_page = popup_info.value
print("新标签页标题:", new_page.title())
# 在新标签页上操作
new_page.locator("#content").click()
new_page.close()
browser.close()
11.6 网络请求拦截
def handle_route(route):
"""拦截 API 请求,可以修改请求或返回模拟数据"""
if "/api/user" in route.request.url:
# 返回模拟数据
route.fulfill(
status=200,
content_type="application/json",
body='{"name": "测试用户", "role": "admin"}'
)
else:
# 放行其他请求
route.continue_()
page.route("**/api/**", handle_route)
page.goto("https://example.com")
11.7 执行 JavaScript
# 在页面中执行 JavaScript
title = page.evaluate("document.title")
print(title)
# 传递参数给 JS
result = page.evaluate("([a, b]) => a + b", [3, 5])
print(result) # 8
# 获取复杂的页面数据
data = page.evaluate("""
() => {
const items = document.querySelectorAll('.product');
return Array.from(items).map(item => ({
name: item.querySelector('.title').textContent,
price: item.querySelector('.price').textContent
}));
}
""")
11.8 滚动操作
# 滚动到元素位置
page.locator("#footer").scroll_into_view_if_needed()
# 使用鼠标滚轮
page.mouse.wheel(0, 500) # 向下滚动 500 像素
# 通过 JS 滚动到页面底部
page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
11.9 iframe 操作
# 通过选择器定位 iframe
frame = page.frame_locator("iframe#editor")
# 在 iframe 中操作元素
frame.locator("button.save").click()
frame.locator("input.title").fill("文档标题")
十二、Codegen:代码录制神器
12.1 什么是 Codegen
Codegen 是 Playwright 内置的代码录制工具。它会打开一个浏览器窗口并录制你的所有操作(点击、输入、导航等),实时生成对应的 Playwright 代码。
12.2 启动方式
# 基本启动 playwright codegen https://example.com # 指定输出语言(Python) playwright codegen --target python https://example.com # 指定输出文件 playwright codegen --output my_script.py https://example.com # 模拟移动设备 playwright codegen --device "iPhone 13" https://example.com # 保存登录状态 playwright codegen --save-storage=auth.json https://example.com
12.3 Codegen 的价值
快速上手复杂页面 ──→ 不用手动研究 DOM 结构
自动生成定位代码 ──→ 自动推荐最优定位方式
实时预览操作效果 ──→ 边操作边看代码生成
降低学习门槛 ──→ 新手也能快速写出自动化脚本
12.4 使用建议
- 对于 不熟悉的页面,先用 Codegen 录制一遍,了解操作流程和元素结构
- 录制的代码通常需要 手动优化(去除冗余步骤、改用更稳定的定位)
- Codegen 是 学习工具,不建议直接用于生产环境
十三、调试与排查技巧
13.1 Trace Viewer
Trace Viewer 是 Playwright 内置的可视化调试工具,可以回放测试执行的完整过程。
context = browser.new_context()
context.tracing.start(screenshots=True, snapshots=True, sources=True)
page = context.new_page()
page.goto("https://example.com")
# ... 操作代码 ...
context.tracing.stop(path="trace.zip")
查看 trace:
playwright show-trace trace.zip
Trace Viewer 会展示每一步操作的截图、DOM 快照、网络请求和控制台日志,是排查问题的利器。
13.2 headed 模式 + slow_mo
# slow_mo 会降低每个操作的速度(单位:毫秒),方便观察执行过程
browser = p.chromium.launch(
headless=False,
slow_mo=500 # 每步间隔 500 毫秒
)
13.3 截图排查
# 在关键步骤后截图,帮助定位问题
page.goto("https://example.com/login")
page.screenshot(path="step1_login_page.png")
page.get_by_label("用户名").fill("admin")
page.screenshot(path="step2_filled.png")
page.get_by_role("button", name="登录").click()
page.screenshot(path="step3_after_login.png")
13.4 控制台日志
# 监听控制台输出
page.on("console", lambda msg: print(f"Console [{msg.type}]: {msg.text}"))
# 监听页面错误
page.on("pageerror", lambda err: print(f"Page Error: {err}"))
# 监听请求失败
page.on("requestfailed", lambda req: print(f"Request Failed: {req.url} - {req.failure}"))
十四、Playwright 能做什么
14.1 应用场景全景
Playwright 应用场景
│
├── 自动化测试
│ ├── E2E(端到端)测试
│ ├── 回归测试
│ ├── 跨浏览器兼容性测试
│ └── 视觉回归测试(配合截图对比)
│
├── 网页自动化
│ ├── 表单自动填写
│ ├── 内容自动发布
│ ├── 定时任务执行
│ └── 流程审批自动化
│
├── 数据采集
│ ├── 商品信息采集
│ ├── 社交媒体内容采集
│ ├── 公开数据集构建
│ └── 价格监控
│
├── 办公自动化
│ ├── 报表自动生成
│ ├── 后台系统批量操作
│ └── 数据导出与归档
│
└── AI + 浏览器
├── AI Agent 的浏览器交互层
├── 智能表单填写
└── 自动化信息检索
14.2 与 pytest 集成(测试场景)
Playwright 官方提供了 pytest 插件,可直接使用 pytest 框架组织测试:
pip install pytest-playwright
# test_login.py
import pytest
from playwright.sync_api import Page, expect
def test_login_success(page: Page):
page.goto("https://example.com/login")
page.get_by_label("用户名").fill("admin")
page.get_by_label("密码").fill("123456")
page.get_by_role("button", name="登录").click()
# 使用 Playwright 的断言(自带等待和重试)
expect(page).to_have_url("**/dashboard")
expect(page.get_by_text("欢迎回来")).to_be_visible()
def test_search(page: Page):
page.goto("https://example.com")
page.get_by_placeholder("搜索").fill("Playwright")
page.get_by_placeholder("搜索").press("Enter")
expect(page.locator(".search-result")).to_have_count(greater_than=0)
运行:
pytest test_login.py --browser chromium
14.3 连接远程浏览器
# 连接到已运行的浏览器实例(通过 CDP)
browser = p.chromium.connect_over_cdp("http://localhost:9222")
# 连接到 Playwright Server
browser = p.chromium.connect("ws://remote-server:3000/")
十五、最佳实践与避坑指南
15.1 定位策略
✅ 推荐
└─ 优先使用 get_by_role / get_by_test_id / get_by_label
这些方式语义清晰、抗页面变化能力强
❌ 避免
└─ 避免使用脆弱的定位方式(如绝对 XPath、索引定位)
页面结构一旦变化就会导致脚本失败
15.2 等待策略
✅ 推荐
└─ 依赖 Playwright 的自动等待机制
只在必要时使用 wait_for_selector / wait_for_url / wait_for_load_state
❌ 避免
└─ 避免使用 time.sleep() 硬编码等待
既不稳定又浪费时间
15.3 资源管理
✅ 推荐
└─ 使用 with 语句(上下文管理器)管理 Playwright 和 Browser 的生命周期
操作完成后及时 close()
❌ 避免
└─ 避免忘记关闭浏览器实例
长时间运行可能导致内存泄漏
15.4 测试组织
✅ 推荐
└─ 每个测试用例使用独立的 BrowserContext
确保测试之间互不影响
利用 pytest fixtures 管理 setup / teardown
❌ 避免
└─ 避免测试用例之间共享登录状态或页面数据
15.5 常见错误排查
| 错误 | 原因 | 解决方案 |
|---|---|---|
TimeoutError: locator.click |
元素未找到或不可交互 | 检查定 位器是否正确,元素是否在 iframe 中 |
Target page, context or browser has been closed |
操作的对象已被关闭 | 检查代码逻辑,避免提前关闭 |
net::ERR_CONNECTION_REFUSED |
网络不通或地址错误 | 确认目标 URL 是否可达 |
Element is not visible |
元素存在但不可见 | 可能需要先滚动到元素位置或展开折叠区域 |
十六、总结与学习路线
16.1 核心要点回顾
Playwright 的本质
└─ 用代码控制浏览器完成一切网页操作
核心优势
├── 自动等待 ──→ 稳定可靠,告别 sleep
├── 智能定位 ──→ get_by_role 等语义化 API,好用且抗变化
├── 跨浏览器 ──→ Chromium / Firefox / WebKit 一套代码全覆盖
├── Codegen ──→ 录制生成代码,高效上手复杂页面
├── Trace ──→ 可视化调试,问题排查效率极高
└── Context ──→ 浏览器上下文隔离,天然支持并行与多用户场景
16.2 学习路线建议
第一阶段:入门
│ 安装环境 → 第一个程序 → 理解核心五步流程
│
第二阶段:基础
│ 掌握常用操作 → 熟练元素定位 → 理解自动等待
│
第三阶段:进阶
│ 网络拦截 → 多上下文管理 → iframe / 多标签页 → 保存状态
│
第四阶段:实战
│ 独立完成一个自动化项目(如数据采集 / 自动化测试)
│
第五阶段:高阶
pytest 集成 → CI/CD 集成 → 性能优化 → AI Agent 集成
Playwright 是现代浏览器自动化的首选工具。 掌握它,你就拥有了解决一切网页操作问题的能力。从今天开始,用代码驱动浏览器,让重复劳动交给自动化。













