Playwright 是一个由微软开发的开源框架,用于可靠地实现端到端 Web 自动化测试。它支持所有现代浏览器,并提供 Python 和 TypeScript/JavaScript 等多种语言的 API。
1. 安装
Playwright 的安装分为两步:安装库和安装所需的浏览器驱动。
| Python | TypeScript / JavaScript | |
|---|---|---|
| 安装库 | pip install playwright |
npm init playwright@latest (这是一个交互式向导,会自动创建配置文件和示例) |
| 安装浏览器 | playwright install (会安装 Chromium, Firefox, WebKit) |
(npm 初始化向导会自动执行此步骤) 如果需要手动安装,则运行 npx playwright install |
2. 核心概念与常用 API
Playwright 的 API 在 Python 和 TS 中非常相似,主要区别在于 Python 使用 snake_case 命名法,而 TS 使用 camelCase。
2.1 核心对象层级
Playwright -> Browser -> BrowserContext -> Page
- Playwright: 入口点,用于启动浏览器实例。
- Browser: 代表一个浏览器实例 (如 Chromium, Firefox)。
- BrowserContext: 一个独立的浏览器会话(类似于隐身模式窗口)。上下文之间 Cookie 和本地存储是隔离的。这是管理会话和登录状态的最佳实践。
- Page: 代表浏览器上下文中的一个标签页,是与网页交互的主要对象。
2.2 启动与导航
| 功能 | Python | TypeScript |
|---|---|---|
| 同步启动 | from playwright.sync_api import sync_playwrightwith sync_playwright() as p:browser = p.chromium.launch(headless=False) |
(TS 主要使用异步) |
| 异步启动 | from playwright.async_api import async_playwrightasync with async_playwright() as p:browser = await p.chromium.launch() |
import { chromium } from 'playwright';const browser = await chromium.launch(); |
| 创建上下文 | context = browser.new_context() |
const context = await browser.newContext(); |
| 创建页面 | page = context.new_page() |
const page = await context.newPage(); |
| 访问 URL | page.goto("https://example.com") |
await page.goto("https://example.com"); |
| 关闭 | browser.close() |
await browser.close(); |
提示:
headless=False会以有头模式启动浏览器,方便调试时观察界面。
2.3 定位器 (Locators) - 最佳实践
定位器是 Playwright 中查找元素的核心机制。它们是自动等待和严格的,这意味着 Playwright 会在执行操作前自动等待元素出现,并且如果定位器匹配到多个元素,会抛出错误(除非明确处理多个元素)。
| 功能 | Python | TypeScript |
|---|---|---|
| CSS 选择器 | page.locator("#myId") |
page.locator('#myId') |
| 文本内容 | page.get_by_text("Submit") |
page.getByText('Submit') |
| 角色 (ARIA) | page.get_by_role("button", name="Sign in") |
page.getByRole('button', { name: 'Sign in' }) |
| 占位符 | page.get_by_placeholder("Enter email") |
page.getByPlaceholder('Enter email') |
| 链式定位 | list_item = page.locator("ul > li").firstlist_item.get_by_role("button").click() |
const listItem = page.locator('ul > li').first();await listItem.getByRole('button').click(); |
| 获取所有 | all_items = page.locator("div").all() |
const allItems = await page.locator('div').all(); |
2.4 页面交互
| 功能 | Python | TypeScript |
|---|---|---|
| 点击 | page.get_by_role("button").click() |
await page.getByRole('button').click(); |
| 填充输入框 | page.get_by_label("Password").fill("secret") |
await page.getByLabel('Password').fill('secret'); |
| 键盘按键 | page.get_by_role("textbox").press("Enter") |
await page.getByRole('textbox').press('Enter'); |
| 截图 | page.screenshot(path="screenshot.png") |
await page.screenshot({ path: 'screenshot.png' }); |
| 获取文本 | text = page.locator("h1").text_content() |
const text = await page.locator('h1').textContent(); |
| 获取属性 | href = page.locator("a").get_attribute("href") |
const href = await page.locator('a').getAttribute('href'); |
2.5 等待 (Waits)
尽管定位器会自动等待,但有时仍需要显式等待某些事件。
| 功能 | Python | TypeScript |
|---|---|---|
| 等待 URL | page.wait_for_url("**/dashboard") |
await page.waitForURL('**/dashboard'); |
| 等待加载状态 | page.wait_for_load_state("networkidle") |
await page.waitForLoadState('networkidle'); |
| 等待选择器 | page.wait_for_selector(".my-element", state="visible") |
await page.waitForSelector('.my-element', { state: 'visible' }); |
2.6 断言 (Assertions)
Playwright 内置了强大的断言库,它也会自动重试直到超时。
| 功能 | Python | TypeScript |
|---|---|---|
| 检查可见性 | expect(locator).to_be_visible() |
await expect(locator).toBeVisible(); |
| 检查文本 | expect(locator).to_have_text("Welcome") |
await expect(locator).toHaveText('Welcome'); |
| 检查数量 | expect(locator).to_have_count(5) |
await expect(locator).toHaveCount(5); |
3. 实战 Demo:登录 GitHub 并读取仓库列表
这个 Demo 分为两个脚本:
- 脚本一
login.py/login.ts: 登录 GitHub 并将包含 Cookie 和本地存储的认证信息保存到文件中 (auth.json)。 - 脚本二
scrape.py/scrape.ts: 加载认证文件,跳过登录直接访问用户的仓库页面,并打印仓库名称列表。
准备工作:
为了安全,请将你的 GitHub 用户名和密码设置为环境变量 GITHUB_USER 和 GITHUB_PASS。
Python Demo 🐍
脚本一: login.py (登录并保存状态)
1 | import os |
脚本二: scrape.py (使用状态并抓取数据)
1 | import os |
TypeScript Demo 🟦
脚本一: login.ts (登录并保存状态)
1 | import { test, expect, chromium } from '@playwright/test'; |
脚本二: scrape.ts (使用状态并抓取数据)
1 | import { test, expect, chromium } from '@playwright/test'; |