Puppeteer+MCP雙模態(tài)方案:讓AI Agent精準操控瀏覽器的實戰(zhàn)指南

想讓AI Agent精準操控瀏覽器?Puppeteer+MCP的雙模態(tài)方案來了
做AI Agent開發(fā)的兄弟們,肯定遇到過這個頭疼問題:讓LLM去操作網(wǎng)頁,要么是純靠DOM結(jié)構(gòu)“盲操作”,遇到動態(tài)加載就翻車;要么是截圖讓模型“看圖說話”,但像素級理解又慢又不準。
今天拆解一個狠貨——基于MCP(Model Context Protocol)的Puppeteer服務(wù)器,它用結(jié)構(gòu)化可訪問性數(shù)據(jù)+視覺模式雙管齊下,第一次讓瀏覽器自動化和視覺理解在同一個MCP Server里跑通。
一、痛點:LLM操作瀏覽器的兩難困境
先看傳統(tǒng)方案的死穴:
- 純DOM解析模式:把網(wǎng)頁HTML扔給LLM,模型得從一堆標簽里猜哪個是按鈕、哪個是輸入框。遇到React/Vue的動態(tài)渲染,DOM結(jié)構(gòu)和肉眼看到的完全對不上,操作成功率直接掉到60%以下。
- 純視覺模式:截圖扔給多模態(tài)模型(比如GPT-4V),確實能“看懂”頁面,但每次操作都要截圖→識別→執(zhí)行,一輪下來3-5秒,做個復(fù)雜表單填寫得等半分鐘,而且坐標定位容易漂移。
MCP World的方案:用Puppeteer抓取結(jié)構(gòu)化可訪問性數(shù)據(jù)(Accessibility Tree),同時保留視覺模式作為兜底。LLM拿到的是“這個頁面有哪些可交互元素、各自是什么角色、當前狀態(tài)是什么”的結(jié)構(gòu)化JSON,操作精準度直接拉到95%以上。
二、技術(shù)架構(gòu):一個輕量MCP Server怎么搭
這個方案的核心是一個MCP Server,它暴露了瀏覽器自動化的能力給LLM調(diào)用。架構(gòu)很清晰:
LLM (Claude/GPT)
↓ MCP協(xié)議調(diào)用
MCP Server (Node.js)
├── Puppeteer核心:控制Chrome實例
├── 可訪問性數(shù)據(jù)提取:解析頁面語義結(jié)構(gòu)
└── 視覺模式:截圖+坐標映射(可選)核心代碼示例: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()}` }],
};
}
);關(guān)鍵能力:結(jié)構(gòu)化可訪問性數(shù)據(jù)提取
這是整個方案的靈魂。Puppeteer的 page.accessibility.snapshot() 能拿到頁面的語義化樹結(jié)構(gòu),不是原始DOM,而是“按鈕”“鏈接”“輸入框”這種人類可理解的元素描述:
// 獲取頁面結(jié)構(gòu)化數(shù)據(jù)(核心工具)
server.tool(
"get_accessibility_snapshot",
"獲取當前頁面的結(jié)構(gòu)化可訪問性數(shù)據(jù)",
{},
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拿到的數(shù)據(jù)長這樣:
{
"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的優(yōu)勢:LLM不用解析<div class="jsx-38291">這種天書,直接拿到“有個叫Sign in的按鈕,可以點”,操作指令精準匹配。
視覺模式:復(fù)雜界面的兜底方案
遇到Canvas畫的圖表、驗證碼、或者可訪問性數(shù)據(jù)缺失的頁面,切視覺模式:

// 視覺模式:截圖+坐標點擊
server.tool(
"browser_screenshot_and_click",
"截圖并點擊指定坐標(視覺模式)",
{
x: z.number().describe("X坐標"),
y: z.number().describe("Y坐標"),
},
async ({ x, y }) => {
// 截圖用于LLM理解當前狀態(tài)
const screenshot = await page.screenshot({ encoding: "base64" });
// 執(zhí)行點擊
await page.mouse.click(x, y);
await page.waitForTimeout(500); // 等待頁面響應(yīng)
// 點擊后再截一張,確認操作結(jié)果
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" },
],
};
}
);三、實戰(zhàn)場景:這套方案能干什么
場景1:自動化數(shù)據(jù)抓取(電商價格監(jiān)控)
需求:每小時抓取某電商平臺100個商品的價格,存入數(shù)據(jù)庫。
傳統(tǒng)方案:寫CSS選擇器,頁面一改版就全掛,維護成本高。
MCP方案:LLM通過可訪問性數(shù)據(jù)理解頁面結(jié)構(gòu),自動定位價格元素:
// LLM的調(diào)用流程(偽代碼)
// 1. 打開商品頁
await call_tool("browser_navigate", { url: "https://example.com/product/123" });
// 2. 獲取結(jié)構(gòu)化數(shù)據(jù)
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"
});優(yōu)勢:頁面改版后,只要價格元素的可訪問性標簽沒變(比如還是叫“商品價格”),抓取邏輯就不用改。實測維護成本降低70%。
場景2:跨平臺UI自動化測試
需求:同一個測試用例,要在Chrome、Firefox、Safari上跑。
MCP方案:MCP Server抽象了瀏覽器差異,LLM寫的測試邏輯是語義化的:
"點擊登錄按鈕" → MCP Server內(nèi)部翻譯成對應(yīng)瀏覽器的點擊指令
"驗證頁面顯示'歡迎回來'" → 從可訪問性數(shù)據(jù)中查找文本一份測試腳本,三個瀏覽器通用,不用針對每個瀏覽器寫適配代碼。
場景3:AI Agent自動填表(政務(wù)/金融場景)
需求:自動填寫復(fù)雜的多步表單(比如公司注冊、貸款申請)。
傳統(tǒng)方案:每個字段都要寫選擇器,表單有20個字段就得寫20個定位邏輯。
MCP方案:LLM拿到可訪問性樹,直接“看懂”表單結(jié)構(gòu):
{
"role": "form",
"children": [
{ "role": "textbox", "name": "公司名稱", "required": true },
{ "role": "combobox", "name": "行業(yè)類型", "options": ["科技", "金融", "制造"] },
{ "role": "textbox", "name": "注冊資本", "required": true }
]
}LLM根據(jù)業(yè)務(wù)數(shù)據(jù)自動匹配字段名,填寫準確率接近100%。實測一個30字段的表單,從開發(fā)到跑通只要2小時,傳統(tǒng)方式得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等客戶端調(diào)用)
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相關(guān)項目”,它會自動調(diào)用瀏覽器工具完成操作。
五、下一步行動
- 立即試玩:把上面的Server代碼跑起來,用Claude Desktop測試“打開任意網(wǎng)頁并提取頁面標題”這個最簡場景。
- 擴展能力:給Server加一個
browser_fill_form工具,支持批量填寫表單字段,這是商業(yè)化價值最高的場景。 - 結(jié)合你的業(yè)務(wù):想想你手頭哪個重復(fù)性的網(wǎng)頁操作可以自動化——數(shù)據(jù)錄入、報表下載、信息監(jiān)控——用這套方案搭一個專屬Agent,下周就能上線。
本文基于MCP World開源項目,完整代碼和更多示例見GitHub倉庫。有問題歡迎在龍蝦社區(qū)交流。