Playwright 浏览器自动化完全指南:从入门到实战
目录
- 一、什么是 Playwright
- 二、Playwright vs Selenium:为什么选择 Playwright
- 三、支持的语言与浏览器
- 四、核心架构与执行流程
- 五、环境安装与验证
- 六、第一个程序:打开网页并截图
- 七、常用操作速查
- 八、元素定位详解
- 九、自动等待机制深度解析
- 十、浏览器上下文与页面管理
- 十一、常见场景实战
- 十二、Codegen:代码录制神器
- 十三、调试与排查技巧
- 十四、Playwright 能做什么
- 十五、最佳实践与避坑指南
- 十六、总结与学习路线
一、什么是 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.url7.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() 按># 按按钮角色 + 名称 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># 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.com12.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.zipTrace 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 chromium14.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 集成16.3 官方资源
- 官方文档:https://playwright.dev/python/
- GitHub 仓库:https://github.com/microsoft/playwright
- API 参考:https://playwright.dev/python/docs/api/class-page
Playwright 是现代浏览器自动化的首选工具。掌握它,你就拥有了解决一切网页操作问题的能力。从今天开始,用代码驱动浏览器,让重复劳动交给自动化。
