# Playwright 快速参考手册
关键区别: 误解1: 能否先 答案: 这取决于具体情况。复制的内容存储在「网页的虚拟剪贴板」中,只要页面没有刷新,虚拟剪贴板数据会保留。 可以工作的条件: 会失败的情况: 误解代码: 为什么不行? press() 的返回值始终是 None Playwright 只是发送按键事件,不能拦截被复制的内容 虚拟剪贴板在页面刷新时会被清空 形象类比: 优点: 风险: 如果中间出现弹窗、页面更新等,可能导致粘贴失败 优点: 这是你提到的方法!它有 4 个参数: 方法签名: 参数详解: state 参数的 4 个值: 返回值: 使用示例: 注意区别: 重要纠正:我之前的说法完全错误! 根据 Playwright 源代码验证,ElementHandle 和 Locator 的操作方法都返回 None。 实际的返回值(通过代码验证): 这意味着什么? 那 Locator 支持什么样的链式? Locator 支持的是「定位器链式」(通过定位方法返回新的 Locator),不是「操作方法链式」: ElementHandle 是什么? ElementHandle 可以做的操作: 对比:wait_for_selector vs locator 为什么还是推荐用 Locator? 正确的使用方式: 为什么能工作? 为什么 page.wait_for_selector().click().is_visible() 不行? 对比 Locator 的定位器链式: 总结: Selenium 的问题: Playwright 的优势:1. 创建 Playwright 对象
方法1:上下文管理器(推荐)
from playwright.sync_api import sync_playwright
with sync_playwright() as pw:
browser = pw.chromium.launch()
page = browser.new_page()
# 执行操作
browser.close()方法2:手动管理生命周期
from playwright.sync_api import sync_playwright
pw = sync_playwright().start()
browser = pw.chromium.launch()
page = browser.new_page()
# 执行操作
browser.close()
pw.stop()2. 创建浏览器对象
# 有头浏览器(显示界面)
browser = pw.chromium.launch(headless=False)
# 无头浏览器(后台运行)
browser = pw.chromium.launch(headless=True)
# 其他浏览器
browser = pw.firefox.launch()
browser = pw.webkit.launch()3. 创建 Page 对象
page = browser.new_page()4. 打开网页
page.goto(url)
page.goto(url, wait_until="networkidle") # 等待网络空闲
page.goto(url, timeout=30000) # 设置超时时间(毫秒)5. 获取网页信息
获取网页源代码
html_content = page.content()获取网页 URL
current_url = page.url获取网页标题
page_title = page.title()6. 元素定位和交互
定位元素
# 返回 Locator 对象(懒加载)
locator = page.locator("xpath=//button[@id='submit']")
locator = page.locator("css=.button-class")
locator = page.locator("#element-id")
locator = page.locator(".element-class")获取多个元素的操作
# 获取第一个元素
first_element = page.locator("//div[@class='item']").first
# 获取最后一个元素
last_element = page.locator("//div[@class='item']").last
# 获取指定索引的元素(0 开始)
nth_element = page.locator("//div[@class='item']").nth(2)
# 获取所有元素列表
all_elements = page.locator("//div[@class='item']").all()
for element in all_elements:
print(element.inner_text())7. 文本输入
使用 fill() - 清空后填充(推荐用于输入框)
page.locator("//input[@name='username']").fill("user123")使用 type() - 逐字输入(模拟用户输入)
# 正常速度输入
page.locator("//input[@name='username']").type("user123")
# 添加延迟(每个字符之间延迟 100ms)
page.locator("//input[@name='username']").type("user123", delay=100)8. 获取元素信息
获取元素文本内容
text = page.locator("//div[@class='content']").inner_text()获取元素属性
# 获取指定属性值
href = page.locator("//a").get_attribute("href")
class_name = page.locator("//div").get_attribute("class")
id_value = page.locator("//button").get_attribute("id")获取元素的 HTML 内容
html = page.locator("//div[@class='content']").inner_html()9. 鼠标操作
点击元素
page.locator("//button[@id='submit']").click()
# 双击
page.locator("//button").dblclick()
# 右击
page.locator("//button").click(button="right")鼠标低级操作
# 移动鼠标到指定坐标
page.mouse.move(x=100, y=200)
# 鼠标按下
page.mouse.down()
# 鼠标释放
page.mouse.up()
# 完整的拖拽操作示例
page.mouse.move(100, 100)
page.mouse.down()
page.mouse.move(200, 200)
page.mouse.up()10. 拖拽操作
使用 drag_and_drop()(参数必须是选择器字符串)
# ❌ 错误做法(不能传 Locator 对象)
# source_locator = page.locator("//div[@id='source']")
# target_locator = page.locator("//div[@id='target']")
# page.drag_and_drop(source=source_locator, target=target_locator) # 不行!
# ✅ 正确做法1:直接用选择器字符串
page.drag_and_drop(
source="//div[@id='source']",
target="//div[@id='target']"
)使用 drag_to()(可以传 Locator 对象)⭐ 推荐
# ✅ 正确做法2:使用 Locator 对象的 drag_to 方法
source_locator = page.locator("//div[@id='source']")
target_locator = page.locator("//div[@id='target']")
source_locator.drag_to(target_locator) # 可以传 Locator 对象!page.drag_and_drop(source, target) → 参数必须是选择器字符串locator.drag_to(target_locator) → 参数可以是 Locator 对象 ✓11. 键盘操作
直接按键
# 单个按键
page.keyboard.press('Enter')
page.keyboard.press('Backspace')
page.keyboard.press('Delete')
# 组合键
page.keyboard.press('Control+a') # 全选
page.keyboard.press('Control+c') # 复制
page.keyboard.press('Control+v') # 粘贴
page.keyboard.press('Control+z') # 撤销
page.keyboard.press('Control+s') # 保存
page.keyboard.press('Alt+Tab') # 切换窗口键盘输入
# 指定输入框中输入文本
page.keyboard.type("Hello World")
# 输入文本并带延迟(毫秒)
page.keyboard.type("Hello", delay=100)12. 复制粘贴操作(重点解答)
⚠️ 常见误解
Ctrl+A 复制,然后定位新标签,再 Ctrl+V 粘贴?⚠️ 常见误解2:能否用变量保存 press() 的返回值?
# ❌ 错误理解
data = page.keyboard.press('Control+c') # 期望 data 里有复制的内容
print(data) # 输出结果:None(空值)
# 然后页面刷新...
page.locator("//input[@id='target']").fill(data) # ✗ 粘贴不了,data 是 Nonedef press(self, key: str) -> None: # 返回值是 None
# 只是发送一个"按键事件",不会返回任何数据page.keyboard.press('Control+c') 向浏览器发送"按 Ctrl+C"的信号这就像你让朋友按下"复制"按钮,然后关闭了程序,
再打开时期望之前复制的东西还在一样 — 不可能的!✅ 方案1:直接获取值再填充(最推荐,100% 可靠)
# 最简单、最稳定的方案!
source_text = page.locator("//input[@id='source']").input_value()
page.locator("//input[@id='target']").fill(source_text)✅ 方案2:使用键盘快捷键(需要谨慎)
# 点击源标签并复制
page.locator("//input[@id='source']").click()
page.keyboard.press('Control+a')
page.keyboard.press('Control+c')
# 立即点击目标标签并粘贴(中间不要有其他操作)
page.locator("//input[@id='target']").click()
page.keyboard.press('Control+v')
# 可选:等待粘贴完成
page.wait_for_timeout(500)✅ 方案3:使用浏览器剪贴板 API(最稳定)
# 读取源标签的文本
source_text = page.locator("//input[@id='source']").input_value()
# 通过 JavaScript 将文本写入浏览器剪贴板
page.evaluate(f'navigator.clipboard.writeText("{source_text}")')
# 点击目标标签并粘贴
page.locator("//input[@id='target']").click()
page.keyboard.press('Control+v')📊 三种方案对比
方案 可靠性 复杂度 跨页面 推荐度 方案1:直接填充 ⭐⭐⭐⭐⭐ ⭐ ✓ ⭐⭐⭐⭐⭐ 方案2:键盘快捷键 ⭐⭐⭐ ⭐⭐ ✗ ⭐⭐⭐ 方案3:剪贴板 API ⭐⭐⭐⭐ ⭐⭐⭐ ✓ ⭐⭐⭐⭐ 13. 其他常用操作
Frame(iframe)操作 ⭐ Playwright 的强项
获取 frame 对象
# 按 name 属性获取 frame
frame = page.frame(name="payment_frame")
# 按 url 获取 frame
frame = page.frame(url="https://example.com/iframe")
# 获取所有 frames
all_frames = page.frames
# 列出所有 frame 的信息
for frame in page.frames:
print(f"Frame name: {frame.name}, URL: {frame.url}")在 frame 中定位元素
# 方法1:先获取 frame,再定位(推荐用于多次操作)
frame = page.frame(name="payment_frame")
frame.locator("//input[@id='card_number']").fill("1234-5678-9012-3456")
frame.locator("//button[@id='submit']").click()
# 方法2:直接链式调用(推荐用于一次性操作)
page.frame(name="payment_frame") \
.locator("//input[@id='card_number']") \
.fill("1234-5678-9012-3456")
# 方法3:使用 frame_locator(按选择器查找 frame)
page.frame_locator("//iframe[@id='payment_frame']") \
.locator("//input[@id='card_number']") \
.fill("1234-5678-9012-3456")嵌套 iframe 操作
# 多层 iframe 嵌套,直接链式调用
page.frame(name="outer") \
.frame(name="inner") \
.frame(name="deepest") \
.locator("//button[@id='submit']") \
.click()
# 或者分步骤写(更易读)
outer_frame = page.frame(name="outer")
inner_frame = outer_frame.frame(name="inner")
inner_frame.locator("//input[@id='nested_input']").fill("数据")
# 或者混合方式
outer = page.frame(name="outer")
outer.frame(name="inner") \
.locator("//input[@id='nested_input']") \
.fill("数据")
outer.locator("//button[@class='submit']").click()获取 frame 中元素的信息
frame = page.frame(name="payment_frame")
# 获取元素文本
text = frame.locator("//span[@id='price']").inner_text()
# 获取元素属性
href = frame.locator("//a[@id='link']").get_attribute("href")
# 获取元素的 HTML
html = frame.locator("//div[@id='content']").inner_html()
# 获取输入框的值
value = frame.locator("//input[@id='username']").input_value()在 frame 中执行 JavaScript
frame = page.frame(name="payment_frame")
# 执行 JavaScript 代码
result = frame.evaluate("1 + 1")
print(result) # 2
# 获取 DOM 信息
count = frame.evaluate("document.querySelectorAll('input').length")
# 修改 DOM
frame.evaluate("document.getElementById('price').innerText = '¥99'")✅ 关键优势:无需手动切换上下文
# Playwright 会自动管理 frame 上下文
# 这段代码在 frame 中操作...
page.frame(name="payment_frame") \
.locator("//input[@id='card_number']") \
.fill("1234-5678-9012-3456")
# ...然后直接回到主页面操作,无需手动切换!
page.locator("//h1").inner_text() # ✓ 自动就在主页面
# Selenium 就需要这样做:
# browser.switch_to.frame("payment_frame")
# browser.find_element(By.ID, "card_number").send_keys("...")
# browser.switch_to.default_content() # 必须手动切换回来!
# browser.find_element(By.TAG_NAME, "h1").text等待操作
⭐ 方法1:等待元素状态变化(page.wait_for_selector)
page.wait_for_selector(
selector: str, # 选择器(必填)
state: str = None, # 元素状态(可选)
timeout: float = None, # 超时时间(可选,单位:秒)
strict: bool = None # 严格模式(可选)
) -> Optional[ElementHandle]参数 说明 默认值 单位 selectorCSS 选择器或 XPath 表达式(必填) - - state元素状态,有 4 个值(见下表) "attached"- timeout等待超时时间 None(30秒) 秒 ⚠️ strict严格模式(选择器必须恰好匹配 1 个元素) None - 值 含义 使用场景 "attached"元素在 DOM 中(默认) 元素加载到页面 "visible"元素可见(附加且不隐藏) 等待元素显示 ⭐ 最常用 "hidden"元素隐藏 等待加载动画消失 "detached"元素从 DOM 完全移除 等待弹窗关闭 ElementHandle 对象(如果 state="visible" 或 "attached")None(如果 state="hidden" 或 "detached")# 最基础:等待元素出现
page.wait_for_selector("//button[@id='submit']")
# 等待元素可见(最常用)
element = page.wait_for_selector("//button[@id='submit']", state="visible")
element.click() # 使用返回的 ElementHandle
# 等待元素消失
page.wait_for_selector("//loading", state="hidden")
# 自定义超时时间(单位:秒)⚠️ 注意不是毫秒!
page.wait_for_selector("//button", state="visible", timeout=10) # 10 秒
# 严格模式:选择器必须恰好匹配一个元素
page.wait_for_selector("//button[@class='submit']", strict=True)
# 获取返回的元素并操作
element = page.wait_for_selector("//input[@id='username']", state="visible")
element.fill("user123")
element.press("Enter")方法2:等待元素状态变化(locator().wait_for)
# 等待元素在 DOM 中出现(默认)
page.locator("//button[@id='submit']").wait_for()
# 等待元素可见(推荐)
page.locator("//button[@id='submit']").wait_for(state="visible")
# 等待元素可见,自定义超时时间(10秒,单位:毫秒)⚠️ 注意是毫秒!
page.locator("//button[@id='submit']").wait_for(state="visible", timeout=10000)
# 等待元素消失(隐藏)
page.locator("//loading-spinner").wait_for(state="hidden")
# 等待元素从 DOM 完全移除
page.locator("//modal").wait_for(state="detached")page.wait_for_selector() 返回 ElementHandle,可以继续操作locator().wait_for() 返回 None,不能链式调用wait_for_selector() 用秒,locator().wait_for() 用毫秒!方法3:等待页面加载完成
# 等待页面加载完成(推荐)
page.wait_for_load_state("load")
# 等待 DOM 内容加载完成(较快)
page.wait_for_load_state("domcontentloaded")
# 等待网络空闲(所有网络请求完成)
page.wait_for_load_state("networkidle")
# 自定义超时时间
page.wait_for_load_state("load", timeout=10)
# 在 goto 中使用更方便(推荐)
page.goto(url, wait_until="networkidle")方法4:等待指定时间(不推荐)
# 等待 2 秒(毫秒为单位)
page.wait_for_timeout(2000)
# ⚠️ 尽量避免使用!应该用等待方法而不是固定延迟方法5:等待 JavaScript 条件成立
# 等待直到元素文本变为特定值
page.wait_for_function(
"document.getElementById('status').textContent === '加载完成'"
)
# 等待直到某个 JavaScript 变量存在
page.wait_for_function("typeof window.myVar !== 'undefined'")
# 等待直到满足条件(自定义超时)
page.wait_for_function(
"document.querySelectorAll('.item').length > 10",
timeout=5
)方法6:等待页面导航完成
# 简单等待导航
page.locator("//a[@href='/next']").click()
page.wait_for_navigation()
# 等待导航到特定 URL
page.wait_for_navigation(url="**/success")
# 使用 with 语句(最推荐)
with page.expect_navigation():
page.locator("//a").click()📊 wait_for_selector vs locator().wait_for 对比
特性 wait_for_selector()locator().wait_for()返回值 ElementHandle 或 NoneNone可以获取元素 ✅ 可以 ❌ 不能 可以继续操作返回值 ✅ 可以 ❌ 不能 参数:state ✅ 支持 ✅ 支持 参数:timeout 单位 秒 ⚠️ 毫秒 参数:strict ✅ 支持 ❌ 不支持 推荐度 ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ 使用场景 需要获取元素时 现代推荐方式 💡 深入理解:ElementHandle vs Locator(完全纠正)
# Locator 的操作方法签名
Locator.click(...) -> None # 返回 None!不是 Locator!
Locator.fill(...) -> None # 返回 None!不是 Locator!
Locator.press(...) -> None # 返回 None!不是 Locator!
Locator.type(...) -> None # 返回 None!不是 Locator!
# ElementHandle 的操作方法
ElementHandle.click(...) -> None
ElementHandle.fill(...) -> None
ElementHandle.press(...) -> None# ❌ Locator 不能链式调用操作方法
page.locator("//button").click().fill() # 错误!click() 返回 None
page.locator("//button").click().is_visible() # 错误!
# ❌ ElementHandle 也不能链式调用操作方法
element = page.wait_for_selector("//button")
element.click().fill() # 错误!click() 返回 None
# ✅ 但单个操作是可以的
page.locator("//button").click() # ✅ 可以
element.click() # ✅ 可以# ✅ 定位器链式(返回新的 Locator 对象)
page.locator("div").first.click() # first 返回 Locator
page.locator("div").last.click() # last 返回 Locator
page.locator("div").nth(0).click() # nth() 返回 Locator
page.locator("div").filter(has=...).click() # filter() 返回 Locator
# ❌ 操作方法链式(都返回 None)
page.locator("div").click().fill() # click() 返回 None,错误!
page.locator("div").fill().type() # fill() 返回 None,错误!page.wait_for_selector() 找到元素时,就返回这个 ElementHandleelement = page.wait_for_selector("//input[@id='username']")
element.click() # 点击(返回 None)
element.fill("user123") # 填充文本(返回 None)
element.type("text") # 输入文本(返回 None)
element.get_attribute("href") # 获取属性(返回字符串)
element.inner_text() # 获取文本(返回字符串)
element.press("Enter") # 按键(返回 None)
element.hover() # 悬停(返回 None)
element.screenshot() # 截图(返回图片)特性 wait_for_selector locator 返回值 ElementHandle Locator 基本操作(click、fill) ✅ 支持 ✅ 支持 操作方法链式 ❌ 不支持 ❌ 不支持 定位器链式(first、last、nth) ❌ 不支持 ✅ 支持 自动等待 ❌ 需要手动 wait_for() ✅ 内置自动等待 代码简洁度 ⭐⭐⭐ ⭐⭐⭐⭐⭐ # ✅ 推荐:用 Locator
page.locator("//button").click()
page.locator("//input").fill("text")
# ✅ Locator 定位器链式(unique feature)
page.locator("div").first.click()
page.locator("div").last.fill("text")
page.locator("table tr").nth(2).click()
# ✅ 可以用 wait_for_selector,但需要分开写
element = page.wait_for_selector("//button")
element.click()💡 最直白的解释:element.click() vs page.wait_for_selector().click()
element = page.wait_for_selector("//button")
# element 就是一个 ElementHandle 对象
element.click() # ✅ 这能工作element.click()
执行过程:
1️⃣ element 已经是 ElementHandle 对象
2️⃣ ElementHandle.click() 执行点击动作
3️⃣ 返回 None(但我们不继续使用它)
✅ 完成!page.wait_for_selector("//button").click().is_visible()
执行过程:
1️⃣ page.wait_for_selector("//button") → 返回 ElementHandle
2️⃣ ElementHandle.click() → 返回 None
3️⃣ None.is_visible() → ❌ 错误!None 没有 .is_visible() 方法page.locator("div").first.click()
执行过程:
1️⃣ page.locator("div") → 返回 Locator
2️⃣ Locator.first → 返回 Locator(注意!这是属性,不是方法)
3️⃣ Locator.click() → 可以工作page.wait_for_selector().click() 可以工作page.wait_for_selector().click().is_visible() 不行(返回 None)page.locator().click().fill() 也不行(返回 None)page.locator().first.click() 可以工作(first 返回 Locator)元素可见性判断
is_visible = page.locator("//button").is_visible()
is_enabled = page.locator("//button").is_enabled()获取元素计数
count = page.locator("//div[@class='item']").count()选择框操作
# 选择 select 框的选项
page.locator("//select[@name='country']").select_option("China")
page.locator("//select[@name='country']").select_option(value="cn")复选框操作
# 勾选
page.locator("//input[@type='checkbox']").check()
# 取消勾选
page.locator("//input[@type='checkbox']").uncheck()
# 切换状态
page.locator("//input[@type='checkbox']").click()14. 快速对比:Playwright vs Selenium
基础操作对比
操作 Playwright Selenium 获取源代码 page.content()driver.page_source点击 page.locator().click()element.click()输入 page.locator().fill()element.send_keys()等待 内置智能等待 需要显式等待 速度 ⚡ 更快 较慢 易用性 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ Frame(iframe)操作对比 - Playwright 的巨大优势 ⭐
HTML 结构示例
<html>
<body>
<h1>主页面内容</h1>
<iframe name="payment_frame" id="iframe-1">
<form>
<input id="card_number" placeholder="输入卡号">
<button>提交</button>
</form>
</iframe>
</body>
</html>❌ Selenium 的做法(需要手动切换上下文,冗长且容易出错)
from selenium import webdriver
from selenium.webdriver.common.by import By
browser = webdriver.Chrome()
browser.get("http://example.com")
# ❌ 直接找不到,因为还在主页面
try:
element = browser.find_element(By.ID, "card_number")
except:
print("❌ 找不到!因为还没进入 iframe")
# 必须先切换到 iframe
browser.switch_to.frame("payment_frame") # 按 name 切换
# 或者 browser.switch_to.frame(0) # 按索引
# 或者 browser.switch_to.frame(browser.find_element(By.ID, "iframe-1"))
# 现在才能找到
element = browser.find_element(By.ID, "card_number")
element.send_keys("1234-5678-9012-3456")
# ⚠️ 必须手动切换回主页面
browser.switch_to.default_content()
# 现在回到了主页面
h1 = browser.find_element(By.TAG_NAME, "h1")
print(h1.text) # "主页面内容"switch_to.frame())switch_to.default_content())✅ Playwright 的做法(直接链式调用,优雅!)
from playwright.sync_api import sync_playwright
with sync_playwright() as pw:
browser = pw.chromium.launch()
page = browser.new_page()
page.goto("http://example.com")
# 🎯 方法1:按 name 进入 iframe(最常用)
frame = page.frame(name="payment_frame")
frame.locator("//input[@id='card_number']").fill("1234-5678-9012-3456")
# 🎯 方法2:按选择器进入(用 frame_locator)
frame = page.frame_locator("//iframe[@id='iframe-1']")
frame.locator("//input[@id='card_number']").fill("1234-5678-9012-3456")
# 🎯 方法3:直接链式调用(最推荐,一行代码搞定!)
page.frame(name="payment_frame") \
.locator("//input[@id='card_number']") \
.fill("1234-5678-9012-3456")
# 🎯 方法4:多层 iframe 嵌套也很简单
page.frame(name="outer_frame") \
.frame(name="inner_frame") \
.locator("//button[@id='submit']") \
.click()
# ✅ 不需要切换回来!Playwright 自动管理上下文
page.locator("//h1").inner_text() # ✓ 自动就在主页面上🌲 复杂场景:嵌套 iframe 的对比
❌ Selenium(冗长且容易出错)
# 进入嵌套 iframe
browser.switch_to.frame("outer")
browser.switch_to.frame("inner")
element = browser.find_element(By.ID, "nested_input")
element.send_keys("数据")
# 需要逐层切回来,很容易出错
browser.switch_to.default_content() # 回到主页面
browser.switch_to.frame("outer") # 再进去
button = browser.find_element(By.XPATH, "//button[text()='按钮A']")
button.click()
browser.switch_to.default_content() # 再切回来✅ Playwright(简洁优雅)
# 进入嵌套 iframe 并输入(一行链式调用)
page.frame(name="outer") \
.frame(name="inner") \
.locator("//input[@id='nested_input']") \
.fill("数据")
# 点击外层按钮(无需切换,自动在正确的上下文)
page.frame(name="outer") \
.locator("//button[text()='按钮A']") \
.click()
# 或者保存中间变量,更易读
outer = page.frame(name="outer")
outer.frame(name="inner").locator("//input[@id='nested_input']").fill("数据")
outer.locator("//button[text()='按钮A']").click()Frame 操作详细对比表
维度 Selenium Playwright 进入 iframe switch_to.frame()page.frame()切换回主页面 switch_to.default_content()无需(自动) 链式调用 ❌ 不支持 ✅ 支持 多层 iframe 麻烦,需要多次切换 简洁,直接链式调用 代码行数 更多(包含切换逻辑) 更少(简洁优雅) 易错性 高(容易忘记切换) 低(自动管理) 可读性 ⭐⭐⭐ ⭐⭐⭐⭐⭐ 维护性 ⭐⭐⭐ ⭐⭐⭐⭐⭐ 15. 完整示例
from playwright.sync_api import sync_playwright
def test_web_automation():
with sync_playwright() as pw:
# 启动浏览器
browser = pw.chromium.launch(headless=False)
page = browser.new_page()
# 打开网页
page.goto("https://example.com")
print(f"页面标题: {page.title()}")
print(f"页面 URL: {page.url}")
# 定位输入框并输入
page.locator("//input[@name='search']").fill("Playwright")
# 点击搜索按钮
page.locator("//button[@type='submit']").click()
# 等待结果加载
page.wait_for_timeout(2000)
# 获取搜索结果
results = page.locator("//div[@class='result']").all()
print(f"找到 {len(results)} 个结果")
for result in results:
print(result.inner_text())
# 获取网页源代码
html = page.content()
print(f"源代码长度: {len(html)}")
# 关闭浏览器
browser.close()
if __name__ == "__main__":
test_web_automation()16. 常见错误排查
错误 原因 解决方案 TimeoutError元素加载超时 增加 wait_for_timeout() 或检查选择器选择器找不到元素 XPath/CSS 语法错误 用浏览器开发者工具验证选择器 鼠标操作失败 元素可能不可见 先滚动到元素位置或等待元素显示 拖拽不工作 坐标不准确 使用 drag_to() 而不是低级鼠标操作