如果你是第一次阅读本系列,建议从以下几篇开始:
第 1 篇:Playwright教程:Playwright从0到1搭建与第一个自动化用例(Python 实战)
第 2 篇:Playwright + Pytest:从单文件到模块化测试项目搭建
第 3 篇:Playwright教程:UI 元素定位全面入门(从最基础到写出稳定定位)
第 4 篇:Playwright + DeepSeek 实战:AI自动生成 POM 结构与测试用例模板
🧩 适合人群:新手、刚开始写自动化脚本的测试人员
🎯 教程目标:彻底解决新手最常见的报错 ——
TimeoutError: locator.click: Element not found
并教会你:Playwright 如何自动等待、什么时候需要手动等待、最常用的等待写法
一、为什么新手最容易遇到 “元素未找到”?
许多新手写自动化时都会遇到:
TimeoutError: locator.click: Element not found
第一反应通常是:
- time.sleep(3)
- 多加几秒
- 多点几次
然而,这样不仅无效,还会导致脚本:
-
• 不稳定 -
• 变慢 -
• 更难维护
真正原因并不是“没等够”,而是——
Playwright 在等,但你选择的等待方式不对。
二、Playwright 的「自动等待」比你想象的强大
Playwright 的所有操作——
click / fill / type / goto / expect
都默认带有智能等待逻辑:
-
• 等待元素出现 -
• 等待 DOM 稳定 -
• 等待元素可见 -
• 等待元素不被遮挡 -
• 等待元素可点击 -
• 等待动画完成 -
• 等待页面加载
例如:
page.get_by_role("button", name="Login").click()
Playwright 会等待按钮真正“可点击”后才执行。
如果超过默认 5 秒仍然不满足条件 → 才会报错。
三、等待失败的 4 个典型场景(入门必懂)
场景 1)页面跳得快,元素还没加载完
登录后跳转:
page.get_by_role("button", name="Login").click()
page.get_by_text("Products").is_visible()
这里 is_visible() 不会等待,就会报错。
正确写法:
from playwright.sync_api import expect
expect(page.get_by_text("Products")).to_be_visible()
场景 2)元素在 iframe 中
必须显式进入 iframe:
frame = page.frame_locator("#my-frame")
frame.get_by_text("OK").click()
场景 3)元素被遮挡
例如动画、弹窗浮层:
Element is not attached to the DOM
场景 4)元素在 SPA 中异步加载
比如 Vue/React 网站渲染时间较慢。
四、Playwright 最稳定的等待方法(新手必学)
✔ 方法 1:expect(最推荐)
from playwright.sync_api import expect
button = page.get_by_role("button", name="Login")
expect(button).to_be_visible()
这是 Playwright 推荐的等待方式
→ 自动等待 + 自动断言 + 语义清晰。
✔ 方法 2:等待 selector 出现
page.wait_for_selector("text=Products")
✔ 方法 3:等待 URL 跳转
page.wait_for_url("**/inventory.html")
✔ 方法 4:等待网络请求全部完成
page.wait_for_load_state("networkidle")
✔ 方法 5(进阶):等待 API 响应
with page.expect_response("**/cart.json") as resp:
page.get_by_text("Add to cart").click()
print(resp.value.status)
五、实战示例:更稳定的登录流程
以下示例适合初学者反复练习。
from playwright.sync_api import sync_playwright, expect
def test_login_wait_stable():
with sync_playwright() as pw:
browser = pw.chromium.launch(headless=False)
page = browser.new_page()
page.goto("https://www.saucedemo.com")
page.get_by_placeholder("Username").fill("standard_user")
page.get_by_placeholder("Password").fill("secret_sauce")
page.get_by_role("button", name="Login").click()
# ★ Playwright 会自动等待,避免随机失败
expect(page.get_by_text("Products")).to_be_visible()
browser.close()
六、常见等待误区(你可能也犯过)
❌ 误区 1:用 time.sleep
注意的问题是:
-
• 浪费时间 -
• 不稳定 -
• 遇到慢网速就挂
❌ 误区 2:用 assert 而不是 expect
assert page.locator("text=Products").is_visible()
解释如下:
-
• ⚠ is_visible() 是立即评估 -
• ⚠ 不会等待 -
• ⚠ 很容易失败
❌ 误区 3:定位方式太复杂
录制脚本会生成:
page.locator("div > div > button.btn.btn_primary").click()
太脆弱。
应该改成更稳定的:
page.get_by_role("button", name="Login").click()
七、什么时候必须手动等待?
默认自动等待就够用了,只有这 3 种情况需要手动等:
1)iframe 操作
必须进入 iframe:
page.frame_locator("#frame-id").locator("button").click()
2)需要等待 API 返回
with page.expect_response("**/checkout") as resp:
page.get_by_text("Checkout").click()
3)页面跳转较慢
使用:
page.wait_for_url("/inventory.html")
八、练习(推荐动手,能真正掌握)
练习 1:等待购物车图标加载
验证:
expect(page.locator(".shopping_cart_badge")).to_be_visible()
练习 2:等待按钮从 Add to cart 变成 Remove
page.get_by_text("Add to cart").click()
expect(page.get_by_text("Remove")).to_be_visible()
练习 3:等待页面资源加载完成
page.wait_for_load_state("networkidle")
九、总结:写自动化要学会“等对东西”
Playwright 等待机制的核心三句话:
-
1. 优先使用 expect -
2. 不要用 time.sleep -
3. 定位不稳,再多等待也没用
学会等待,自动化脚本的稳定性就会巨大提升。
如果觉得本教程和本公众号对你有帮助,还请 点个赞,关个注,下次更新不迷路!

