当前位置: 首页 > web >正文

Playwright Python 教程:中级篇

文章目录

      • **Playwright Python 教程:中级篇**
        • **目标**
        • **一、高级等待策略:告别 `time.sleep`**
        • **二、处理复杂的 UI 组件**
        • **三、Frame 和 Iframe 处理**
        • **四、管理多个 Page 和 Tab**
        • **五、实战演练:一个综合案例**
        • **中级篇总结**

Playwright Python 教程:中级篇

目标

本篇将帮助你从"能用"到"用好",重点讲解如何处理真实测试场景中的复杂情况,让你的脚本更加稳定和高效。


一、高级等待策略:告别 time.sleep

在基础篇中,我们提到了要避免使用 page.wait_for_timeout()。这是因为固定等待时间非常不可靠(网络或机器速度可能导致元素加载或快或慢)。Playwright 提供了内置的自动等待和更智能的显式等待机制。

1. 内置的自动等待(Auto-waiting)
Playwright 的核心优势。在执行操作(如 click, fill)之前,它会自动执行一系列可操作性检查:

  • 元素是否存在(Attached)
  • 元素是否可见(Visible)
  • 元素是否稳定(Not animated, Stable)
  • 元素是否可交互(Enabled, Editable)
  • 元素是否接收到事件(Not obscured)

这意味着你通常不需要手动等待元素出现,直接操作即可。page.click(selector) 会一直等待该元素可点击(最多 30s,超时则报错)。

2. 显式等待(Explicit Waits)
有些情况下,你需要等待一个特定的条件发生,而不是某个元素可操作。这时就需要显式等待。

  • page.wait_for_selector(selector, state="visible", timeout=30000): 等待匹配选择器的元素达到特定状态(如 "visible", "hidden", "attached", "detached")。

    # 等待一个加载 spinner 消失
    page.wait_for_selector(".spinner", state="hidden")
    # 等待成功消息出现
    page.wait_for_selector("text=Operation successful", state="visible")
    
  • page.wait_for_url(url, timeout=30000): 等待页面导航到指定的 URL(支持通配符和正则表达式)。

    # 等待页面跳转到 dashboard 页
    page.wait_for_url("**/dashboard")
    
  • page.wait_for_function(js_function, arg=None, timeout=30000): 等待页面中的 JavaScript 函数返回真值。这是最灵活的等待方式。

    # 等待 window.isDataReady 变量变为 true
    page.wait_for_function("window.isDataReady === true")
    # 等待文档的高度超过 1000px
    page.wait_for_function("() => document.body.scrollHeight > 1000")
    
  • page.wait_for_timeout(milliseconds): 最后的手段。只有在没有其他更好方法时(例如等待一个非前端的后端处理),才使用固定等待。


二、处理复杂的 UI 组件

1. 下拉选择框(Select Dropdowns)
Playwright 为 <select> 元素提供了专用的 API,非常方便。

# 导航到有下拉框的页面,例如 https://demoqa.com/select-menu
page.goto("https://demoqa.com/select-menu")# 通过标签选择 'Green'
page.select_option("#oldSelectMenu", "green")# 通过值(value)选择 'Blue'
page.select_option("#oldSelectMenu", "blue")# 或者通过索引选择 (索引从0开始)
page.select_option("#oldSelectMenu", index=3)

2. 文件上传(File Uploads)
处理 <input type="file"> 元素非常简单。

# 设置文件路径,一次选择一个文件
page.set_input_files('input[type="file"]', "path/to/myfile.pdf")# 一次选择多个文件
page.set_input_files('input[type="file"]', ["file1.pdf", "file2.jpg"])# 清空选择(将 input 的值设为空)
page.set_input_files('input[type="file"]', [])

3. 处理对话框(Dialogs: Alert, Confirm, Prompt)
Playwright 可以监听并响应 JavaScript 的原生对话框。

# 在点击触发对话框的按钮之前,先注册一个监听器
page.once("dialog", lambda dialog: dialog.accept())  # 默认点击 "OK"
# 或者,也可以获取对话框消息并做出选择
def handle_dialog(dialog):print(f"对话框消息: {dialog.message}")if "确认删除" in dialog.message:dialog.accept()  # 点击 "OK" 或 "Confirm"else:dialog.dismiss()  # 点击 "Cancel"page.on("dialog", handle_dialog)# 现在执行会触发对话框的操作,例如点击删除按钮
page.click("#delete-button")
# 操作完成后,最好移除监听器,以免影响后续操作
page.remove_listener("dialog", handle_dialog)

三、Frame 和 Iframe 处理

网页中的 iframe 就像一个"页面中的页面"。要操作 iframe 内的元素,必须先切换到对应的 frame 对象上。

1. 通过 Name/URL 或选择器定位 Frame

# 通过 frame 的 name 属性或 URL 匹配
frame = page.frame(name="my-frame")
frame = page.frame(url="**/login.html")# 通过 CSS/XPath 选择器定位 iframe 元素,然后获取其对应的 frame 对象
iframe_element = page.query_selector(".my-iframe")
frame = iframe_element.content_frame

2. 在 Frame 内操作元素
获取到 frame 对象后,其 API 和 page 对象几乎一样。

# 在 frame 内点击、填写
frame.click("button")
frame.fill("#username", "myuser")# 也可以使用 with 语句进行上下文切换,这是一种更清晰的方式
with page.frame("my-frame"):page.click("button")  # 这里的 page 操作会自动限定在 frame 内

四、管理多个 Page 和 Tab

点击一个链接有时会打开新标签页或新窗口。Playwright 可以轻松管理多个页面。

# 在点击之前,监听新的 page 事件
with page.expect_popup() as popup_info:page.click('a[target="_blank"]')  # 点击一个会打开新窗口的链接# 获取新打开的 page 对象
new_page = popup_info.value# 等待新页面加载完成
new_page.wait_for_load_state()# 在新页面上进行操作
print(new_page.title())# 操作完成后,关闭新页面
new_page.close()# 切回原始页面继续操作
page.bring_to_front()  # 将原始页面提到最前(激活状态)
page.click("#back-to-original")

五、实战演练:一个综合案例

让我们模拟一个稍复杂的场景:登录一个需要处理 iframe 和等待的网站。

from playwright.sync_api import sync_playwrightdef test_complex_login():with sync_playwright() as p:# 启动浏览器,设置视口大小browser = p.chromium.launch(headless=False)context = browser.new_context(viewport={"width": 1920, "height": 1080})page = context.new_page()# 1. 导航到登录页page.goto("https://example.com/login")# 2. 处理登录表单(假设它在 iframe 里)login_frame = page.frame(url="**/login-iframe")assert login_frame, "Login iframe not found"# 在 iframe 内操作login_frame.fill("#username", "testuser")login_frame.fill("#password", "testpass")# 3. 点击登录按钮,等待导航到仪表盘(Dashboard)with page.expect_navigation():  # 等待主页面发生导航login_frame.click("#submit-btn")# 4. 等待仪表盘上的某个关键元素出现page.wait_for_selector("text=Welcome, testuser", state="visible")page.wait_for_url("**/dashboard")# 5. 进行一些操作,例如上传头像# 假设点击一个按钮会触发文件选择对话框page.click("#upload-avatar")page.set_input_files('input[type="file"]', "avatar.jpg")# 6. 等待上传成功的 Toast 消息出现并消失page.wait_for_selector("text=Upload successful", state="visible")page.wait_for_selector("text=Upload successful", state="hidden")# 7. 断言头像图片的 src 属性已更新avatar_src = page.get_attribute("#user-avatar", "src")assert avatar_src and "avatar" in avatar_src, "Avatar was not updated"print("✅ 综合测试通过!")browser.close()if __name__ == "__main__":test_complex_login()

中级篇总结

你现在已经掌握了处理真实世界 Web 应用自动化测试的中级技能:

  1. 智能等待:使用 wait_for_selector, wait_for_function 等替代固定等待,让脚本更稳定。
  2. 复杂组件交互:熟练处理下拉框文件上传JavaScript 对话框
  3. Frame 处理:能够定位 frame 对象并在其上下文内进行操作。
  4. 多页面管理:使用 expect_popup 来捕获和控制新打开的标签页。

下一步(高级篇预览)
在高级篇,我们将探索真正强大的功能,迈向专家水平:

  • 网络拦截与模拟:mock API 响应,修改请求头,实现"无依赖测试"。
  • 认证与持久化状态:如何保存登录状态,实现一次登录多次测试。
  • 高级浏览器配置:代理设置、语言、地理位置模拟。
  • 录制视频与追踪:生成测试执行的视频和可视化报告。
  • 与 Pytest 集成:如何组织测试用例、夹具(fixtures)和生成报告。
http://www.xdnf.cn/news/19525.html

相关文章:

  • Windows PowerShell
  • QT6(QStandardItemModel和QTableView及自定义代理)
  • 【数据结构】并查集
  • Nodejs之HelloWord Hello-Http
  • 深度学习篇---MobileNet
  • 【系列12】端侧AI:构建与部署高效的本地化AI模型 第11章:边缘设备与IoT部署
  • C++ 面试高频考点 力扣 69. x 的平方根 二分查找 题解 每日一题
  • 鸿蒙创新赛活动——Mac提交压缩失败后续
  • [知识点记录]SQLite 数据库和MySQL 数据库有什么区别?
  • 服务器音频查找
  • 【MD文本编辑器Typora】实用工具推荐之——轻量级 Markdown 编辑器Typora下载安装使用教程 办公学习神器
  • 【Android】LayoutInflater 控件实例化的桥梁类
  • 【Linux】模拟实现Shell(上)
  • 【大模型面试宝典之微调篇】(一)
  • 【C++详解】C++11(二) lambda表达式、类型分类、引⽤折叠、完美转发
  • JavaEE初阶网络原理-初识
  • ArrayList源码解析之序列化
  • 【LeetCode 热题 100】64. 最小路径和——(解法二)递推
  • DSPFilters实现低通滤波器(QT)
  • 【开题答辩全过程】以 留守儿童志愿者服务系统为例,包含答辩的问题和答案
  • Java全局异常处理器:优雅处理系统异常
  • 数学运算符号:跨越千年的智慧结晶与文明印记
  • strtok()字符串分隔函数
  • VideoPoet:Google发布的用于视频生成的大语言模型
  • 【C#】在一个任意旋转的矩形(由四个顶点定义)内绘制一个内切椭圆
  • SpringAI应用开发面试实录:核心技术、架构设计与业务场景全解析
  • 华为研发投资与管理实践(IPD)读书笔记
  • VSCode `tasks.json` 中 `tasks` 数组的详细解析
  • 语义分析:从读懂到理解的深度跨越
  • Photoshop - Ps 标尺