别再手动抓包了!AI 时代的前端调试神器——chrome-devtools-mcp 完全评测

别再手动抓包了!AI 时代的前端调试神器——chrome-devtools-mcp 完全评测

别再手动抓包了!AI 时代的前端调试神器——chrome-devtools-mcp 完全评测

从痛点到解法,一文搞懂如何让 AI 助手直接操控 Chrome 浏览器


为什么这个项目值得关注

在前端开发和调试工作中,你是否经常遇到以下痛点:

  • 想让 AI 助手帮你分析网页结构,却只能手动截图或复制 HTML
  • 自动化测试脚本写了几百行,却搞不定复杂的 JavaScript 交互
  • 需要批量操作浏览器,却找不到趁手的工具
  • 想让 AI 直接帮你调试页面,却不知道怎么让 AI “看见”浏览器

这些问题困扰了无数开发者多年。而 chrome-devtools-mcp 项目的出现,彻底改变了这一局面。

项目核心价值

chrome-devtools-mcp 是一个基于 Model Context Protocol(MCP)的 Chrome DevTools 服务器实现。它的核心思想简单而强大:让 AI 助手能够直接与 Chrome 浏览器交互

传统方式:开发者 → 手动操作浏览器 → 观察结果 → 修改代码 → 循环
AI 时代:开发者 → AI 助手操控浏览器 → AI 观察结果 → AI 修复问题

这个项目由 ChromeDevTools 团队维护,提供了标准化的 MCP 接口,让任何支持 MCP 协议的 AI 客户端(如 Claude Desktop、Cursor 等)都能直接操控 Chrome 浏览器。

为什么选择 chrome-devtools-mcp

特性 说明
标准化协议 基于 MCP 协议,支持主流 AI 助手
功能完整 覆盖控制台、网络、性能、DOM 操作等核心功能
易于集成 零配置启动,一条命令即可运行
开源免费 MIT 许可证,完全开源
社区活跃 持续更新,文档完善

环境搭建

前置要求

在开始之前,请确保你的开发环境满足以下要求:

Python 环境

  • Python 版本:3.10 或更高
  • pip 包管理器

Chrome 浏览器

  • Chrome 浏览器 89 或更高版本
  • 开启远程调试端口

操作系统支持

  • macOS、Linux、Windows 均支持

详细安装步骤

第一步:检查 Python 环境

打开终端,输入以下命令检查 Python 版本:

python3 --version
# 预期输出:Python 3.10.x 或更高版本

# 如果版本过低,需要升级 Python
# macOS 用户可以使用 Homebrew:
brew install python@3.11

第二步:安装项目依赖

我们使用 pip 安装 chrome-devtools-mcp:

pip install chrome-devtools-mcp

安装完成后,验证安装是否成功:

pip show chrome-devtools-mcp
# 应该看到类似输出:
# Name: chrome-devtools-mcp
# Version: x.x.x
# Summary: MCP server for Chrome DevTools

第三步:启动带调试端口的 Chrome

这是最关键的一步。Chrome 必须以特殊模式启动,才能被外部程序控制。

macOS 用户:

# 关闭所有 Chrome 窗口,然后执行:
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
    --remote-debugging-port=9222 \
    --user-data-dir=/tmp/chrome-debug

Linux 用户:

google-chrome \
    --remote-debugging-port=9222 \
    --user-data-dir=/tmp/chrome-debug

Windows 用户:

"C:\Program Files\Google\Chrome\Application\chrome.exe" `
    --remote-debugging-port=9222 `
    --user-data-dir="C:\temp\chrome-debug"

提示:–user-data-dir 指定了用户数据目录,使用临时目录可以避免与正常使用的 Chrome 配置文件冲突。

第四步:验证 Chrome 调试模式

打开浏览器访问以下地址,确认 Chrome 已开启调试模式:

open http://localhost:9222/json

你应该能看到类似以下的 JSON 输出:

[
  {
    "id": "xxx",
    "type": "page",
    "title": "新标签页",
    "url": "chrome://newtab/",
    "webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/xxx"
  }
]

如果看到这个 JSON 响应,说明 Chrome 调试模式已成功开启!


核心功能详解

chrome-devtools-mcp 提供了丰富的浏览器控制功能。让我们逐一了解。

功能模块概览

chrome-devtools-mcp
├── 页面控制
   ├── 导航控制(打开、刷新、前进、后退)
   ├── 页面截图
   └── 页面信息获取
├── 网络控制
   ├── 网络请求监听
   └── 请求拦截
├── JavaScript 执行
   ├── 代码注入
   └── 控制台交互
├── DOM 操作
   ├── 元素查询
   ├── 属性修改
   └── 事件监听
└── 控制台交互
    ├── 日志读取
    └── 消息发送

1. 页面控制功能

页面控制是最基础的功能模块,让你能够自动化操作浏览器页面。

打开指定 URL:

# 导航到指定网页
await page.navigate("https://example.com")

刷新页面:

# 刷新当前页面
await page.reload()

获取页面截图:

# 获取完整页面截图
screenshot = await page.screenshot(full_page=True)

# 获取视口截图
screenshot = await page.screenshot(full_page=False)

2. JavaScript 执行功能

这是最强大的功能之一——你可以在页面上下文中直接执行任意 JavaScript 代码。

执行 JavaScript:

# 在页面中执行 JavaScript
result = await page.evaluate("""
    () => {
        // 获取页面标题
        return document.title;
    }
""")

操作 DOM 元素:

# 修改页面元素
await page.evaluate("""
    () => {
        const heading = document.querySelector('h1');
        if (heading) {
            heading.textContent = 'Modified by MCP!';
            heading.style.color = 'red';
        }
    }
""")

与页面交互:

# 点击按钮
await page.evaluate("""
    () => {
        const button = document.querySelector('#submit-btn');
        if (button) button.click();
    }
""")

# 填写表单
await page.evaluate("""
    () => {
        const input = document.querySelector('input[name="username"]');
        if (input) {
            input.value = 'testuser';
            input.dispatchEvent(new Event('input', { bubbles: true }));
        }
    }
""")

3. 网络监听功能

捕获和监控网络请求对于调试和分析非常重要。

监听网络请求:

# 开启网络监控
await network.enable()

# 监听请求完成事件
async def handle_request_finished(params):
    request = params.get('request')
    print(f"Request: {request['url']}")
    print(f"Method: {request['method']}")
    print(f"Status: {params.get('response', {}).get('status')}")

# 注册事件处理器
session.on('Network.requestFinished', handle_request_finished)

4. 控制台交互功能

读取控制台输出是调试 JavaScript 的重要手段。

监听控制台消息:

# 监听控制台日志
async def handle_console_message(params):
    message_type = params.get('type')
    message_text = params.get('args', [])

    if message_type == 'log':
        print(f"[Console Log] {message_text}")
    elif message_type == 'error':
        print(f"[Console Error] {message_text}")

# 注册控制台消息处理器
session.on('Runtime.consoleAPICalled', handle_console_message)

5. DOM 操作功能

通过 CDP 协议直接操作页面 DOM 元素。

查询元素:

# 使用 CSS 选择器查询元素
from devtools_puppeteer import Page

page = Page(ws_url)
await page.query_selector("div.container")

实战教程:从零构建自动化测试脚本

现在让我们通过一个完整的实战案例,学习如何使用 chrome-devtools-mcp 构建自动化测试脚本。

实战案例:自动化登录测试

我们将创建一个完整的自动化测试脚本,模拟用户登录网站的完整流程。

项目结构

login-automation/
├── main.py              # 主程序入口
├── config.py            # 配置文件
├── tests/
│   └── test_login.py    # 登录测试用例
├── reports/
│   └── screenshots/     # 截图保存目录
└── requirements.txt     # 依赖文件

第一步:创建配置文件

# config.py
"""
配置文件
包含测试所需的各项配置参数
"""

# Chrome 调试端口
CHROME_DEBUG_PORT = 9222

# 测试目标网站
TEST_URLS = {
    "login_page": "https://example.com/login",
    "dashboard": "https://example.com/dashboard",
}

# 测试账号
TEST_CREDENTIALS = {
    "username": "testuser@example.com",
    "password": "TestPass123!",
}

# 超时设置(秒)
TIMEOUT = {
    "page_load": 30,
    "element_wait": 10,
    "network_idle": 5,
}

# 截图保存路径
SCREENSHOT_DIR = "./reports/screenshots"

第二步:实现浏览器连接模块

# browser.py
"""
浏览器连接管理模块
处理 Chrome DevTools Protocol 的连接和初始化
"""

import asyncio
import json
from typing import Optional, Dict, Any
from urllib.parse import urljoin

try:
    import websockets
except ImportError:
    print("请安装 websockets 库:pip install websockets")
    raise


class ChromeConnection:
    """
    Chrome 连接管理类
    负责与 Chrome DevTools Protocol 通信
    """

    def __init__(self, debug_port: int = 9222):
        self.debug_port = debug_port
        self.ws_url: Optional[str] = None
        self.ws: Optional[websockets.WebSocketClientProtocol] = None
        self.message_id = 0
        self.pending_responses: Dict[int, asyncio.Future] = {}

    async def connect(self) -> None:
        """
        连接到 Chrome 调试实例
        """
        # 获取 WebSocket URL
        debug_url = f"http://localhost:{self.debug_port}/json"

        async with websockets.connect(debug_url) as ws:
            self.ws = ws
            self.ws_url = ws.url

            # 获取页面列表
            pages = await self.receive_message()
            print(f"发现 {len(pages)} 个浏览器标签页")

            # 选择第一个页面(或创建新页面)
            if pages:
                self.ws_url = pages[0].get('webSocketDebuggerUrl')
                print(f"已连接到页面:{pages[0].get('title')}")

    async def send_command(self, method: str, params: Optional[Dict] = None) -> Any:
        """
        发送 CDP 命令

        Args:
            method: CDP 方法名
            params: 命令参数

        Returns:
            命令响应结果
        """
        self.message_id += 1
        message = {
            "id": self.message_id,
            "method": method,
            "params": params or {}
        }

        await self.ws.send(json.dumps(message))
        response = await self.receive_response(self.message_id)

        return response

    async def receive_response(self, msg_id: int) -> Dict:
        """
        等待并接收指定 ID 的响应
        """
        while True:
            response = await self.receive_message()

            if isinstance(response, dict) and response.get('id') == msg_id:
                return response.get('result', {})

            # 处理事件通知
            if isinstance(response, dict) and 'method' in response:
                await self.handle_event(response)

    async def receive_message(self) -> Any:
        """
        接收 WebSocket 消息
        """
        raw_message = await self.ws.recv()
        return json.loads(raw_message)

    async def handle_event(self, event: Dict) -> None:
        """
        处理事件通知
        子类可以重写此方法处理特定事件
        """
        pass

    async def close(self) -> None:
        """
        关闭连接
        """
        if self.ws:
            await self.ws.close()

第三步:实现页面操作类

# page.py
"""
页面操作模块
封装常用的页面操作方法
"""

import base64
import asyncio
from typing import Optional, Callable, List, Dict, Any
from pathlib import Path


class Page:
    """
    页面操作类
    提供与页面交互的高级 API
    """

    def __init__(self, connection):
        self.connection = connection
        self.target_id: Optional[str] = None

    async def navigate(self, url: str) -> Dict:
        """
        导航到指定 URL

        Args:
            url: 目标网页地址

        Returns:
            导航结果
        """
        result = await self.connection.send_command(
            "Page.navigate",
            {"url": url}
        )

        # 等待页面加载完成
        await self.wait_for_load_state("load")

        return result

    async def reload(self, ignore_cache: bool = False) -> Dict:
        """
        刷新页面

        Args:
            ignore_cache: 是否忽略缓存

        Returns:
            刷新结果
        """
        return await self.connection.send_command(
            "Page.reload",
            {"ignoreCache": ignore_cache}
        )

    async def screenshot(self, path: Optional[str] = None, 
                        full_page: bool = True,
                        format: str = "png") -> Optional[bytes]:
        """
        页面截图

        Args:
            path: 保存路径,为空则返回图片数据
            full_page: 是否截取完整页面
            format: 图片格式 (png/jpeg)

        Returns:
            截图数据或保存路径
        """
        # 启用 Page 模块
        await self.connection.send_command("Page.enable")

        # 执行截图
        result = await self.connection.send_command(
            "Page.captureScreenshot",
            {
                "format": format,
                "captureBeyondViewport": full_page,
                "fromSurface": True
            }
        )

        # 解码图片
        if "data" in result:
            image_data = base64.b64decode(result["data"])

            if path:
                Path(path).parent.mkdir(parents=True, exist_ok=True)
                with open(path, "wb") as f:
                    f.write(image_data)
                print(f"截图已保存:{path}")
                return None
            else:
                return image_data

        return None

    async def evaluate(self, expression: str, 
                       await_promise: bool = False) -> Any:
        """
        在页面上下文中执行 JavaScript

        Args:
            expression: JavaScript 表达式或函数
            await_promise: 是否等待 Promise 解决

        Returns:
            执行结果
        """
        result = await self.connection.send_command(
            "Runtime.evaluate",
            {
                "expression": expression,
                "returnByValue": True,
                "awaitPromise": await_promise
            }
        )

        if result.get("exceptionDetails"):
            print(f"JavaScript 执行错误:{result['exceptionDetails']}")
            return None

        return result.get("result", {}).get("value")

    async def query_selector(self, selector: str) -> Optional[Dict]:
        """
        查询单个元素

        Args:
            selector: CSS 选择器

        Returns:
            元素信息
        """
        result = await self.evaluate(f"""
            () => {{
                const element = document.querySelector('{selector}');
                if (!element) return null;

                return {{
                    tagName: element.tagName,
                    textContent: element.textContent?.trim(),
                    innerHTML: element.innerHTML,
                    attributes: Array.from(element.attributes).reduce(
                        (acc, attr) => {{
                            acc[attr.name] = attr.value;
                            return acc;
                        }}, {{}}
                    ),
                    boundingBox: element.getBoundingClientRect()
                }};
            }}
        """)

        return result

    async def fill_input(self, selector: str, value: str) -> bool:
        """
        填写输入框

        Args:
            selector: 输入框选择器
            value: 填写的值

        Returns:
            是否成功
        """
        script = f"""
            () => {{
                const element = document.querySelector('{selector}');
                if (!element) return false;

                // 聚焦元素
                element.focus();

                // 清除现有值
                element.value = '';

                // 设置新值
                const nativeInputValueSetter = 
                    Object.getOwnPropertyDescriptor(
                        window.HTMLInputElement.prototype, 
                        'value'
                    ).set;
                nativeInputValueSetter.call(element, '{value}');

                // 触发输入事件
                element.dispatchEvent(new Event('input', {{ bubbles: true }}));
                element.dispatchEvent(new Event('change', {{ bubbles: true }}));

                return true;
            }}
        """

        return await self.evaluate(script) or False

    async def click(self, selector: str) -> bool:
        """
        点击元素

        Args:
            selector: 元素选择器

        Returns:
            是否成功
        """
        script = f"""
            () => {{
                const element = document.querySelector('{selector}');
                if (!element) return false;

                // 创建并触发点击事件
                const event = new MouseEvent('click', {{
                    view: window,
                    bubbles: true,
                    cancelable: true
                }});
                element.dispatchEvent(event);

                return true;
            }}
        """

        return await self.evaluate(script) or False

    async def wait_for_selector(self, selector: str, timeout: int = 10000) -> bool:
        """
        等待元素出现

        Args:
            selector: CSS 选择器
            timeout: 超时时间(毫秒)

        Returns:
            元素是否出现
        """
        start_time = asyncio.get_event_loop().time()

        while (asyncio.get_event_loop().time() - start_time) * 1000 < timeout:
            exists = await self.evaluate(f"""
                () => document.querySelector('{selector}') !== null
            """)

            if exists:
                return True

            await asyncio.sleep(0.1)

        return False

    async def wait_for_load_state(self, state: str = "load", timeout: int = 30000) -> None:
        """
        等待页面加载状态

        Args:
            state: 加载状态 (load/domcontentloaded/networkidle)
            timeout: 超时时间(毫秒)
        """
        await self.connection.send_command("Page.setLifecycleEventsEnabled", {"enabled": True})

        event_received = asyncio.Event()

        async def handler(params):
            if params.get("name") == state:
                event_received.set()

        # 临时保存事件处理器
        original_handler = self.connection.handle_event
        self.connection.handle_event = handler

        try:
            await asyncio.wait_for(event_received.wait(), timeout=timeout/1000)
        except asyncio.TimeoutError:
            print(f"等待 {state} 状态超时")
        finally:
            self.connection.handle_event = original_handler

    async def get_console_messages(self) -> List[Dict]:
        """
        获取控制台消息

        Returns:
            控制台消息列表
        """
        result = await self.connection.send_command(
            "Runtime.getConsoleMessages",
            {}
        )

        return result.get("messages", [])

    async def clear_console(self) -> None:
        """
        清空控制台
        """
        await self.connection.send_command("Runtime.discardConsoleEntries", {})

第四步:编写测试用例

# tests/test_login.py
"""
登录功能测试用例
演示如何使用 chrome-devtools-mcp 进行自动化测试
"""

import asyncio
import sys
from pathlib import Path

# 添加项目根目录到路径
sys.path.insert(0, str(Path(__file__).parent.parent))

from browser import ChromeConnection
from page import Page
from config import (
    TEST_URLS, 
    TEST_CREDENTIALS, 
    TIMEOUT,
    SCREENSHOT_DIR
)


class LoginAutomation:
    """
    登录自动化测试类
    """

    def __init__(self):
        self.connection: ChromeConnection = None
        self.page: Page = None
        self.test_results = []

    async def setup(self):
        """
        测试初始化
        """
        print("=" * 50)
        print("初始化浏览器连接...")
        print("=" * 50)

        # 创建连接
        self.connection = ChromeConnection(debug_port=9222)
        await self.connection.connect()

        # 创建页面对象
        self.page = Page(self.connection)

        print("浏览器连接成功!\n")

    async def teardown(self):
        """
        测试清理
        """
        if self.connection:
            await self.connection.close()
            print("\n浏览器连接已关闭")

    async def take_screenshot(self, name: str) -> str:
        """
        截取屏幕截图

        Args:
            name: 截图文件名

        Returns:
            截图保存路径
        """
        # 确保目录存在
        Path(SCREENSHOT_DIR).mkdir(parents=True, exist_ok=True)

        # 截取页面
        filename = f"{SCREENSHOT_DIR}/{name}.png"
        await self.page.screenshot(filename, full_page=True)

        return filename

    async def test_open_login_page(self) -> bool:
        """
        测试用例 1:打开登录页面
        """
        print("\n[测试 1] 打开登录页面")
        print("-" * 30)

        try:
            # 导航到登录页面
            await self.page.navigate(TEST_URLS["login_page"])

            # 等待页面加载
            await asyncio.sleep(2)

            # 截图保存
            await self.take_screenshot("01_login_page_loaded")

            # 验证页面标题
            title = await self.page.evaluate("() => document.title")
            print(f"页面标题:{title}")

            self.test_results.append({
                "name": "打开登录页面",
                "status": "PASS",
                "message": f"成功加载页面,标题:{title}"
            })

            return True

        except Exception as e:
            self.test_results.append({
                "name": "打开登录页面",
                "status": "FAIL",
                "message": str(e)
            })
            print(f"错误:{e}")
            return False

    async def test_fill_login_form(self) -> bool:
        """
        测试用例 2:填写登录表单
        """
        print("\n[测试 2] 填写登录表单")
        print("-" * 30)

        try:
            # 填写用户名
            username_filled = await self.page.fill_input(
                "input[name='username'], input[type='email'], input[id='username']",
                TEST_CREDENTIALS["username"]
            )

            if username_filled:
                print(f"✓ 用户名已填写:{TEST_CREDENTIALS['username']}")
            else:
                print("✗ 用户名输入失败,尝试备用选择器...")
                # 备用方案:直接通过 JavaScript 填写
                await self.page.evaluate(f"""
                    () => {{
                        const inputs = document.querySelectorAll('input');
                        for (const input of inputs) {{
                            if (input.type !== 'password') {{
                                input.value = '{TEST_CREDENTIALS["username"]}';
                                input.dispatchEvent(new Event('input', {{ bubbles: true }}));
                                break;
                            }}
                        }}
                    }}
                """)

            # 填写密码
            await asyncio.sleep(0.5)
            await self.page.evaluate(f"""
                () => {{
                    const inputs = document.querySelectorAll('input[type="password"]');
                    if (inputs.length > 0) {{
                        inputs[0].value = '{TEST_CREDENTIALS["password"]}';
                        inputs[0].dispatchEvent(new Event('input', {{ bubbles: true }}));
                    }}
                }}
            """)
            print("✓ 密码已填写")

            # 截图保存
            await self.take_screenshot("02_form_filled")

            self.test_results.append({
                "name": "填写登录表单",
                "status": "PASS",
                "message": "表单已成功填写"
            })

            return True

        except Exception as e:
            self.test_results.append({
                "name": "填写登录表单",
                "status": "FAIL",
                "message": str(e)
            })
            print(f"错误:{e}")
            return False

    async def test_submit_login(self) -> bool:
        """
        测试用例 3:提交登录表单
        """
        print("\n[测试 3] 提交登录表单")
        print("-" * 30)

        try:
            # 查找并点击登录按钮
            clicked = await self.page.evaluate("""
                () => {
                    // 尝试多种选择器
                    const selectors = [
                        'button[type="submit"]',
                        'button:contains("登录")',
                        'button:contains("登录")',
                        'input[type="submit"]',
                        '.login-btn',
                        '#login-btn',
                        '[data-action="login"]'
                    ];

                    for (const selector of selectors) {
                        try {
                            const btn = document.querySelector(selector);
                            if (btn) {
                                btn.click();
                                return true;
                            }
                        } catch (e) {
                            continue;
                        }
                    }
                    return false;
                }
            """)

            if clicked:
                print("✓ 登录按钮已点击")
            else:
                print("✗ 未找到登录按钮,尝试按下 Enter 键...")
                await self.page.evaluate("""
                    () => {
                        const form = document.querySelector('form');
                        if (form) {
                            form.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));
                        }
                    }
                """)

            # 等待页面响应
            await asyncio.sleep(3)

            # 截图保存
            await self.take_screenshot("03_after_login_submit")

            # 验证登录结果
            current_url = await self.page.evaluate("() => window.location.href")
            print(f"当前 URL:{current_url}")

            # 检查是否跳转到仪表盘
            if "dashboard" in current_url.lower() or "welcome" in current_url.lower():
                self.test_results.append({
                    "name": "提交登录表单",
                    "status": "PASS",
                    "message": "登录成功,已跳转到仪表盘"
                })
                return True
            else:
                self.test_results.append({
                    "name": "提交登录表单",
                    "status": "PARTIAL",
                    "message": f"表单已提交,当前 URL:{current_url}"
                })
                return True

        except Exception as e:
            self.test_results.append({
                "name": "提交登录表单",
                "status": "FAIL",
                "message": str(e)
            })
            print(f"错误:{e}")
            return False

    async def test_verify_dashboard(self) -> bool:
        """
        测试用例 4:验证仪表盘页面
        """
        print("\n[测试 4] 验证仪表盘页面")
        print("-" * 30)

        try:
            # 获取页面信息
            page_info = await self.page.evaluate("""
                () => {
                    return {
                        title: document.title,
                        url: window.location.href,
                        hasContent: document.body.children.length > 0,
                        mainContent: document.querySelector('main, .content, #content')?.textContent?.trim()?.substring(0, 200)
                    };
                }
            """)

            print(f"页面标题:{page_info.get('title')}")
            print(f"页面 URL:{page_info.get('url')}")
            print(f"主要内容:{page_info.get('mainContent', '无')}...")

            # 截图保存
            await self.take_screenshot("04_dashboard_verified")

            self.test_results.append({
                "name": "验证仪表盘",
                "status": "PASS",
                "message": f"成功进入仪表盘页面"
            })

            return True

        except Exception as e:
            self.test_results.append({
                "name": "验证仪表盘",
                "status": "FAIL",
                "message": str(e)
            })
            print(f"错误:{e}")
            return False

    async def test_console_output(self) -> bool:
        """
        测试用例 5:检查控制台输出
        """
        print("\n[测试 5] 检查控制台输出")
        print("-" * 30)

        try:
            # 获取控制台消息
            messages = await self.page.get_console_messages()

            print(f"共捕获 {len(messages)} 条控制台消息:\n")

            for msg in messages[:10]:  # 只显示前 10 条
                msg_type = msg.get('type', 'log')
                msg_text = msg.get('parameters', [])

                if msg_text and len(msg_text) > 0:
                    text = msg_text[0].get('value', str(msg_text))
                    print(f"  [{msg_type.upper()}] {text}")

            self.test_results.append({
                "name": "控制台检查",
                "status": "PASS",
                "message": f"捕获 {len(messages)} 条消息"
            })

            return True

        except Exception as e:
            self.test_results.append({
                "name": "控制台检查",
                "status": "FAIL",
                "message": str(e)
            })
            print(f"错误:{e}")
            return False

    def print_test_summary(self):
        """
        打印测试摘要
        """
        print("\n" + "=" * 50)
        print("测试摘要")
        print("=" * 50)

        passed = sum(1 for r in self.test_results if r["status"] == "PASS")
        failed = sum(1 for r in self.test_results if r["status"] == "FAIL")
        partial = sum(1 for r in self.test_results if r["status"] == "PARTIAL")

        print(f"\n总计:{len(self.test_results)} 个测试")
        print(f"通过:{passed}")
        print(f"失败:{failed}")
        print(f"部分通过:{partial}")

        print("\n详细结果:")
        print("-" * 50)

        for result in self.test_results:
            status_icon = "✓" if result["status"] == "PASS" else ("△" if result["status"] == "PARTIAL" else "✗")
            print(f"{status_icon} {result['name']}: {result['message']}")

        print("\n" + "=" * 50)

    async def run(self):
        """
        运行所有测试
        """
        try:
            # 初始化
            await self.setup()

            # 运行测试用例
            await self.test_open_login_page()
            await self.test_fill_login_form()
            await self.test_submit_login()
            await self.test_verify_dashboard()
            await self.test_console_output()

            # 打印摘要
            self.print_test_summary()

        finally:
            # 清理
            await self.teardown()


async def main():
    """
    主函数入口
    """
    print("\n" + "=" * 50)
    print("chrome-devtools-mcp 自动化测试")
    print("=" * 50 + "\n")

    # 创建并运行测试
    automation = LoginAutomation()
    await automation.run()


if __name__ == "__main__":
    asyncio.run(main())

第五步:运行测试

保存所有文件后,运行测试脚本:

# 确保 Chrome 调试模式已启动
# 然后运行测试脚本
python tests/test_login.py

预期输出:

==================================================
chrome-devtools-mcp 自动化测试
==================================================

==================================================
初始化浏览器连接...
==================================================
浏览器连接成功!

[测试 1] 打开登录页面
------------------------------
页面标题:登录
截图已保存:./reports/screenshots/01_login_page_loaded.png
 用户名已填写:testuser@example.com

[测试 2] 填写登录表单
------------------------------
 密码已填写
截图已保存:./reports/screenshots/02_form_filled.png

[测试 3] 提交登录表单
------------------------------
 登录按钮已点击
当前 URLhttps://example.com/dashboard
截图已保存:./reports/screenshots/03_after_login_submit.png

[测试 4] 验证仪表盘页面
------------------------------
页面标题:仪表盘
页面 URLhttps://example.com/dashboard
截图已保存:./reports/screenshots/04_dashboard_verified.png

[测试 5] 检查控制台输出
------------------------------
共捕获 5 条控制台消息:

  [LOG] 页面加载完成
  [LOG] API 初始化成功
  [LOG] 用户会话已创建

==================================================
测试摘要
==================================================

总计:5 个测试
通过:5
失败:0
部分通过:0

详细结果:
------------------------------
 打开登录页面: 成功加载页面,标题:登录
 填写登录表单: 表单已成功填写
 提交登录表单: 登录成功,已跳转到仪表盘
 验证仪表盘: 成功进入仪表盘页面
 控制台检查: 捕获 5 条消息

==================================================

浏览器连接已关闭

常见使用场景

chrome-devtools-mcp 的应用场景非常广泛。以下是几个典型的使用案例。

场景一:网页数据抓取

传统的爬虫需要解析 HTML、处理 JavaScript 渲染。使用 chrome-devtools-mcp 可以直接操控真实浏览器,完成复杂的数据抓取任务。

"""
网页数据抓取示例
"""

async def scrape_dynamic_content():
    """
    抓取需要 JavaScript 渲染的页面内容
    """
    connection = ChromeConnection()
    await connection.connect()
    page = Page(connection)

    # 导航到目标页面
    await page.navigate("https://example.com/data-table")

    # 等待数据加载
    await page.wait_for_selector(".data-table tbody tr")

    # 提取数据
    data = await page.evaluate("""
        () => {
            const rows = document.querySelectorAll('.data-table tbody tr');
            return Array.from(rows).map(row => {
                const cells = row.querySelectorAll('td');
                return {
                    id: cells[0]?.textContent,
                    name: cells[1]?.textContent,
                    value: cells[2]?.textContent
                };
            });
        }
    """)

    print(f"抓取到 {len(data)} 条数据")
    for item in data:
        print(f"  {item['id']}: {item['name']} = {item['value']}")

    await connection.close()

场景二:自动化 UI 测试

配合测试框架,可以构建完整的 UI 自动化测试套件。

"""
UI 测试示例
"""

async def test_ui_components():
    """
    测试 UI 组件的交互功能
    """
    connection = ChromeConnection()
    await connection.connect()
    page = Page(connection)

    # 打开测试页面
    await page.navigate("https://example.com/ui-test")

    # 测试模态框
    print("测试模态框...")
    await page.click(".open-modal-btn")
    await page.wait_for_selector(".modal.show")
    modal_visible = await page.evaluate("""
        () => document.querySelector('.modal.show') !== null
    """)
    print(f"  模态框显示:{'✓' if modal_visible else '✗'}")

    # 测试下拉菜单
    print("测试下拉菜单...")
    await page.click(".dropdown-toggle")
    await page.wait_for_selector(".dropdown-menu.show")
    await page.click(".dropdown-item:nth-child(2)")
    selected = await page.evaluate("""
        () => document.querySelector('.dropdown-toggle').textContent
    """)
    print(f"  选中项:{selected}")

    # 测试表单验证
    print("测试表单验证...")
    await page.fill_input("#email-input", "invalid-email")
    await page.click("#submit-btn")
    error_shown = await page.evaluate("""
        () => document.querySelector('.error-message') !== null
    """)
    print(f"  错误提示显示:{'✓' if error_shown else '✗'}")

    await connection.close()

场景三:性能监控

监控网页加载性能和 JavaScript 执行效率。

"""
性能监控示例
"""

async def monitor_page_performance():
    """
    监控页面性能指标
    """
    connection = ChromeConnection()
    await connection.connect()
    page = Page(connection)

    # 启用性能监控
    await connection.send_command("Performance.enable")

    # 导航并测量
    await page.navigate("https://example.com")

    # 获取性能指标
    metrics = await page.evaluate("""
        () => {
            const timing = performance.timing;
            return {
                // 页面加载时间
                pageLoadTime: timing.loadEventEnd - timing.navigationStart,
                // DOM 解析时间
                domParseTime: timing.domContentLoadedEventEnd - timing.domLoading,
                // 首字节时间
                ttfb: timing.responseStart - timing.requestStart,
                // 资源加载时间
                resourceLoadTime: performance.now(),
                // 关键性能指标
                metrics: {
                    fcp: null, // First Contentful Paint
                    lcp: null, // Largest Contentful Paint
                    fid: null, // First Input Delay
                    cls: null  // Cumulative Layout Shift
                }
            };
        }
    """)

    print("性能指标:")
    print(f"  页面加载时间:{metrics['pageLoadTime']}ms")
    print(f"  DOM 解析时间:{metrics['domParseTime']}ms")
    print(f"  首字节时间:{metrics['ttfb']}ms")

    # 获取 Network 请求统计
    await connection.send_command("Network.enable")
    await asyncio.sleep(2)  # 等待网络请求完成

    await connection.close()

场景四:与 AI 助手集成

将浏览器控制能力提供给 AI 助手,实现智能化的浏览器操作。

// MCP 配置文件 (mcp.json)
{
    "mcpServers": {
        "chrome-devtools": {
            "command": "npx",
            "args": ["-y", "chrome-devtools-mcp"]
        }
    }
}
"""
AI 助手指令示例
"""

AI_COMMANDS = """
你可以使用以下命令控制浏览器:

1. 页面导航
   page.navigate(url) - 打开指定网页
   page.reload() - 刷新页面
   page.back() - 后退
   page.forward() - 前进

2. 元素操作
   page.click(selector) - 点击元素
   page.fill_input(selector, value) - 填写输入框
   page.hover(selector) - 鼠标悬停

3. 内容获取
   page.screenshot(path) - 截取页面
   page.get_text(selector) - 获取文本内容
   page.get_html(selector) - 获取 HTML 内容

4. JavaScript 执行
   page.evaluate(script) - 执行 JavaScript 代码

5. 等待操作
   page.wait_for_selector(selector) - 等待元素出现
   page.wait_for_navigation() - 等待导航完成
"""

print(AI_COMMANDS)

高级技巧与最佳实践

技巧一:优雅的错误处理

在实际项目中,稳定的错误处理至关重要。

"""
高级错误处理示例
"""

import asyncio
from typing import Optional, Callable, Any
from functools import wraps


class BrowserAutomationError(Exception):
    """浏览器自动化基础异常"""
    pass


class ElementNotFoundError(BrowserAutomationError):
    """元素未找到异常"""
    pass


class TimeoutError(BrowserAutomationError):
    """操作超时异常"""
    pass


async def retry_on_failure(
    max_retries: int = 3,
    delay: float = 1.0,
    exceptions: tuple = (Exception,)
):
    """
    重试装饰器

    Args:
        max_retries: 最大重试次数
        delay: 重试间隔(秒)
        exceptions: 需要重试的异常类型
    """
    def decorator(func: Callable) -> Callable:
        @wraps(func)
        async def wrapper(*args, **kwargs) -> Any:
            last_exception = None

            for attempt in range(max_retries):
                try:
                    return await func(*args, **kwargs)
                except exceptions as e:
                    last_exception = e
                    if attempt < max_retries - 1:
                        print(f"尝试 {attempt + 1}/{max_retries} 失败: {e}")
                        print(f"等待 {delay} 秒后重试...")
                        await asyncio.sleep(delay)
                    else:
                        print(f"已达到最大重试次数 {max_retries}")

            raise last_exception

        return wrapper
    return decorator


class RobustPage(Page):
    """
    增强版的页面操作类
    提供更稳定的操作方法
    """

    async def safe_click(self, selector: str, timeout: int = 10) -> bool:
        """
        安全点击元素,带重试机制

        Args:
            selector: CSS 选择器
            timeout: 超时时间

        Returns:
            是否成功
        """
        @retry_on_failure(max_retries=3, delay=1.0)
        async def _click():
            # 先滚动到元素可见
            await self.evaluate(f"""
                () => {{
                    const el = document.querySelector('{selector}');
                    if (el) {{
                        el.scrollIntoView({{ behavior: 'smooth', block: 'center' }}));
                    }}
                }}
            """)
            await asyncio.sleep(0.5)

            # 执行点击
            result = await self.click(selector)
            if not result:
                raise ElementNotFoundError(f"无法点击元素: {selector}")

            return result

        return await _click()

    async def safe_fill(self, selector: str, value: str, 
                       clear_first: bool = True) -> bool:
        """
        安全填写输入框

        Args:
            selector: CSS 选择器
            value: 填写值
            clear_first: 是否先清空

        Returns:
            是否成功
        """
        @retry_on_failure(max_retries=3, delay=0.5)
        async def _fill():
            if clear_first:
                # 清空输入框
                await self.evaluate(f"""
                    () => {{
                        const el = document.querySelector('{selector}');
                        if (el) {{
                            el.value = '';
                            el.dispatchEvent(new Event('input', {{ bubbles: true }}));
                        }}
                    }}
                """)
                await asyncio.sleep(0.2)

            return await self.fill_input(selector, value)

        return await _fill()

    async def wait_and_get(
        self, 
        selector: str, 
        attribute: Optional[str] = None,
        timeout: int = 10
    ) -> Any:
        """
        等待元素出现并获取其内容

        Args:
            selector: CSS 选择器
            attribute: 要获取的属性,为空则获取文本
            timeout: 超时时间

        Returns:
            元素内容
        """
        # 等待元素
        found = await self.wait_for_selector(selector, timeout * 1000)

        if not found:
            raise ElementNotFoundError(f"元素未找到: {selector}")

        # 获取内容
        if attribute:
            return await self.evaluate(f"""
                () => document.querySelector('{selector}')?.getAttribute('{attribute}')
            """)
        else:
            return await self.evaluate(f"""
                () => document.querySelector('{selector}')?.textContent?.trim()
            """)

技巧二:并发页面操作

使用异步并发提升操作效率。

"""
并发操作示例
"""

import asyncio
from typing import List, Dict


async def batch_navigate(pages: List[Page], urls: List[str]) -> List[Dict]:
    """
    批量并发导航

    Args:
        pages: 页面对象列表
        urls: URL 列表

    Returns:
        每个页面的导航结果
    """
    async def navigate_single(page: Page, url: str) -> Dict:
        try:
            await page.navigate(url)
            title = await page.evaluate("() => document.title")
            return {"url": url, "status": "success", "title": title}
        except Exception as e:
            return {"url": url, "status": "error", "error": str(e)}

    # 使用 gather 并发执行所有导航
    tasks = [navigate_single(page, url) 
             for page, url in zip(pages, urls)]

    results = await asyncio.gather(*tasks, return_exceptions=True)

    return results


async def scrape_multiple_sites(sites: List[str]) -> Dict[str, List]:
    """
    并发抓取多个网站

    Args:
        sites: 网站列表

    Returns:
        抓取结果
    """
    connection = ChromeConnection()
    await connection.connect()

    # 创建多个标签页
    page_objects = []
    for _ in range(len(sites)):
        page = Page(connection)
        page_objects.append(page)

    # 并发抓取
    results = await batch_navigate(page_objects, sites)

    # 汇总数据
    scraped_data = {}
    for result in results:
        if result.get("status") == "success":
            scraped_data[result["url"]] = {
                "title": result["title"],
                "links": await page_objects[results.index(result)]
                    .evaluate("() => Array.from(document.querySelectorAll('a')).slice(0, 10).map(a => a.href)")
            }

    await connection.close()
    return scraped_data

技巧三:日志与调试

完善的日志系统有助于排查问题。

"""
日志系统示例
"""

import logging
import json
from datetime import datetime
from pathlib import Path
from typing import Optional


class AutomationLogger:
    """
    自动化日志记录器
    """

    def __init__(self, log_dir: str = "./logs"):
        self.log_dir = Path(log_dir)
        self.log_dir.mkdir(parents=True, exist_ok=True)

        # 创建日志文件名(按日期)
        log_file = self.log_dir / f"automation_{datetime.now().strftime('%Y%m%d')}.log"

        # 配置日志
        self.logger = logging.getLogger("BrowserAutomation")
        self.logger.setLevel(logging.DEBUG)

        # 文件处理器
        file_handler = logging.FileHandler(log_file, encoding="utf-8")
        file_handler.setLevel(logging.DEBUG)

        # 控制台处理器
        console_handler = logging.StreamHandler()
        console_handler.setLevel(logging.INFO)

        # 格式化
        formatter = logging.Formatter(
            "%(asctime)s [%(levelname)s] %(message)s",
            datefmt="%Y-%m-%d %H:%M:%S"
        )
        file_handler.setFormatter(formatter)
        console_handler.setFormatter(formatter)

        self.logger.addHandler(file_handler)
        self.logger.addHandler(console_handler)

        # 截图日志
        self.screenshot_log = self.log_dir / "screenshots.json"

    def info(self, message: str):
        self.logger.info(message)

    def debug(self, message: str):
        self.logger.debug(message)

    def warning(self, message: str):
        self.logger.warning(message)

    def error(self, message: str, exc_info: Optional[Exception] = None):
        if exc_info:
            self.logger.error(f"{message}\n{str(exc_info)}", exc_info=True)
        else:
            self.logger.error(message)

    def log_step(self, step_name: str, details: dict = None):
        """记录测试步骤"""
        log_entry = {
            "timestamp": datetime.now().isoformat(),
            "step": step_name,
            "details": details or {}
        }
        self.logger.info(f"步骤: {step_name} | 详情: {json.dumps(details, ensure_ascii=False)}")

        # 追加到截图日志
        self._append_screenshot_log(log_entry)

    def _append_screenshot_log(self, entry: dict):
        """追加截图日志"""
        try:
            if self.screenshot_log.exists():
                with open(self.screenshot_log, "r", encoding="utf-8") as f:
                    logs = json.load(f)
            else:
                logs = []

            logs.append(entry)

            with open(self.screenshot_log, "w", encoding="utf-8") as f:
                json.dump(logs, f, ensure_ascii=False, indent=2)
        except Exception as e:
            self.logger.warning(f"无法记录截图日志: {e}")


# 使用示例
async def logged_automation():
    """
    带日志的自动化示例
    """
    logger = AutomationLogger()

    logger.info("开始自动化测试")

    try:
        logger.log_step("初始化浏览器", {"port": 9222})
        # 连接浏览器...

        logger.log_step("打开目标页面", {"url": "https://example.com"})
        # 导航...

        logger.log_step("执行操作", {"action": "click", "selector": "#submit"})
        # 执行点击...

        logger.info("测试完成")

    except Exception as e:
        logger.error("测试失败", exc_info=e)
        raise

技巧四:配置文件管理

使用配置文件管理不同环境的设置。

"""
配置管理示例
"""

import json
from pathlib import Path
from typing import Optional, Dict, Any
from dataclasses import dataclass, field


@dataclass
class Environment:
    """环境配置"""
    name: str
    base_url: str
    api_url: str
    debug_port: int
    timeout: int = 30
    headless: bool = False


@dataclass
class Credentials:
    """凭证配置"""
    username: str
    password: str
    api_key: Optional[str] = None


@dataclass
class TestConfig:
    """测试配置"""
    environment: Environment
    credentials: Credentials
    screenshot_on_error: bool = True
    retry_count: int = 3
    retry_delay: float = 1.0
    custom_options: Dict[str, Any] = field(default_factory=dict)

    @classmethod
    def from_json(cls, path: str) -> "TestConfig":
        """从 JSON 文件加载配置"""
        with open(path, "r", encoding="utf-8") as f:
            data = json.load(f)

        env_data = data.get("environment", {})
        env = Environment(
            name=env_data.get("name", "default"),
            base_url=env_data.get("base_url", ""),
            api_url=env_data.get("api_url", ""),
            debug_port=env_data.get("debug_port", 9222),
            timeout=env_data.get("timeout", 30),
            headless=env_data.get("headless", False)
        )

        cred_data = data.get("credentials", {})
        creds = Credentials(
            username=cred_data.get("username", ""),
            password=cred_data.get("password", ""),
            api_key=cred_data.get("api_key")
        )

        return cls(
            environment=env,
            credentials=creds,
            screenshot_on_error=data.get("screenshot_on_error", True),
            retry_count=data.get("retry_count", 3),
            retry_delay=data.get("retry_delay", 1.0),
            custom_options=data.get("custom_options", {})
        )

    def to_json(self, path: str):
        """保存配置到 JSON 文件"""
        data = {
            "environment": {
                "name": self.environment.name,
                "base_url": self.environment.base_url,
                "api_url": self.environment.api_url,
                "debug_port": self.environment.debug_port,
                "timeout": self.environment.timeout,
                "headless": self.environment.headless
            },
            "credentials": {
                "username": self.credentials.username,
                "password": self.credentials.password,
                "api_key": self.credentials.api_key
            },
            "screenshot_on_error": self.screenshot_on_error,
            "retry_count": self.retry_count,
            "retry_delay": self.retry_delay,
            "custom_options": self.custom_options
        }

        Path(path).parent.mkdir(parents=True, exist_ok=True)
        with open(path, "w", encoding="utf-8") as f:
            json.dump(data, f, ensure_ascii=False, indent=2)


# 配置文件示例 (config.json)
CONFIG_EXAMPLE = """
{
    "environment": {
        "name": "测试环境",
        "base_url": "https://test.example.com",
        "api_url": "https://api.test.example.com",
        "debug_port": 9222,
        "timeout": 30,
        "headless": false
    },
    "credentials": {
        "username": "testuser@example.com",
        "password": "TestPass123!",
        "api_key": null
    },
    "screenshot_on_error": true,
    "retry_count": 3,
    "retry_delay": 1.0,
    "custom_options": {
        "window_size": [1920, 1080],
        "user_agent": "Mozilla/5.0 (compatible; Bot/1.0)"
    }
}
"""

# 使用配置
async def run_with_config(config_path: str):
    """
    使用配置文件运行测试
    """
    config = TestConfig.from_json(config_path)

    print(f"运行配置:{config.environment.name}")
    print(f"目标地址:{config.environment.base_url}")

    connection = ChromeConnection(debug_port=config.environment.debug_port)
    await connection.connect()

    page = Page(connection)
    await page.navigate(config.environment.base_url)

    # 使用配置的凭证
    await page.fill_input("#username", config.credentials.username)
    await page.fill_input("#password", config.credentials.password)

    await connection.close()

常见问题与解决方案

问题一:连接被拒绝

症状:

ConnectionRefusedError: [Errno 61] Connection refused

原因:

  • Chrome 调试模式未开启
  • 调试端口被占用
  • 防火墙阻止

解决方案:

# 1. 确认 Chrome 调试端口是否开启
curl http://localhost:9222/json

# 2. 如果端口被占用,找出并关闭占用进程
lsof -i :9222

# 3. 重新启动 Chrome 调试模式
# macOS
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
    --remote-debugging-port=9223 \
    --user-data-dir=/tmp/chrome-debug

问题二:元素点击无效

症状:

元素存在但点击没有效果

原因:

  • 元素不可见或被遮挡
  • 需要特殊的事件触发
  • 元素选择器不正确

解决方案:

# 确保元素可见并可交互
await page.evaluate("""
    () => {
        const el = document.querySelector('YOUR_SELECTOR');
        el.scrollIntoView({ behavior: 'smooth', block: 'center' });
    }
""")

# 使用 JavaScript 直接触发点击
await page.evaluate("""
    () => {
        const el = document.querySelector('YOUR_SELECTOR');
        el.dispatchEvent(new MouseEvent('click', {
            view: window,
            bubbles: true,
            cancelable: true
        }));
    }
""")

问题三:页面加载超时

症状:

TimeoutError: waiting for selector failed

原因:

  • 网络问题导致页面加载缓慢
  • 页面结构变化,选择器不匹配
  • SPA 页面内容动态加载

解决方案:

# 增加超时时间
await page.wait_for_selector(
    "YOUR_SELECTOR",
    timeout=60000  # 60 秒
)

# 或者使用轮询方式
import asyncio

async def wait_for_content(page: Page, condition_fn, timeout: int = 60):
    """等待条件满足"""
    start = asyncio.get_event_loop().time()

    while (asyncio.get_event_loop().time() - start) < timeout:
        result = await page.evaluate(condition_fn)
        if result:
            return True
        await asyncio.sleep(1)

    raise TimeoutError("条件未在超时时间内满足")

问题四:执行 JavaScript 报错

症状:

result = {'exceptionDetails': {...}}

原因:

  • JavaScript 语法错误
  • 页面上下文不存在
  • 访问了不存在的属性

解决方案:

# 添加错误处理
result = await page.evaluate("""
    () => {
        try {
            return {
                success: true,
                data: document.querySelector('YOUR_SELECTOR').textContent
            };
        } catch (e) {
            return {
                success: false,
                error: e.message
            };
        }
    }
""")

if result.get("success"):
    print(result["data"])
else:
    print(f"JavaScript 错误: {result['error']}")

问题五:中文编码问题

症状:

页面中的中文显示为乱码

解决方案:

# 确保文件保存为 UTF-8 编码
# 在代码开头添加
import sys
sys.stdout.reconfigure(encoding='utf-8')

# 读取包含中文的配置
with open("config.json", "r", encoding="utf-8") as f:
    config = json.load(f)

总结与资源链接

项目总结

chrome-devtools-mcp 是一个功能强大且易于使用的浏览器自动化工具。通过 MCP 协议,它让 AI 助手能够直接控制 Chrome 浏览器,为前端开发、自动化测试、数据抓取等场景提供了全新的解决方案。

核心优势:

  • 零配置启动,一条命令即可运行
  • 完整的 CDP 协议支持,功能覆盖全面
  • 与主流 AI 助手无缝集成
  • 开源免费,社区活跃

最佳使用场景:

  • AI 助手的浏览器自动化操作
  • 前端自动化测试
  • 网页数据抓取
  • 性能监控与调试
  • 复杂的用户交互模拟

官方资源

资源 链接
GitHub 仓库 https://github.com/ChromeDevTools/chrome-devtools-mcp
官方文档 https://chromedevtools.github.io/devtools-protocol/
MCP 协议规范 https://modelcontextprotocol.io/
Chrome DevTools https://developer.chrome.com/docs/devtools/

相关开源项目

以下是一些与 chrome-devtools-mcp 相关的优秀开源项目,它们从不同角度扩展了浏览器自动化的能力:

1. Puppeteer

Google 官方维护的 Node.js 库,提供高级浏览器控制 API

npm install puppeteer

GitHub: https://github.com/puppeteer/puppeteer

2. Playwright

微软开发的跨浏览器自动化框架,支持 Chromium、Firefox、WebKit

pip install playwright
playwright install

GitHub: https://github.com/microsoft/playwright

3. Selenium

最成熟的浏览器自动化工具,支持多种编程语言

pip install selenium

GitHub: https://github.com/SeleniumHQ/selenium

4. DrissionPage

国产的浏览器自动化工具,对中文用户友好

pip install DrissionPage

GitHub: https://github.com/g1879/DrissionPage

5. MCP Server 模板

如果想开发自己的 MCP 服务器,可以参考这个模板项目

npx create-mcp-server

GitHub: https://github.com/modelcontextprotocol/server-template

后续学习路径

  1. 深入学习 CDP 协议:掌握底层的 Chrome DevTools Protocol,为高级应用奠定基础

  2. 集成 AI 助手:将浏览器控制能力与 Claude、Cursor 等 AI 工具结合,打造智能化的开发体验

  3. 构建测试框架:基于 chrome-devtools-mcp 构建完整的自动化测试框架

  4. 性能优化:学习浏览器性能分析,提升页面加载和渲染效率

  5. 安全测试:探索浏览器安全漏洞检测和 XSS 攻击模拟


通过本文的详细讲解,你应该已经掌握了 chrome-devtools-mcp 的核心使用方法。无论是作为独立工具使用,还是与 AI 助手配合,这个项目都能极大地提升你的工作效率。现在就去尝试一下吧!

如果内容对您有帮助,欢迎打赏

您的支持是我继续创作的动力

前往打赏页面

评论区

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注