Playwright

Playwright简介

Playwright 是由 Microsoft 推出的现代化 浏览器 自动化框架,支持使用代码控制真实浏览器完成用户操作。可以把它理解为一个功能强大的 Web 浏览器控制器 。允许通过一套统一的 API,用代码去自动操作 Chromium(Chrome、Edge 的内核)、Firefox 和 WebKit(Safari 的内核)这三种主流浏览器自动完成"人类的浏览器操作":打开网页、点击、输入、拖拽、上传文件、截图/PDF、录制日志与回放,最常见的用途是 端到端(E2E)测试,也可做轻量爬取与流程自动化。。

核心特性:
1.智能自动等待:这是其最核心的特性之一。当你执行 click()、fill() 等操作时,Playwright 会自动检查元素是否可见、可用、稳定,直到满足可操作状态才会执行下一步 。这几乎消除了在代码中随意添加 time.sleep() 或手工设置等待时间的需要。

2.强大的选择器引擎:它提供了多种现代化的定位方式,让选择器更易写、更健壮 。可以通过元素的语义角色和名称定位;也可以通过标签文本、占位符或测试id 等来定位,也支持传统的 CSS 和 XPath 。

3.全面的测试工具链:Playwright 不仅是一个自动化库,还自带了一套完整的测试工具 :

  • Codegen(代码生成器):它会打开一个浏览器窗口和一个检查器。你在浏览器上的每一次点击、输入都会被自动转换成你指定语言的测试代码 。
  • Trace Viewer(追踪查看器):当测试失败时,它可以捕获完整的执行轨迹,包括操作录屏、每一步前后的 DOM 快照、网络请求等。
  • HTML 报告:测试运行后会自动生成详细的 HTML 报告,包含测试结果、耗时、错误日志和截图,方便结果分享和趋势分析 。

应用场景:

  1. 前端测试:自动测试网页的按钮、表单、导航等是否正常。
  2. 回归测试:新功能上线前,跑一遍全站测试用例,确保旧功能不出问题。
  3. 数据采集(爬虫):抓取动态网页(比如需要滚动才能看到的数据)。
  4. 网页截图 & PDF:给页面生成截图或 PDF(比如生成报告)。
  5. 流程自动化:自动化重复操作(如自动登录、批量表单填写)。
  6. 端到端(E2E)测试:模拟真实用户在关键路径上的完整操作流程,如注册、登录、下单结账等 。
  7. 跨浏览器测试:只需一套脚本,就能确保你的 Web 应用在 Chrome、Firefox 和 Safari 上都能正常工作,有效发现兼容性问题 。
  8. API 测试:它不仅限于 UI,还能直接发送请求测试后端 API,可以在 UI 测试中拦截和模拟(Mock)API 响应,用于测试各种边界情况 。
  9. 性能监控:定时运行 Playwright 脚本,可以测量关键页面的加载时间,对网站进行主动的“合成监控” 。

与传统工具(如 Selenium)的对比

功能PlaywrightSelenium
自动等待内置智能等待,操作前自动等待元素就绪需手动设置等待时间,脚本易不稳定
跨浏览器支持一套 API 无缝支持 Chromium, Firefox, WebKit支持,但各浏览器需单独驱动,配置繁琐
测试执行速度快速,通过 WebSocket 直接控制浏览器,支持原生并行较慢,基于 HTTP 的 WebDriver 协议有额外开销
支持语言官方支持 JS/TS, Python, Java, .NET支持 Java, Python, C#, Ruby, JS 等
核心功能与生态功能全面且强大:丰富内置,如网络拦截、移动端模拟、Trace 查看器等,提供 Trace Viewer 和 Codegen 等工具,生态由微软公司维护功能基础:多需依赖第三方库或插件,调试相对传统,多依赖日志和截图,生态成熟,社区庞大

Playwright快速入门

下文中Playwright特指Playwright for python(Playwright 官方为 Python 语言提供的库,可以用 Python 代码来无缝控制 Chromium、Firefox 和 WebKit 浏览器,实现 Web 自动化 )

环境准备:

  1. 安装python,Playwright内部使用异步(async/await)写法,所以需要python3.7及以上版本

官网地址:https://www.python.org/downloads/

  1. 安装Playwright

单独安装Playwright

pip install playwright

同时安装Playwright和pytest

pip install pytest-playwright
  1. 安装浏览器

Playwright需要使用特定的浏览器,这些浏览器的Playwright进行了编译并提供下载,安装命令:

playwright install  #安装所有支持的浏览器
playwright install chrome # 只安装chrome浏览器

Codegen一个交互式的代码生成器,能将浏览器中的操作实时输出为Playwright代码
在pycharm终端启动Codegen命令:

playwright codegen url  # url可以是任何想测试的网站

如:百度登录页面

playwright codegen https://passport.baidu.com/v2/?login

执行命令后将会出现两个窗口:

浏览器窗口:在次窗口中的所有操作(点击、输入、选择等)都将被记录,可以完全模拟用户操作

Playwright Inspector窗口:实时显示操作所对应的Playwright Python代码

添加断言
Codegen无法自动记录断言,可通过Inspector按钮添加断言:

  1. Assert visibility:断言元素的可见
  2. Assert text:断言元素的text属性
  3. Assert value:断言元素的value属性

Codegen应用场景:

  1. API学习:当不确定某个操作应用那个API时,手动操作一遍,Codegen会给出标准答案;
  2. 定位器探索:当在浏览器中移动鼠标时,Codegen会高亮元素并提示推荐的定位器策略;
  3. 快速原型开发:快速录制一个完整的业务流程,将生成的代码作为测试用例的初始框架,再进行后续的优化和封装。

Playwright API应用

Playwright最核心的三个对象:

  1. Browser:一个 Browser 实例代表了一个实际的浏览器,例如 Chromium、Firefox 或 WebKit;
  2. BrowserContext:一个 BrowserContext 就像一个独立的浏览器会话。它完全隔离了页面、本地存储、cookies 等数据。可以在一个浏览器中同时运行多个互不影响的自动化任务;
  3. Page:进行实际操作的地方。代表了浏览器上下文中的一个标签页或窗口。所有的操作,如点击、输入、导航、截图等,都是在 Page 对象上执行的。

同步/异步

  1. 同步模式 (sync_api):代码会按照顺序一行一行地执行,等前一个操作(如打开网页)完全结束后,才会执行下一个操作。它的优点是简单直观,容易上手。
from playwright.sync_api import sync_playwright
def run_sync():
    with sync_playwright() as p:    # 使用同步with语句管理上下文
        browser = p.chromium.launch(headless=False)  # 无头模式浏览器启动
        page = browser.new_page()    #  创建 Page 对象
        page.goto('https://example.com')  # 页面基本导航
        browser.close()  # 关闭浏览器
run_sync()
  1. 异步模式 (async_api):代码可以在等待一个操作(如页面加载)的同时,转而去执行另一个任务。它的优点是效率高,特别适合需要同时处理多个浏览器操作的场景。
import asyncio
from playwright.async_api import async_playwright
async def run_async():
    async with async_playwright() as p:    # 使用异步with语句管理上下文
        browser = await p.chromium.launch(headless=False)   # 无头模式浏览器启动  需要await
        page = await browser.new_page() #  创建 Page 对象  需要await
        await page.goto('https://example.com')  # 页面基本导航  需要await
        await browser.close()  # 关闭浏览器  需要await
asyncio.run(run_async())

对于绝大多数测试脚本和自动化任务,选择同步模式就足够了,简单、直接、不容易出错。

with 语句
在 Playwright 中,with 语句(上下文管理器)会自动处理浏览器实例和 Playwright 进程的创建与销毁,确保资源被正确释放,避免内存泄漏。

from playwright.sync_api import sync_playwright
with sync_playwright() as p:  # 使用with管理playwright的整个生命周期
    # 这里的p是PlaywrightContextManager对象
    browser = p.chromium.launch()
    page = browser.new_page()
    page.goto('https://example.com')
    print(page.title())
    browser.close()   # 退出with块后,playwright进程自动关闭

浏览器启动

#各种浏览器启动选项
browser = playwright.chromium.launch(
    headless=False,           # 显示浏览器界面
    slow_mo=100,             # 操作间延迟(毫秒)
    args=['--start-maximized'],  # 浏览器参数
    executable_path='/path/to/chrome',  # 指定浏览器路径
    timeout=30000,            # 超时时间
    devtools=True,            # 打开开发者工具
    proxy={
        'server': 'http://proxy.example.com:8080',
        'username': 'user',
        'password': 'pass'
    }
)

等待策略

#等待元素出现
page.wait_for_selector('#element-id', timeout=10000)
#等待特定条件
page.wait_for_function('document.querySelector(".content").innerText.includes("Hello")')
#等待超时
page.wait_for_timeout(3000)  # 等待3秒
#等待导航完成
with page.expect_navigation():
    page.click('a#submit')
#等待弹出窗口
with page.expect_popup() as popup_info:
    page.click('a[target="_blank"]')
popup = popup_info.value

等待策略多用于调试、爬取数据,测试使用场景较少,因Playwright存在自动等待,如fill方法默认等待最大时间30秒:
在这里插入图片描述

页面交互

  1. 页面导航
#基本导航
page.goto('https://example.com')
#带选项的导航
page.goto('https://example.com', 
    timeout=30000,           # 超时时间
    wait_until='networkidle', # 等待条件:'load'|'domcontentloaded'|'networkidle'
    referer='https://google.com' # 设置referer
)
#导航事件处理
page.on('load', lambda: print('Page loaded!'))
#前进和后退
page.go_back()
page.go_forward()
#重新加载
page.reload()
  1. 页面状态
#获取当前页面URL
current_url = page.url
#获取当前页面标题
page_title=page.title()
#获取完整的页面HTML内容
content = page.content()
  1. 页面动作
#设置页面的视口大小
page.set_viewport_size({"width":375,"height":812})
#捕获当前页面的截图
page.screenshot(path="screenshots/login")
#截取整个可滚动页面的完整长图,关键参数full_page=True
page.screenshot(path="screenshots/login_full",full_page=True)
#在页面上下文中执行一段JavaScript表达式并返回其结果
page.evaluate(localStorage.getItem("access_token"))

元素定位
Locator定位器的特性:

  1. 惰性定位:

Playwright使用Locator定位元素,但只有当该元素需要执行一个动作或断言时,才会根据Locator内容去页面上寻找匹配的元素。

  1. 自动等待:
    当Locator去页面上寻找元素时,当元素自动等待以下状态就绪,确保寻找到的元素可以进行使用:
  • Attached(已附加):元素已存在于DOM树中;
  • Visible(可见):元素及其祖先没有display:none或visibility:hidden等样式,且尺寸不为零;
  • Stable(稳定):元素已停止动画或过渡效果,位置固定;
  • ReceivesEvents(可接受事件):元素没有被其他元素遮挡,能够接收鼠标点击;
  • Enable(已启用):元素不是disabled状态;
  • Editable(可编辑):对于输入动作,元素必须是可编辑状态。

面向用户定位:基于可访问性(Accessibility)信息,模拟真实用户的感知方式

  1. Role定位 get_by_role() 通过元素的ARIA角色和可访问名称定位,最健壮、不受DOM结构变化影响。
#定位"登录"按钮
page.get_by_role("button", name="登录").click()
#定位文本输入框(通过关联的标签)
page.get_by_role("textbox", name="用户名").fill("myuser")
#定位标题
page.get_by_role("heading", name="欢迎回来", level=1)
  1. Label定位 get_by_label() 通过关联的
#定位"邮箱地址"对应的输入框
page.get_by_label("邮箱地址")
  1. 文本定位 get_by_text() 通过元素的可见文本内容定位,适合按钮、链接等。
#精确匹配
page.get_by_text("提交")
#模糊匹配(包含)
page.get_by_text("商品", exact=False)
#正则匹配
page.get_by_text(/确认.*/)
  1. 占位符定位 get_by_placeholder() 通过placeholder属性定位输入框输入框专用。
page.get_by_placeholder("请输入手机号")
  1. Alt文本定位get_by_alt_text()通过alt属性定位元素,通常是图像。
page.get_by_alt_text("产品图片")
  1. Title定位get_by_title()通过title属性定位元素。
page.get_by_title("用户头像")

面向结构定位:当用户定位方式不适用时使用的备选方案,依赖DOM结构。

  1. 测试ID定位 get_by_test_id()需要开发配合在元素上添加data-testid属性,完全隔离UI变化影响,稳定的结构定位。
#HTML: <button data-testid="login-submit">登录</button>
page.get_by_test_id("login-submit").click()
  1. CSS选择器 locator()灵活但易受样式变化影响。
#ID定位
page.locator("#username")
#类名定位
page.locator(".product-item")
#属性定位(支持模糊匹配)
page.locator('input[type="text"]')
page.locator('[class*="btn-primary"]')  # 包含
page.locator('[id^="input-"]')          # 开头匹配
  1. XPath定位强大但脆弱,维护成本高,作为最后手段。
#文本匹配
page.locator("xpath=//button[text()='登录']")
#组合条件
page.locator("xpath=//input[@type='checkbox' and @checked='true']")

高级定位技巧

  1. 组合定位(链式调用)从父元素出发定位子元素,提高精确度。
#先定位商品列表容器,再找具体商品
product_list = page.locator("ul.product-list")
products = product_list.locator("li.item")
#根据包含文本过滤
await page
  .get_by_role("row")
  .filter(has_text="待付款")
  .get_by_role("button", name="支付")
  .click()
  1. 可见性过滤,只操作可见元素。
#只点击可见的按钮
page.locator("button:visible:has-text('提交')").click()
  1. 包含关系定位,定位包含特定子元素的父元素。
#定位包含"特惠"标签的商品卡片
discount_product = page.locator('div.product:has(span.discount:has-text("特惠"))')
  1. 列表元素处理
#获取所有匹配元素
items = page.locator(".list-item")
count = items.count()  # 获取数量
#遍历处理
for i in range(count):
    item = items.nth(i)
    print(item.text_content())
#取第一个/最后一个
first = items.first()
last = items.last()
  1. iframe定位
#切换到iframe内定位
frame = page.frame_locator("iframe#payment-frame")
frame.locator("input#card-number").fill("1234")
  1. 动态元素处理
#等待元素出现再操作
page.wait_for_selector(".modal-content", state="visible")
#使用locator的自动等待特性
locator = page.locator(".dynamic-element")
locator.click()  # 自动等待直到可操作

元素交互

#点击操作
page.click('button')
page.dblclick('button')
page.click('button', button='right')  # 右键点击
page.click('button', modifiers=['Shift'])  # 带修饰键点击
#输入操作
page.fill('input#name', 'John Doe')
page.type('input#name', 'Hello World', delay=100)  # 模拟按键延迟
#选择操作
page.select_option('select#colors', 'red')
page.select_option('select#colors', index=1)  # 按索引选择
page.select_option('select#colors', value=['red', 'blue'])  # 多选
#复选框和单选按钮
page.check('input#agree')
page.uncheck('input#subscribe')
#文件上传
page.set_input_files('input[type=file]', 'path/to/file.txt')
page.set_input_files('input[type=file]', ['file1.txt', 'file2.txt'])  # 多文件
#聚焦和模糊
page.focus('input#search')
page.blur('input#search')
#悬停
page.hover('div.menu-item')
#拖放
page.drag_and_drop('#source', '#target')

高级交互

  1. 键盘操作
#键盘输入
page.keyboard.type('Hello World!')
page.keyboard.press('Enter')
page.keyboard.down('Shift')
page.keyboard.up('Shift')
#快捷键
page.keyboard.press('Control+A')  # 全选
page.keyboard.press('Control+C')  # 复制
page.keyboard.press('Control+V')  # 粘贴
#特殊键
page.keyboard.press('ArrowLeft')
page.keyboard.press('Tab')
page.keyboard.press('Escape')
  1. 鼠标操作
#获取元素位置
bounding_box = page.query_selector('button').bounding_box()
#精确鼠标控制
page.mouse.move(bounding_box['x'] + bounding_box['width']/2, 
                bounding_box['y'] + bounding_box['height']/2)
page.mouse.click(bounding_box['x'] + bounding_box['width']/2, 
                 bounding_box['y'] + bounding_box['height']/2)
#鼠标拖拽
page.mouse.down()
page.mouse.move(100, 100)
page.mouse.up()

页面与框架管理

  1. 多页面处理
# 获取所有页面
all_pages = context.pages
# 创建新页面
new_page = context.new_page()
# 等待新页面
with context.expect_page() as new_page_info:
    page.click('a[target="_blank"]')
new_page = new_page_info.value
# 切换到特定页面
context.pages[1].bring_to_front()
  1. 框架处理
# 获取框架
frame = page.frame(name='frame-name')
frame = page.frame(url=r'.*login.*')
# 框架内操作
frame.click('button.submit')
frame.fill('input#username', 'user')
# 遍历所有框架
for frame in page.frames:
    print(frame.name)

页面上下文

# 执行JavaScript
page.evaluate('document.title')
result = page.evaluate('([x, y]) => x + y', [1, 2])

# 注入JavaScript
page.add_script_tag(url='https://code.jquery.com/jquery.js')
page.add_script_tag(content='console.log("Hello");')

# 注入样式
page.add_style_tag(url='https://cdn.example.com/style.css')
page.add_style_tag(content='body { background: red; }')

# 设置页面内容
page.set_content('<html><body><h1>Hello</h1></body></html>')

网络请求处理

  1. 请求拦截与修改
# 路由拦截
def handle_route(route):
    if 'api.example.com' in route.request.url:
        route.fulfill(
            status=200,
            content_type='application/json',
            body='{"success": true}'
        )
    else:
        route.continue_()
page.route('**/*', handle_route)

# 特定模式拦截
page.route('**/*.jpg', lambda route: route.abort())
page.route('**/api/*', handle_route)

# 修改请求头
page.route('**/*', lambda route: route.continue_(headers={**route.request.headers, 'x-custom': 'value'}))
  1. 请求监控
# 请求事件处理
def log_request(request):
    print(f"Request: {request.method} {request.url}")
def log_response(response):
    print(f"Response: {response.status} {response.url}")
page.on('request', log_request)
page.on('response', log_response)

# 等待请求完成
with page.expect_response('https://example.com/api/data') as response_info:
    page.click('button#load-data')
response = response_info.value

# 等待多个请求
with page.expect_response(lambda response: response.url.startswith('https://api')) as response_info:
    page.click('button#fetch')
  1. API请求直接调用
# 使用Playwright发起API请求
response = page.request.get('https://api.example.com/data')
print(response.json())

# 带参数的请求
response = page.request.post(
    'https://api.example.com/login',
    data={'username': 'user', 'password': 'pass'},
    headers={'Content-Type': 'application/json'}
)

断言

  1. 内置断言
# 页面标题断言
assert page.title() == 'Expected Title'
# URL断言
assert page.url == 'https://example.com'
# 元素可见性断言
assert page.is_visible('div.success')
# 文本内容断言
assert 'Welcome' in page.text_content('body')
  1. 等待断言
# 等待元素出现并断言
page.wait_for_selector('div.success', state='visible')
assert page.text_content('div.success') == 'Operation successful'
# 等待文本出现
page.wait_for_selector('text=Welcome back')
# 等待函数条件
page.wait_for_function('() => document.querySelectorAll(".item").length > 5')

截图与PDF生成

  1. 截图功能
# 页面截图
page.screenshot(path='screenshot.png')
# 元素截图
page.locator('div.chart').screenshot(path='chart.png')
# 截图选项
page.screenshot(
    path='fullpage.png',
    full_page=True,           # 截取完整页面
    type='jpeg',              # 图片类型:'png'|'jpeg'
    quality=80,               # JPEG质量(0-100)
    omit_background=True,     # 隐藏默认背景
    timeout=10000             # 超时时间
)
  1. PDF生成
# 生成PDF
page.pdf(
    path='page.pdf',
    scale=0.8,                # 缩放比例
    format='A4',              # 纸张格式
    margin={'top': '1cm', 'bottom': '1cm'},  # 边距
    print_background=True,    # 打印背景
    landscape=False           # 横向/纵向
)

测试框架集成
与pytest集成

# conftest.py
import pytest
from playwright.sync_api import sync_playwright
@pytest.fixture(scope='session')
def browser():
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)
        yield browser
        browser.close()
@pytest.fixture
def page(browser):
    context = browser.new_context()
    page = context.new_page()
    yield page
    context.close()
# test_example.py
def test_login(page):
    page.goto('https://example.com/login')
    page.fill('input#username', 'testuser')
    page.fill('input#password', 'password')
    page.click('button#submit')
    assert page.url == 'https://example.com/dashboard'

测试最佳实践

# 页面对象模型
class LoginPage:
    def __init__(self, page):
        self.page = page
        self.username_input = page.locator('input#username')
        self.password_input = page.locator('input#password')
        self.submit_button = page.locator('button#submit')
    def navigate(self):
        self.page.goto('https://example.com/login')
    def login(self, username, password):
        self.username_input.fill(username)
        self.password_input.fill(password)
        self.submit_button.click()
# 使用页面对象
def test_login(page):
    login_page = LoginPage(page)
    login_page.navigate()
    login_page.login('testuser', 'password')
    assert page.url == 'https://example.com/dashboard'

调试

# 启用调试模式
browser = p.chromium.launch(headless=False, devtools=True)
# 暂停执行
page.pause()
# 慢速模式
browser = p.chromium.launch(headless=False, slow_mo=1000)
# 记录控制台输出
page.on('console', lambda msg: print(f'CONSOLE: {msg.text}'))
# 记录页面错误
page.on('pageerror', lambda err: print(f'PAGE ERROR: {err}'))
# 记录请求失败
page.on('requestfailed', lambda req: print(f'REQUEST FAILED: {req.url} {req.failure}'))

# 高亮匹配元素
page.locator('button').highlight()
# 评估选择器
count = page.locator('button').count()
print(f'Found {count} buttons')
# 调试选择器
try:
    page.wait_for_selector('button:visible', timeout=5000)
except:
    print('Button not found within 5 seconds')

性能优化

  1. 浏览器上下文复用
# 复用浏览器上下文
context = browser.new_context()
# 在不同测试中复用
def test_1(context):
    page = context.new_page()
    # 测试代码...
def test_2(context):
    page = context.new_page()
    # 测试代码...
# 清理上下文
context.clear_cookies()
context.clear_permissions()
  1. 并行执行
# 使用多线程并行测试
import concurrent.futures
def run_test(test_func, browser_type):
    with sync_playwright() as p:
        browser = getattr(p, browser_type).launch()
        context = browser.new_context()
        page = context.new_page()
        test_func(page)
        browser.close()
with concurrent.futures.ThreadPoolExecutor() as executor:
    futures = []
    for _ in range(3):
        futures.append(executor.submit(run_test, test_function, 'chromium'))
    for future in concurrent.futures.as_completed(futures):
        future.result()

Playwright MCP

使用 Playwright 提供浏览器自动化功能的 Model Context Protocol (MCP) 服务器。该服务器使 LLM 能够通过结构化的无障碍快照与网页进行交互,从而绕过了对屏幕截图或视觉调优模型的需求。

主要特性

  1. 快速且轻量:使用 Playwright 的无障碍树,而不是基于像素的输入;
  2. LLM 友好:无需视觉模型,纯粹基于结构化数据操作;
  3. 确定性的工具应用:避免了基于屏幕截图方法常见的歧义。

使用场景

  1. 网页导航和表单填写;
  2. 从结构化内容中提取数据;
  3. 由 LLM 驱动的自动化测试;
  4. 适用于代理的一般用途浏览器交互。

在ClaudeCode中安装

在命令行中运行以下命令:

#切换到你的项目目录
cd your-project-folder

#添加Playwright MCP服务器
claude mcp add playwright -- cmd /c npx @playwright/mcp@latest

#安装浏览器(Chromium、Firefox等)
npx playwright install

#验证安装
claude mcp list #playwright显示为"Connected"状态

使用Playwright MCP

安装完成后,在Claude Code会话中就可以直接用自然语言控制浏览器了:

“使用playwright mcp 打开 https://passport.baidu.com/v2/?login
使用用户名登录,在用户名字段输入13800000000,在密码字段输入123,点击登录按钮
等待登录信息出现后截屏,保存至.\screenshots”

Playwright MCP 完整工具集

  1. 输入自动化(8个工具)
工具名描述参数
browser_click在网页上执行点击操作element-元素描述, ref-元素引用
browser_drag在两个元素之间执行拖放操作startElement-源元素描述, startRef-源元素引用, endElement-目标元素描述, endRef-目标元素引用
browser_type在可编辑元素中输入文本element-元素描述, ref-元素引用, text-输入文本, submit-是否按回车提交
browser_hover悬停在页面元素上element-元素描述, ref-元素引用
browser_select_option在下拉菜单中选择选项element-元素描述, ref-元素引用, values-要选择的值数组
browser_choose_file选择一个或多个文件进行上传paths-要上传文件的绝对路径数组
browser_press_key在键盘上按下一个键key-键名或字符,如ArrowLeft或a
browser_save_as_pdf将页面保存为PDF无
  1. 导航自动化(4个工具)
工具名描述参数
browser_navigate导航到指定的URLurl-要导航到的URL地址
browser_go_back返回上一页无
browser_go_forward前进到下一页无
browser_close关闭当前页面无
  1. 调试与捕获(3个工具)
工具名描述参数
browser_snapshot捕获当前页面的可访问性快照(优于截图)无
browser_take_screenshot捕获页面的屏幕截图raw-(可选)返回无损PNG格式截图,默认为JPEG
browser_wait等待指定的时间(秒)time-等待时间,上限10秒

Chrome DevTools MCP

充当MCP服务器,让 AI 能够访问 Chrome DevTools 的全部功能,以实现可靠的自动化、深入调试和性能分析。

主要特性

  1. 获取性能洞察:使用 Chrome DevTools 记录跟踪并提取可操作的性能洞察;
  2. 高级浏览器调试:分析网络请求、截屏并检查浏览器控制台;
  3. 可靠的自动化:使用 puppeteer 在 Chrome 中自动执行操作,并自动等待操作结果。

在ClaudeCode中安装

在命令行中运行以下命令:

#切换到你的项目目录
cd your-project-folder

#添加chrome-devtools服务器
claude mcp add chrome-devtools -- cmd /c npx chrome-devtools-mcp@latest

#验证安装
claude mcp list   #chrome-devtools应显示为"Connected"状态

使用Chrome DevTools MCP

安装完成后,在Claude Code会话中用自然语言控制浏览器:

“使用chrome-devtools打开当前浏览器分析性能”

Chrome DevTools MCP 完整工具集

  1. 输入自动化(7个工具)
工具名描述参数
click点击页面元素element-元素描述, ref-元素引用
drag拖放操作startElement-起始元素描述, startRef-起始元素引用, endElement-目标元素描述, endRef-目标元素引用
fill填充输入框element-元素描述, ref-元素引用, text-输入文本
fill_form一键填写整个表单formData-包含字段名和值的对象
handle_dialog处理弹窗(alert/confirm/prompt)action-操作类型(accept/dismiss), promptText-(可选)提示框输入文本
hover悬停在元素上element-元素描述, ref-元素引用
upload_file上传文件element-上传元素描述, ref-元素引用, paths-文件路径数组
  1. 导航自动化(7个工具)
工具名描述参数
close_page关闭当前页面无
list_pages列出所有打开的页面无
navigate_page导航到指定URLurl-目标网址, waitUntil-等待条件(load/domcontentloaded/networkidle)
navigate_page_history历史导航direction-方向(back/forward)
new_page创建新页面context-(可选)浏览器上下文
select_page切换到指定页面pageId-页面ID
wait_for等待条件满足condition-等待条件(load/selector/function), timeout-超时时间
  1. 模拟(3个工具)
工具名描述参数
emulate_cpu模拟CPU性能(节流)throttlingRate-CPU节流倍率,例如4表示4倍慢速
emulate_network模拟网络条件networkCondition-网络条件(offline/slow3G/fast3G/wifi等), latency-自定义延迟(ms), downloadThroughput-自定义下载速度, uploadThroughput-自定义上传速度
resize_page调整页面视口大小width-视口宽度(px), height-视口高度(px)
  1. 性能(3个工具)
工具名描述参数
performance_analyze_insight分析性能洞察traceData-性能追踪数据, insightType-洞察类型(loadTime/renderBlocking/layoutShift等)
performance_start_trace开始性能追踪categories-要追踪的类别数组,例如["loading","scripting","rendering"]
performance_stop_trace停止性能追踪并获取结果无
  1. 网络(2个工具)
工具名描述参数
get_network_request获取特定网络请求的详细信息requestId-请求ID
list_network_requests列出所有网络请求filter-(可选)过滤条件,例如*.js、api/*等
  1. 调试(4个工具)
工具名描述参数
evaluate_script在页面上下文中执行JavaScript脚本script-要执行的JavaScript代码, returnByValue-是否返回值(true)或返回对象引用(false)
list_console_messages列出控制台消息clear-是否在获取后清除控制台消息
take_screenshot截取页面截图format-图片格式(png/jpeg), quality-(仅jpeg)图片质量0-100, fullPage-是否截取整个页面(包括滚动部分)
take_snapshot捕获页面的DOM快照(包含可访问性信息)includeDom-是否包含DOM结构, includeAccessibility-是否包含可访问性信息

Playwright MCP vs Chrome DevTools MCP:

一、核心差异对比

对比维度Playwright MCPChrome DevTools MCP
底层协议Playwright自动化库Chrome DevTools Protocol (CDP)
浏览器实例启动全新的浏览器实例连接已有的Chrome浏览器
适用场景UI自动化测试、E2E测试、表单填写调试现有页面、性能分析、网络监控
核心优势自动化能力强、跨浏览器支持调试功能丰富、性能分析深入
启动方式每次新建独立会话可复用现有浏览器
学习曲线平缓,面向自动化中等,面向调试场景

二、工具集功能对比

  1. 输入自动化能力
功能Playwright MCPChrome DevTools MCP
表单填写❌ 需要手动组合✅ fill_form(一键填表)
处理弹窗❌✅ handle_dialog
键盘按键✅ browser_press_key❌
  1. 导航与页面管理
功能Playwright MCPChrome DevTools MCP
列出所有页面❌✅ list_pages
切换页面❌✅ select_page
新建页面❌✅ new_page
  1. 调试与诊断能力
功能Playwright MCPChrome DevTools MCP
页面快照✅ browser_snapshot(可访问性树)✅ take_snapshot(DOM+可访问性)
截图✅ browser_take_screenshot✅ take_screenshot(更多格式选项)
保存为PDF✅ browser_save_as_pdf❌
执行JS脚本❌✅ evaluate_script
查看控制台❌✅ list_console_messages
性能追踪❌✅ performance_start/stop_trace
性能分析❌✅ performance_analyze_insight
网络请求监控❌✅ list_network_requests
网络详情获取❌✅ get_network_request
  1. 模拟与仿真能力
功能Playwright MCPChrome DevTools MCP
CPU节流❌✅ emulate_cpu
网络模拟❌✅ emulate_network
视口调整❌(需启动时配置)✅ resize_page

Playwright MCP 是自动化测试的首选,它提供了简洁的API、稳定的执行环境和跨浏览器支持,适合构建可靠的测试套件。

Chrome DevTools MCP 是调试和诊断的利器,它深入浏览器内部,提供性能分析、网络监控和实时调试能力,适合开发和问题排查。

结合使用:

  1. 用Playwright构建自动化测试流水线 :编写稳定的E2E测试、表单填写、用户行为模拟;
  2. 用DevTools深入分析问题和优化性能 :当测试失败时,用DevTools调试;当需要优化时,用DevTools分析;
  3. 在关键场景中互相验证和补充 :用Playwright模拟用户操作,用DevTools验证页面状态和性能指标。

@playwright/cli

微软官方推出的专为AI编码代理设计的命令行工具,于2026年初发布。它与传统的 npx playwright test 完全不同,是一个专门面向AI代理(如Claude Code、GitHub Copilot、Cursor等)的浏览器自动化接口。

核心理念:

极致的token效率,与MCP将完整的可访问性树和截图数据推送到AI上下文不同,CLI将所有数据保存在磁盘上,AI代理只接收简洁的命令输出和文件路径,按需读取所需信息。

MCP VS CLI:

对比项MCP方式CLI方式
页面快照返回完整可访问性树(数千token)保存为YAML文件,返回文件路径
截图返回Base64图像(数万token)保存为PNG文件,返回文件路径
单会话token消耗~114,000 tokens~27,000 tokens(约4倍节省)
长会话表现上下文迅速填满,性能下降上下文始终保持轻量

安装

  1. 安装
#全局安装
npm install -g @playwright/cli@latest

#验证安装
playwright-cli --version
#在工作区初始化
cd your-project-folder
playwright-cli install  #在当前工作区创建 .playwright-cli/ 目录,用于存放浏览器会话和配置文件。
  1. 安装浏览器
#如果系统没有Chrome,可以安装独立浏览器
playwright-cli install-browser

#安装特定浏览器
playwright-cli install-browser --browser=firefox
  1. 安装技能(让AI学习CLI)
#为Claude Code安装技能文档
playwright-cli install --skills  #复制 SKILL.md 文件到工作区,AI代理可以通过阅读该文件学习所有可用命令。

@playwright/cli 提供了超过50个命令,远多于MCP默认启用的工具集,如需了解工具集可查询帮助文档查看,也可以询问ClaudeCode,梳理大致使用场景

使用playwright-cli

“使用playwright-cli打开https://passport.baidu.com/v2/?login,测试登录功能,使用有头模式让我看见”

核心优势:

  1. 极致token效率:比MCP节省4-10倍的token消耗;
  2. 命令集更丰富:50+命令 vs MCP的~20个工具;
  3. 确定性控制:元素引用ID(如 e21)确保精准交互;
  4. 多会话支持:并行管理多个独立浏览器实例;
  5. 可视化监控:playwright-cli show 实时观察AI操作。

与 Playwright MCP 的选型指南:

选择条件推荐 @playwright/cli推荐 Playwright MCP
代理类型编码代理(Claude Code、Copilot)通用聊天代理(Claude Desktop)
环境有文件系统和shell访问权限沙箱环境,无文件系统访问
会话长度长会话(10+步骤)短会话(1-3步)
token预算有限,需优化充足,接受高消耗
确定性要求高,需精确控制中等,允许探索性
集成场景与代码库、测试框架协同独立聊天工具

微软官方的建议:

如果你的任务涉及编码、测试,且使用编码代理——使用 CLI。如果你正在构建需要 MCP 协议标准的自主代理工作流——使用 MCP。

总结:

  1. MCP vs CLI 的本质区别:不是功能多少,而是数据传递方式。MCP推送完整数据(适合短会话),CLI保存文件+路径(适合长会话)。
  2. token效率是AI时代的关键:@playwright/cli的4-10倍token节省。
  3. 生态互补而非替代:传统库、MCP服务器、CLI工具三者各司其职,共同构成完整的Playwright生态系统。
  4. 未来趋势:随着AI编码代理的普及,@playwright/cli这类面向AI设计的工具将逐渐成为主流。
Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐