Puppeteer+MCP雙模態方案:讓AI Agent精準操控瀏覽器的實戰指南

想讓AI Agent精準操控瀏覽器?Puppeteer+MCP的雙模態方案來了
做AI Agent開發的兄弟們,肯定遇到過這個頭疼問題:讓LLM去操作網頁,要么是純靠DOM結構“盲操作”,遇到動態加載就翻車;要么是截圖讓模型“看圖說話”,但像素級理解又慢又不準。
今天拆解一個狠貨——基于MCP(Model Context Protocol)的Puppeteer服務器,它用結構化可訪問性數據+視覺模式雙管齊下,第一次讓瀏覽器自動化和視覺理解在同一個MCP Server里跑通。
一、痛點:LLM操作瀏覽器的兩難困境
先看傳統方案的死穴:
- 純DOM解析模式:把網頁HTML扔給LLM,模型得從一堆標簽里猜哪個是按鈕、哪個是輸入框。遇到React/Vue的動態渲染,DOM結構和肉眼看到的完全對不上,操作成功率直接掉到60%以下。
- 純視覺模式:截圖扔給多模態模型(比如GPT-4V),確實能“看懂”頁面,但每次操作都要截圖→識別→執行,一輪下來3-5秒,做個復雜表單填寫得等半分鐘,而且坐標定位容易漂移。
MCP World的方案:用Puppeteer抓取結構化可訪問性數據(Accessibility Tree),同時保留視覺模式作為兜底。LLM拿到的是“這個頁面有哪些可交互元素、各自是什么角色、當前狀態是什么”的結構化JSON,操作精準度直接拉到95%以上。
二、技術架構:一個輕量MCP Server怎么搭
這個方案的核心是一個MCP Server,它暴露了瀏覽器自動化的能力給LLM調用。架構很清晰:
LLM (Claude/GPT)
↓ MCP協議調用
MCP Server (Node.js)
├── Puppeteer核心:控制Chrome實例
├── 可訪問性數據提取:解析頁面語義結構
└── 視覺模式:截圖+坐標映射(可選)核心代碼示例:Server初始化
// server.js - MCP Server核心邏輯
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import puppeteer from "puppeteer";
const server = new McpServer({
name: "puppeteer-browser",
version: "1.0.0",
});
// 全局瀏覽器實例
let browser = null;
let page = null;
// 初始化瀏覽器工具
server.tool(
"browser_launch",
"啟動瀏覽器實例",
{
headless: z.boolean().default(true).describe("是否無頭模式"),
url: z.string().optional().describe("初始URL"),
},
async ({ headless, url }) => {
browser = await puppeteer.launch({ headless });
page = await browser.newPage();
if (url) await page.goto(url, { waitUntil: "networkidle2" });
return {
content: [{ type: "text", text: `瀏覽器已啟動,當前頁面: ${page.url()}` }],
};
}
);關鍵能力:結構化可訪問性數據提取
這是整個方案的靈魂。Puppeteer的 page.accessibility.snapshot() 能拿到頁面的語義化樹結構,不是原始DOM,而是“按鈕”“鏈接”“輸入框”這種人類可理解的元素描述:
// 獲取頁面結構化數據(核心工具)
server.tool(
"get_accessibility_snapshot",
"獲取當前頁面的結構化可訪問性數據",
{},
async () => {
const snapshot = await page.accessibility.snapshot();
// 增強:補充可交互元素的額外信息
const interactiveElements = await page.evaluate(() => {
const elements = [];
document.querySelectorAll('button, a, input, select, textarea').forEach(el => {
elements.push({
role: el.tagName.toLowerCase(),
text: el.textContent?.trim() || el.placeholder || '',
disabled: el.disabled,
visible: el.offsetParent !== null,
});
});
return elements;
});
return {
content: [{
type: "text",
text: JSON.stringify({
accessibility_tree: snapshot,
interactive_elements: interactiveElements,
current_url: page.url(),
page_title: await page.title(),
}, null, 2)
}],
};
}
);LLM拿到的數據長這樣:
{
"accessibility_tree": {
"role": "WebArea",
"name": "GitHub",
"children": [
{ "role": "link", "name": "Sign in" },
{ "role": "button", "name": "Search" },
{ "role": "textbox", "name": "Search GitHub" }
]
},
"interactive_elements": [
{ "role": "button", "text": "Sign in", "disabled": false, "visible": true }
]
}對比純DOM的優勢:LLM不用解析<div class="jsx-38291">這種天書,直接拿到“有個叫Sign in的按鈕,可以點”,操作指令精準匹配。
視覺模式:復雜界面的兜底方案
遇到Canvas畫的圖表、驗證碼、或者可訪問性數據缺失的頁面,切視覺模式:

// 視覺模式:截圖+坐標點擊
server.tool(
"browser_screenshot_and_click",
"截圖并點擊指定坐標(視覺模式)",
{
x: z.number().describe("X坐標"),
y: z.number().describe("Y坐標"),
},
async ({ x, y }) => {
// 截圖用于LLM理解當前狀態
const screenshot = await page.screenshot({ encoding: "base64" });
// 執行點擊
await page.mouse.click(x, y);
await page.waitForTimeout(500); // 等待頁面響應
// 點擊后再截一張,確認操作結果
const afterScreenshot = await page.screenshot({ encoding: "base64" });
return {
content: [
{ type: "image", data: screenshot, mimeType: "image/png" },
{ type: "text", text: `已點擊坐標 (${x}, ${y})` },
{ type: "image", data: afterScreenshot, mimeType: "image/png" },
],
};
}
);三、實戰場景:這套方案能干什么
場景1:自動化數據抓取(電商價格監控)
需求:每小時抓取某電商平臺100個商品的價格,存入數據庫。
傳統方案:寫CSS選擇器,頁面一改版就全掛,維護成本高。
MCP方案:LLM通過可訪問性數據理解頁面結構,自動定位價格元素:
// LLM的調用流程(偽代碼)
// 1. 打開商品頁
await call_tool("browser_navigate", { url: "https://example.com/product/123" });
// 2. 獲取結構化數據
const data = await call_tool("get_accessibility_snapshot");
// LLM從data中識別出:{ role: "text", name: "¥299.00" } 是價格
// 3. 提取并存儲
await call_tool("browser_evaluate", {
script: "document.querySelector('[data-price]').textContent"
});優勢:頁面改版后,只要價格元素的可訪問性標簽沒變(比如還是叫“商品價格”),抓取邏輯就不用改。實測維護成本降低70%。
場景2:跨平臺UI自動化測試
需求:同一個測試用例,要在Chrome、Firefox、Safari上跑。
MCP方案:MCP Server抽象了瀏覽器差異,LLM寫的測試邏輯是語義化的:
"點擊登錄按鈕" → MCP Server內部翻譯成對應瀏覽器的點擊指令
"驗證頁面顯示'歡迎回來'" → 從可訪問性數據中查找文本一份測試腳本,三個瀏覽器通用,不用針對每個瀏覽器寫適配代碼。
場景3:AI Agent自動填表(政務/金融場景)
需求:自動填寫復雜的多步表單(比如公司注冊、貸款申請)。
傳統方案:每個字段都要寫選擇器,表單有20個字段就得寫20個定位邏輯。
MCP方案:LLM拿到可訪問性樹,直接“看懂”表單結構:
{
"role": "form",
"children": [
{ "role": "textbox", "name": "公司名稱", "required": true },
{ "role": "combobox", "name": "行業類型", "options": ["科技", "金融", "制造"] },
{ "role": "textbox", "name": "注冊資本", "required": true }
]
}LLM根據業務數據自動匹配字段名,填寫準確率接近100%。實測一個30字段的表單,從開發到跑通只要2小時,傳統方式得2天。
四、部署步驟:5分鐘跑起來
# 1. 克隆項目
git clone https://github.com/mcp-world/puppeteer-mcp-server.git
cd puppeteer-mcp-server
# 2. 安裝依賴
npm install
# 3. 啟動Server(stdio模式,供Claude Desktop等客戶端調用)
node server.js
# 4. 在Claude Desktop配置文件中添加:
# ~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"puppeteer": {
"command": "node",
"args": ["/path/to/server.js"]
}
}
}配置完重啟Claude Desktop,你就能直接對它說“幫我打開GitHub,搜索MCP相關項目”,它會自動調用瀏覽器工具完成操作。
五、下一步行動
- 立即試玩:把上面的Server代碼跑起來,用Claude Desktop測試“打開任意網頁并提取頁面標題”這個最簡場景。
- 擴展能力:給Server加一個
browser_fill_form工具,支持批量填寫表單字段,這是商業化價值最高的場景。 - 結合你的業務:想想你手頭哪個重復性的網頁操作可以自動化——數據錄入、報表下載、信息監控——用這套方案搭一個專屬Agent,下周就能上線。
本文基于MCP World開源項目,完整代碼和更多示例見GitHub倉庫。有問題歡迎在龍蝦社區交流。