Skip to content

PlayWright

CDP 是( Chrome DevTools Protocol) Google 开发的协议,允许外部工具(如 Playwright、Chrome DevTools)直接控制 Chrome 或 Chromium 浏览器。

常见并发量

场景 并发量 举例
小型网站 100~1000 普通企业官网、博客
中型网站 1000~1 万 中小型电商、论坛
大型网站 1 万~10 万 大型电商(非高峰期)、新闻网站
超大型网站 10 万以上 淘宝双 11、12306 抢票

浏览器实例只创建一次,而不是每次循环都创建新的浏览器。具体结构是:

  1. 浏览器实例 (browser):整个程序运行期间只创建一次
  2. 浏览器上下文 (context):在浏览器实例内部创建,整个循环过程中重用同一个上下文
  3. 页面 (page):每次循环都会创建一个新页面,执行完后关闭

这种结构类似于:

  • 浏览器实例 = 浏览器程序(如 Chrome.exe)
  • 浏览器上下文 = 浏览器中的一个用户配置文件(如 Chrome 的不同用户)
  • 页面 = 浏览器中的标签页

重用浏览器上下文的优势

  1. 共享状态:同一个上下文中的所有页面共享 Cookie、本地存储等状态
  2. 资源效率:创建新的浏览器实例非常消耗资源,而创建新页面几乎不消耗额外资源
  3. 速度更快:重用浏览器上下文可以避免浏览器启动的开销

与方案二的对比

特征 方案一 方案二
浏览器实例 1 个(整个程序运行期间) 1 个(整个程序运行期间)
浏览器上下文 1 个(循环中重用) 每个循环创建新的上下文
页面 每个循环创建新页面 每个循环创建新页面
Cookie 状态 共享(同一个上下文中) 每次重新加载存储状态
资源消耗 低(重用上下文) 中(创建新上下文)
性能 高(上下文只需初始化一次) 中(每次循环初始化上下文)

总结

方案一中的浏览器实例不是全新的,而是在整个程序运行期间只创建一次。每次循环只会创建新的页面,而浏览器上下文是重用的,这样可以保持 Cookie 状态并提高性能。

如果您需要完全隔离的环境(例如测试不同用户的登录状态),可以选择方案二。但如果您只是想定期检查 Cookie 是否有效,方案一更高效。

在 Playwright 中,同步模式下使用 with 语句可以简化资源管理,无需手动关闭浏览器和页面。以下是同步模式的示例代码:

1. 基本用法:启动浏览器并创建页面

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    # 启动浏览器(支持 chromium、firefox、webkit)
    browser = p.chromium.launch(headless=False)  # 非无头模式,可看到浏览器窗口

    # 创建页面
    page = browser.new_page()
    page.goto("https://example.com")
    print(page.title())  # 输出页面标题

    # 无需手动关闭,with 块结束后自动释放资源

2. 自定义浏览器启动参数

with sync_playwright() as p:
    browser = p.chromium.launch(
        headless=False,
        slow_mo=500,  # 操作延迟(毫秒)
        args=["--window-size=1920,1080"]  # 浏览器窗口大小
    )
    page = browser.new_page()
    page.goto("https://example.com")
    # 执行其他操作...

3. 使用浏览器上下文(Context)

浏览器上下文用于隔离 Cookie、本地存储等数据:

with sync_playwright() as p:
    browser = p.chromium.launch()

    # 创建独立上下文
    with browser.new_context() as context:
        page = context.new_page()
        page.goto("https://example.com")
        page.fill("input[name='username']", "test_user")
        page.screenshot(path="screenshot.png")

核心区别对比

场景 不创建独立上下文 创建独立上下文
Cookie / 会话共享 所有页面共享同一套 Cookie 和会话 每个上下文独立管理 Cookie 和会话
内存占用 所有页面共用一个上下文,占用少 每个上下文独立,内存占用更高
多用户测试 难:需手动清理 Cookie 切换用户 易:创建多个上下文并行测试不同用户
无痕模式 不支持(除非手动清除数据) 支持(上下文默认类似无痕模式)
资源释放 关闭浏览器时统一释放 上下文结束时自动释放,更灵活

4. 创建多个页面

with sync_playwright() as p:
    browser = p.chromium.launch()

    # 创建多个页面
    page1 = browser.new_page()
    page2 = browser.new_page()

    page1.goto("https://example.com")
    page2.goto("https://playwright.dev")

    print(page1.title())  # 输出第一个页面标题
    print(page2.title())  # 输出第二个页面标题

关键说明

  • 同步 vs 异步:同步模式(无 async/await)适合简单脚本,异步模式适合高并发场景。
  • 资源管理with 语句会自动关闭浏览器、上下文和页面,无需手动调用 browser.close()
  • 执行顺序:代码按顺序执行,适合线性自动化流程。

在 Playwright 中,上传文件主要通过 set_input_files 方法实现,以下分**普通场景**(页面有 <input type="file"> 元素 )和**特殊场景**(文件选择框由动态事件触发 )详细说明写法,结合 Python 示例:

一、普通场景:页面存在 <input type="file"> 元素

如果页面里有明确的文件上传输入框(即 <input type="file"> 标签 ),直接定位该元素,调用 set_input_files 传入文件路径即可,支持单文件、多文件上传。

1. 单文件上传示例

from playwright.sync_api import sync_playwright

def upload_single_file():
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)  # 非无头模式,方便查看过程
        page = browser.new_page()
        page.goto("https://example.com/upload")  # 替换为实际上传页面地址

        # 定位文件上传 input 元素,可通过 CSS 选择器、Label 等方式,这里以 CSS 为例
        file_input = page.locator('input[type="file"]')  
        # 上传单个文件,传入文件的绝对路径
        file_input.set_input_files("D:\\test\\single_file.pdf")  

        # 若有提交按钮,可模拟点击提交(根据实际页面调整)
        page.locator('button[type="submit"]').click()  

        browser.close()

upload_single_file()

2. 多文件上传示例

from playwright.sync_api import sync_playwright

def upload_multiple_files():
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)
        page = browser.new_page()
        page.goto("https://example.com/upload")

        file_input = page.locator('input[type="file"]')
        # 上传多个文件,传入文件路径列表
        file_input.set_input_files([
            "D:\\test\\file1.pdf", 
            "D:\\test\\file2.png"
        ])  

        # 模拟提交(按需调整)
        page.locator('button[type="submit"]').click()

        browser.close()

upload_multiple_files()

二、特殊场景:文件选择框由点击事件动态触发

有些页面的文件上传,不是直接显示 <input type="file"> 元素,而是点击某个按钮后,才弹出系统文件选择框。这时需要用 page.expect_file_chooser() 监听文件选择框事件,再操作上传。

示例代码:

from playwright.sync_api import sync_playwright

def upload_with_file_chooser():
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)
        page = browser.new_page()
        page.goto("https://example.com/upload")

        # 先监听文件选择框事件,再点击触发上传的按钮
        with page.expect_file_chooser() as fc_info:
            # 点击触发文件选择框的按钮(比如“选择文件”按钮,根据实际页面定位)
            page.locator('text=选择文件').click()  
        file_chooser = fc_info.value  # 获取文件选择框对象

        # 上传文件,支持单文件或多文件列表
        file_chooser.set_files("D:\\test\\target_file.pdf")  
        # 若多文件:file_chooser.set_files(["file1.pdf", "file2.pdf"])

        # 若有后续提交等操作,继续执行
        page.locator('button[type="submit"]').click()

        browser.close()

upload_with_file_chooser()

关键要点说明

  • 路径格式:Windows 系统里文件路径用 \\ 或原始字符串 r"D:\test\file.pdf" ;Linux、macOS 用 / 分隔路径,如 /Users/user/test/file.pdf
  • 元素定位:除了示例里的 CSS 选择器、文本定位,还能用 page.get_by_label("上传文件") (根据标签关联文本定位 )等 Playwright 丰富的定位策略,精准找到文件上传控件。
  • 异步处理:如果用 Playwright 异步 API(async_playwright ),需配合 async/await ,逻辑和同步类似,只是语法调整为异步风格,比如 await page.locator(...).click()

按照页面实际的上传控件形式,选对应方式就能实现文件上传自动化,解决手动操作繁琐问题,尤其适合批量上传、自动化测试等场景 。

无头浏览器**主要区别在于运行方式**

特性 无头浏览器(默认) 有头浏览器(显式启用)
界面显示 无浏览器窗口,后台运行 显示真实浏览器窗口,可观察操作过程
调试便利性 需依赖日志、截图或视频 可直接查看页面状态,便于调试
性能 通常更快(无需渲染界面) 略慢(需处理界面渲染)
默认启动参数 部分浏览器可能使用不同的默认参数(如 Chromium 的沙盒模式) 与真实用户使用的浏览器更接近

在 Playwright 中,判断元素是否存在于页面上有多种方式,具体取决于你的场景需求。以下是几种常见方法及其适用场景:

1. 使用 query_selector()(同步检查,不等待)

直接查询元素,立即返回结果(无论元素是否可见)。

element = page.query_selector("selector")
if element:
    print("元素存在(无论是否可见)")
else:
    print("元素不存在")

2. 使用 is_visible()is_hidden()(判断可见性)

element = page.locator("selector")
if element.is_visible():
    print("元素可见")
else:
    print("元素隐藏或不存在")

核心区别对比

条件 is_visible() is_hidden()
元素存在且可见 True False
元素存在但被隐藏(如 display: none False True
元素不存在 False True

3. 使用 wait_for_selector()(带超时的等待)

  • 等待元素出现
try:
    page.wait_for_selector("selector", timeout=3000)  # 超时时间3秒
    print("元素存在或已出现")
except TimeoutError:
    print("元素不存在或超时未出现")
  • 等待元素消失
try:
    page.wait_for_selector("selector", timeout=3000, state="detached")
    print("元素不存在或已消失")
except TimeoutError:
    print("元素仍然存在")

4. 使用 count() 方法(批量判断)

elements = page.locator("selector")
if elements.count() > 0:
    print("存在至少一个匹配的元素")
else:
    print("没有匹配的元素")

选择建议

  • 立即判断(不等待):用 query_selector()count()
  • 判断可见性:用 is_visible()is_hidden()
  • 等待元素状态变化:用 wait_for_selector() 并指定 state 参数("attached""detached""visible""hidden")。

在 Playwright 中,"录制" 通常指两种功能:操作视频录制**和**代码生成录制器。下面分别介绍它们的用法:

一、录制操作视频(Video Recording)

通过 recordVideo 选项可录制浏览器操作的视频,适用于测试回放和问题定位。

基础用法

在创建浏览器上下文时启用视频录制,并指定保存目录:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    context = browser.new_context(
        record_video_dir="videos/"  # 视频保存目录
    )
    page = context.new_page()
    page.goto("https://example.com")
    page.click("text=Sign Up")

    # 获取视频路径(需在关闭上下文后)
    video_path = page.video.path()
    print(f"视频已保存至: {video_path}")

    context.close()
    browser.close()

关键参数

  • record_video_dir(必需):视频保存目录。
  • record_video_size(可选):视频分辨率,如 {"width": 800, "height": 600}

异步模式用法

import asyncio
from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch()
        context = await browser.new_context(record_video_dir="videos/")
        page = await context.new_page()
        await page.goto("https://example.com")

        # 关闭上下文后获取视频路径
        await context.close()
        video_path = await page.video.path()
        print(f"视频保存路径: {video_path}")

asyncio.run(main())

二、代码生成录制器(Codegen)

Playwright 提供的 codegen 工具可录制你的浏览器操作并自动生成对应的代码,适合快速创建测试脚本。

启动录制器

在终端中运行以下命令,然后手动操作浏览器:

playwright codegen https://example.com
  • 操作会实时转换为代码并显示在终端。
  • 支持多种语言(Python、JavaScript 等),使用 --lang python 指定语言。

常用选项

选项 作用
--target python 生成 Python 代码
--output script.py 将代码保存到文件
--viewport-size 1920,1080 设置视口尺寸
--save-storage auth.json 保存认证状态(如 Cookie)

示例:生成登录测试代码

playwright codegen --target python --output test_login.py --viewport-size 1280,720 http://localhost:8080/

然后在浏览器中完成登录流程,工具会自动生成对应的 Python 代码。

Playwright 的 codegen 工具在录制过程中提供了一系列按钮,用于控制录制流程、调整设置和生成代码。以下是这些按钮的功能解释和使用场景:

一、主界面按钮

当你运行 playwright codegen https://example.com 后,会看到以下核心按钮:

1. 录制控制按钮

按钮图标 / 名称 功能描述
▶️ Start Recording 开始录制浏览器操作(默认自动开始)。
⏸️ Pause 暂停录制,暂停后操作不会生成代码。
⏹️ Stop 停止录制并退出 codegen。
🔄 Restart 重置录制,清空已生成的代码并重新开始。

2. 代码控制按钮

按钮图标 / 名称 功能描述
💾 Save 将生成的代码保存到文件(如 script.py)。
📋 Copy 复制生成的代码到剪贴板。
🌐 Change Language 切换生成代码的语言(Python、JavaScript 等)。
🎯 Selector Highlight 启用 / 禁用元素选择器高亮显示(鼠标悬停时显示匹配的元素)。

二、高级设置按钮

点击界面右上角的 ⚙️ Settings 按钮,可打开以下选项:

1. 录制选项

设置项 功能描述
Slow Motion 减慢操作速度,便于调试复杂交互。
Skip Navigation 跳过页面导航操作(如 goto()),只记录页面内交互。
Screenshots 在操作步骤中插入截图代码(使用 page.screenshot())。
Mask Text 对敏感文本(如密码)进行掩码处理(生成 page.fill("input", "*****"))。

2. 选择器选项

设置项 功能描述
Prefer CSS 优先使用 CSS 选择器而非 XPath。
Use aria-* Attributes 在选择器中包含 ARIA 属性(如 rolearia-label)。
Use Data Attributes 在选择器中包含自定义数据属性(如 data-testid)。

三、元素交互辅助按钮

在浏览器中操作时,鼠标悬停在元素上会显示辅助按钮:

1. 元素操作按钮

按钮图标 / 名称 功能描述
🖱️ Click 生成点击该元素的代码(等价于 page.click(selector))。
📝 Fill 生成填充文本到输入框的代码(等价于 page.fill(selector, "text"))。
🔍 Hover 生成鼠标悬停的代码(等价于 page.hover(selector))。
⏳ Wait for Element 生成等待元素出现的代码(等价于 page.wait_for_selector(selector))。

2. 选择器信息按钮

按钮图标 / 名称 功能描述
📋 Copy Selector 复制当前元素的选择器(如 button:has-text("Sign Up"))。
🔍 Inspect 在浏览器开发者工具中检查该元素。

四、使用技巧

  1. 暂停录制复杂操作 遇到不需要录制的操作(如登录)时,点击 ⏸️ 暂停,操作完成后再继续录制。

  2. 自定义选择器 若自动生成的选择器不稳定,可手动编辑代码,使用更具描述性的选择器(如 get_by_roleget_by_text)。

  3. 结合调试选项

  4. 使用 --debug 选项启动 codegen,查看详细的录制日志。

  5. 使用 --target python-async 生成异步代码。

  6. 保存认证状态 使用 --save-storage auth.json 保存登录状态,避免重复录制登录流程:

playwright codegen --save-storage auth.json https://example.com/login

五、常见问题

  1. 按钮未生成预期代码
  2. 确保元素可交互(如按钮未被禁用)。
  3. 尝试使用 --wait-for-navigation 选项等待页面加载完成。
  4. 选择器过于复杂
  5. 在元素上添加 data-testid 属性,使选择器更简洁稳定。
  6. 手动编辑生成的代码,替换为 get_by_roleget_by_text
  7. 无法录制某些操作
  8. 文件上传、弹窗等特殊操作可能需要手动补充代码。
  9. 使用 page.wait_for_event() 处理动态内容。

总结

Playwright Codegen 的录制按钮提供了直观的方式来生成自动化脚本,通过合理使用这些工具,你可以:

  • 快速创建测试用例的基础框架。
  • 学习 Playwright API 的使用方法。
  • 定位复杂交互的选择器。

对于高级场景,建议手动优化生成的代码,结合语义化选择器(如 get_by_role)和断言,使测试更健壮。

pw手册

img

在 Playwright 中,page.get_by_role()page.get_by_text() 是两种强大的元素定位方法,它们基于语义和文本内容定位元素,比传统的 CSS/XPath 选择器更直观、更具可读性。下面详细介绍它们的用法和区别:

一、page.get_by_role():基于语义角色定位

核心概念

  • 角色(Role):指元素在页面中的语义功能,如 buttonlinkheadingcheckbox 等。
  • 可访问性(Accessibility):该方法基于浏览器的无障碍树(Accessibility Tree)定位元素,更符合用户实际交互方式。

基本用法

# 定位"Sign Up"按钮
page.get_by_role("button", name="Sign Up").click()

# 定位一级标题
page.get_by_role("heading", level=1).text_content()

# 定位带有特定标签的链接
page.get_by_role("link", name="Learn more", exact=True).hover()

常用角色参数

角色类型 对应元素示例
button <button>, <input type="button">
link <a href>
heading <h1><h6>
checkbox <input type="checkbox">
radio <input type="radio">
textbox <input type="text">, <textarea>
listitem <li>
option <option> in <select>

高级过滤

# 定位禁用的按钮
page.get_by_role("button", disabled=True)

# 定位包含特定文本的卡片
page.get_by_role("card").get_by_text("Featured Product")

# 定位第2个菜单项
page.get_by_role("menuitem").nth(1)

二、page.get_by_text():基于文本内容定位

核心概念

  • 直接通过元素的可见文本内容定位,无需关心元素类型或结构。
  • 支持模糊匹配(默认)和精确匹配(exact=True)。

基本用法

# 点击包含"Submit"的按钮(模糊匹配)
page.get_by_text("Submit").click()

# 精确匹配文本(必须完全一致)
page.get_by_text("Hello, world!", exact=True)

# 定位嵌套在特定元素中的文本
page.get_by_role("article").get_by_text("Read more")

文本匹配规则

  • 模糊匹配:文本包含指定内容即可(如 "Sign" 匹配 "Sign Up")。
  • 精确匹配:文本必须与指定内容完全一致(大小写敏感)。

三、对比与选择建议

场景 get_by_role() get_by_text()
已知元素语义角色 ✅ 推荐(如按钮、链接) ❌ 需先定位父元素
仅知文本内容 ❌ 需猜测角色类型 ✅ 直接定位
文本在特定元素内 ❌ 需嵌套选择 ✅ 可结合父元素定位
无障碍测试 ✅ 基于无障碍树 ❌ 不保证语义正确性
动态文本(如时间戳) ✅ 可结合其他属性过滤 ❌ 文本可能变化

四、组合使用示例

# 定位包含"Product"文本的卡片中的"Add to Cart"按钮
product_card = page.get_by_role("card").get_by_text("Product")
product_card.get_by_role("button", name="Add to Cart").click()

# 在导航栏中定位"About"链接
nav = page.get_by_role("navigation")
nav.get_by_role("link", name="About").hover()

五、最佳实践

  1. 优先使用 get_by_role():语义更明确,更贴近用户行为,代码更稳定。
  2. 结合使用:用 get_by_role() 定位容器,用 get_by_text() 筛选内容。
  3. 避免过度依赖文本:如果文本可能变化(如翻译或动态内容),考虑使用 get_by_role() 结合其他属性。
  4. 精确匹配:使用 exact=True 避免意外匹配相似文本。

总结

  • get_by_role():基于元素语义角色定位,推荐用于交互元素(按钮、链接等)。
  • get_by_text():基于文本内容定位,适合快速筛选包含特定文本的元素。

两者结合可使定位逻辑更清晰、更具可读性,同时提高测试的稳定性和可维护性。

在 Playwright 中,语义化选择器**是指基于元素的**无障碍角色(Role)、**文本内容(Text)**和**标签语义**进行定位的方法。这些方法比传统的 CSS/XPath 更贴近用户视角,能提高测试的稳定性和可读性。以下是 Playwright 提供的主要语义化方法:

一、核心语义化定位方法

1. page.get_by_role()

基于元素的无障碍角色和名称定位,适用于各种交互元素。

常用角色参数

# 按钮
page.get_by_role("button", name="Submit")

# 链接
page.get_by_role("link", name="Learn more")

# 标题(h1~h6)
page.get_by_role("heading", name="Welcome", level=1)

# 复选框
page.get_by_role("checkbox", name="Remember me").check()

# 单选按钮
page.get_by_role("radio", name="Option 1").click()

# 输入框
page.get_by_role("textbox", name="Username").fill("test")

# 下拉框选项
page.get_by_role("option", name="English").select_option()

2. page.get_by_text()

基于元素的可见文本内容定位,支持模糊匹配和精确匹配。

示例

# 模糊匹配(包含"Sign"即可)
page.get_by_text("Sign").click()

# 精确匹配
page.get_by_text("Hello, world!", exact=True)

# 在特定区域内查找文本
page.get_by_role("article").get_by_text("Read more")

3. page.get_by_label()

基于关联的标签文本定位表单元素,适用于输入框、复选框等。

示例

<label for="email">Email:</label>
<input type="email" id="email">
page.get_by_label("Email:").fill("test@example.com")

4. page.get_by_placeholder()

基于输入框的占位文本定位。

示例

<input type="text" placeholder="Enter your name">
page.get_by_placeholder("Enter your name").fill("John")

5. page.get_by_alt_text()

基于图像的 alt 属性定位图片元素。

示例

<img src="logo.png" alt="Company logo">
page.get_by_alt_text("Company logo").screenshot()

6. page.get_by_title()

基于元素的 title 属性定位。

示例

<button title="Close this dialog">X</button>
page.get_by_title("Close this dialog").click()

二、组合语义化选择器

可以链式调用多个语义化方法,缩小定位范围:

# 在导航栏中查找"Products"链接
nav = page.get_by_role("navigation")
nav.get_by_role("link", name="Products").click()

# 在卡片中查找带有"Add to Cart"按钮的商品
product_card = page.get_by_role("card").get_by_text("Featured Product")
product_card.get_by_role("button", name="Add to Cart").click()

三、与非语义化方法的结合

语义化方法可与 CSS/XPath 混合使用,处理复杂场景:

# 使用 CSS 选择容器,再用语义化方法定位内部元素
container = page.locator(".product-list")
container.get_by_role("button", name="View Details").nth(0).click()

# 使用 XPath 定位复杂结构,再用文本筛选
parent_element = page.locator('//div[contains(@class, "custom-component")]')
parent_element.get_by_text("Expand").click()

四、优势总结

优势 说明
稳定性高 不依赖具体的 CSS 类名或 ID,DOM 结构变化时不易失效。
可读性强 代码直接表达业务含义(如 "点击登录按钮"),无需理解底层选择器。
符合无障碍标准 基于浏览器的无障碍树定位,确保测试覆盖真实用户体验。
维护成本低 页面样式调整时,语义化选择器通常无需修改。

五、使用建议

  1. 优先使用 get_by_role():对于交互元素(按钮、链接、表单控件),这是最稳定的定位方式。
  2. 结合 get_by_text():当角色不明确时,通过文本内容定位是第二选择。
  3. 添加测试标识:对于自定义组件,使用 data-testid 属性并结合 CSS 选择器。
  4. 避免过度依赖 XPath:仅在语义化方法无法实现时使用,因其可读性和性能较差。

通过合理使用语义化选择器,你的 Playwright 测试将更加健壮、易读,且更贴近用户实际操作。