Python API

API 教程与代码示例

掌握 Playwright Python 最常用的 API,配合可运行的代码示例快速学习。

页面导航

控制浏览器页面的打开、跳转、前进后退和等待加载。

navigation.py
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")
    page.goto("https://example.com/login", wait_until="networkidle")

    # wait_until 选项:
    #   "load"         — 页面 load 事件触发(默认)
    #   "domcontentloaded" — DOMContentLoaded 事件
    #   "networkidle"  — 500ms 无网络请求
    #   "commit"       — 收到网络响应

    # 前进后退与刷新
    page.go_back()
    page.go_forward()
    page.reload()

    # 获取页面信息
    print(page.url)        # 当前 URL
    print(page.title())    # 页面标题
    print(page.content())  # 完整 HTML

    # 设置超时
    page.set_default_timeout(30000)  # 30 秒
    page.set_default_navigation_timeout(60000)  # 导航 60 秒

    browser.close()

💡 提示

networkidle 等待所有网络请求完成,适合首屏数据加载;但对于有长轮询或 WebSocket 的页面,建议用 domcontentloaded 配合 wait_for_selector

元素定位

Playwright 提供多种选择器引擎,推荐优先使用语义化定位方法。

selectors.py — 推荐方式
# ===== 推荐:语义化定位方法 =====

# 按角色(ARIA role)定位 — 最推荐
page.get_by_role("button", name="提交")
page.get_by_role("heading", name="欢迎")
page.get_by_role("link", name="首页")
page.get_by_role("textbox", name="用户名")
page.get_by_role("checkbox", name="记住我")

# 按可见文本定位
page.get_by_text("立即注册")
page.get_by_text("注册", exact=True)  # 精确匹配

# 按 label 定位表单元素
page.get_by_label("邮箱地址")
page.get_by_label("密码")

# 按 placeholder 定位
page.get_by_placeholder("请输入搜索内容")

# 按 alt text 定位图片
page.get_by_alt_text("公司 Logo")

# 按 data-testid 定位(适合与开发协作)
page.get_by_test_id("submit-button")
selectors.py — CSS / XPath / 组合
# ===== CSS 选择器 =====
page.locator("#login-form")
page.locator(".btn.btn-primary")
page.locator("input[type='email']")
page.locator("div.card >> h3")  # 嵌套

# ===== XPath =====
page.locator("xpath=//button[@class='submit']")

# ===== 链式过滤 =====
page.locator(".product-card").filter(
    has_text="Python 入门"
).first

# ===== 第 N 个元素 =====
page.locator("li.item").nth(2)  # 第 3 个(从 0 开始)
page.locator("li.item").first
page.locator("li.item").last

# ===== 计数 =====
count = page.locator("li.item").count()
print(f"共 {count} 个元素")

页面操作

点击、输入、选择、拖放等常用交互操作。

actions.py
# ===== 点击操作 =====
page.click("#submit")
page.dblclick("#item")              # 双击
page.click("#menu", button="right")  # 右键
page.click("#link", modifiers=["Shift"])  # Shift+点击

# ===== 文本输入 =====
page.fill("#username", "admin")         # 清空再填入
page.type("#search", "Playwright", delay=80)  # 模拟逐字输入
page.press("#search", "Enter")        # 按键

# ===== 下拉选择 =====
page.select_option("#city", "shanghai")        # 按 value
page.select_option("#city", label="上海")      # 按显示文本
page.select_option("#tags", ["python", "web"])  # 多选

# ===== 复选框与单选框 =====
page.check("#agree")     # 勾选
page.uncheck("#agree")   # 取消勾选
page.set_checked("#agree", True)  # 设置状态

# ===== 文件上传 =====
page.set_input_files("input[type='file']", "report.pdf")
page.set_input_files("input[type='file']", ["a.png", "b.png"])
page.set_input_files("input[type='file']", [])  # 清空

# ===== 拖放 =====
page.drag_and_drop("#source", "#target")

# ===== 悬停 =====
page.hover(".dropdown-trigger")

# ===== 聚焦 =====
page.focus("#email")

# ===== 截图 =====
page.screenshot(path="full.png", full_page=True)
page.locator(".chart").screenshot(path="chart.png")

等待与断言

Playwright 内置的自动等待与 expect 断言 API。

waiting.py
# ===== 自动等待(内置,通常不需要手动等待) =====
# page.click() 自动等待元素:可见、稳定、可接受事件、已启用
# page.fill() 自动等待元素:可见、已启用、可编辑

# ===== 显式等待 =====
# 等待元素出现
page.wait_for_selector(".result", state="visible")
page.wait_for_selector(".spinner", state="hidden")
page.wait_for_selector(".modal", state="detached")

# 等待 URL 变化
page.wait_for_url("**/dashboard")
page.wait_for_url(lambda url: "success" in url)

# 等待网络请求
with page.expect_response("**/api/data") as resp:
    page.click("#load-data")
data = resp.value.json()
assertions.py — 配合 pytest
from playwright.sync_api import expect

# ===== 页面断言 =====
expect(page).to_have_title("Dashboard")
expect(page).to_have_url("https://example.com/dashboard")

# ===== 元素断言 =====
locator = page.locator(".message")
expect(locator).to_be_visible()
expect(locator).to_have_text("操作成功")
expect(locator).to_contain_text("成功")
expect(locator).to_have_class("alert alert-success")
expect(locator).to_have_attribute("href", "/home")
expect(locator).to_be_enabled()
expect(locator).to_be_checked()

# 否定断言
expect(locator).not_to_be_visible()

# 列表断言
items = page.locator("ul > li")
expect(items).to_have_count(5)
expect(items).to_have_text(["A", "B", "C", "D", "E"])

网络处理

拦截请求、Mock API、监控网络活动。

network.py
# ===== 监听请求与响应 =====
page.on("request", lambda req: print(f">> {req.method} {req.url}"))
page.on("response", lambda res: print(f"<< {res.status} {res.url}"))

# ===== 拦截并修改请求 =====
def block_images(route):
    if route.request.resource_type == "image":
        route.abort()
    else:
        route.continue_()

page.route("**/*", block_images)

# ===== Mock API 响应 =====
def mock_api(route):
    route.fulfill(
        status=200,
        content_type="application/json",
        body='{"users": [{"name": "Alice"}, {"name": "Bob"}]}'
    )

page.route("**/api/users", mock_api)

# ===== 修改请求头 =====
def add_auth(route):
    headers = route.request.headers
    headers["Authorization"] = "Bearer my-token"
    route.continue_(headers=headers)

page.route("**/api/**", add_auth)

# ===== 等待特定请求 =====
with page.expect_request("**/api/submit") as req:
    page.click("#submit")
print(req.value.post_data)

高级功能

截图录屏、PDF 导出、多标签页、设备模拟等进阶用法。

device_emulation.py
# 模拟移动设备
iphone = p.devices["iPhone 14"]
ctx = browser.new_context(**iphone)
page = ctx.new_page()

# 模拟地理位置
ctx = browser.new_context(
    geolocation={
        "latitude": 31.23,
        "longitude": 121.47
    },
    permissions=["geolocation"]
)

# 模拟暗色模式
ctx = browser.new_context(
    color_scheme="dark"
)
multi_tab.py
# 处理新标签页
with context.expect_page() as new:
    page.click("a[target='_blank']")
new_page = new.value
print(new_page.url)

# 处理弹窗
page.on("dialog", lambda d: d.accept())
page.click("#delete")

# 处理文件下载
with page.expect_download() as dl:
    page.click("#download-btn")
download = dl.value
download.save_as("report.xlsx")
pdf_and_video.py
# 导出 PDF(仅 Chromium headless)
page.pdf(
    path="page.pdf",
    format="A4",
    print_background=True,
    margin={"top": "1cm", "bottom": "1cm"}
)

# 录制视频
ctx = browser.new_context(
    record_video_dir="./videos",
    record_video_size={"width": 1280, "height": 720}
)
page = ctx.new_page()
# ... 操作 ...
ctx.close()  # 关闭后视频保存
print(page.video.path())
auth_state.py
# 保存登录状态(避免重复登录)
ctx = browser.new_context()
page = ctx.new_page()
page.goto("https://example.com/login")
page.fill("#user", "admin")
page.fill("#pass", "secret")
page.click("#login")

# 保存状态到文件
ctx.storage_state(path="auth.json")

# 后续测试直接加载
ctx2 = browser.new_context(
    storage_state="auth.json"
)
# 已经是登录状态!