一、概述
網頁自動化在數據抓取、UI 測試和業務流程優化中發揮著重要作用。然而,傳統工具如 Selenium 和 Puppeteer 要求用戶具備編程技能,編寫復雜的選擇器和腳本維護成本高昂。Midscene.js 通過自然語言接口革新了這一領域,用戶只需描述任務(如“點擊登錄按鈕”或“提取產品價格”),AI 即可自動執行,大幅降低技術門檻。
Midscene.js 由 web-infra-dev 團隊開發,開源于 GitHub(GitHub - web-infra-dev/midscene),采用 MIT 許可。它支持多種 AI 模型,集成 Puppeteer 和 Playwright,提供豐富的功能。本文基于官網(Midscene.js)內容,全面解析其功能、模型、案例、技術細節和優化建議,為開發者提供詳盡指南。
二、安裝與配置
2.1. npm 安裝
Midscene.js 提供兩個包:
@midscene/web
:支持瀏覽器自動化,集成 Puppeteer/Playwright。@midscene/core
:核心功能,適合輕量級場景。
npm install @midscene/web
配置 GPT-4o:
import { overrideAIConfig } from '@midscene/web';
// 配置 AI 模型和密鑰
overrideAIConfig({OPENAI_API_KEY: '你的密鑰', // OpenAI API 密鑰,用于訪問 GPT-4omodel: 'gpt-4o' // 默認使用 GPT-4o 模型
});
本地模型(如 UI-TARS):
// 配置本地運行的 UI-TARS 模型
overrideAIConfig({model: 'ui-tars', // 開源 UI 自動化模型endpoint: 'http://localhost:5000' // 本地服務地址
});
2.1.1.環境要求
建議 Node.js 18+,安裝時確保網絡暢通以拉取依賴。
2.2. Chrome 擴展
通過 Chrome Web Store 安裝,提供無代碼體驗,直接在網頁輸入指令即可運行。
2.2.1. 使用步驟
- 訪問 Chrome Web Store,點擊“添加至 Chrome”。
- 在瀏覽器右上角擴展圖標中輸入指令,如“提取頁面標題”。
2.3.環境變量與高級配置
支持環境變量:
MIDSCENE_OPENAI_API_KEY
:密鑰。MIDSCENE_LANGSMITH_DEBUG
:調試模式(1
)。MIDSCENE_CACHE
:啟用緩存(1
)。
運行時配置:
// 高級配置示例
overrideAIConfig({OPENAI_API_KEY: '你的密鑰', // 設置 API 密鑰model: 'gpt-4o', // 指定模型cache: true, // 啟用緩存以提升性能timeout: 20000, // 設置超時時間為 20 秒logLevel: 'verbose' // 設置詳細日志級別
});
2.2.1. 本地部署
運行 UI-TARS:
docker run -p 5000:5000 ui-tars:latest # 啟動 Docker 容器運行 UI-TARS 服務
配置:
// 配置本地模型連接
overrideAIConfig({model: 'ui-tars',endpoint: 'http://localhost:5000'
});
三、核心功能詳解
3.1. 自然語言交互(aiAction)
通過自然語言執行操作:
// 在搜索框輸入并點擊搜索按鈕
await agent.aiAction('在頁面頂部的搜索框中輸入 "JavaScript",然后點擊旁邊的搜索按鈕');
使用技巧
- 具體性:指令越詳細越好,如“點擊右上角的紅色按鈕”。
- 多步驟:支持連續操作:
// 完整登錄流程
await agent.aiAction('前往 https://example.com/login'); // 訪問登錄頁面
await agent.aiAction('在用戶名輸入框中輸入 "user123"'); // 輸入用戶名
await agent.aiAction('在密碼輸入框中輸入 "pass123"'); // 輸入密碼
await agent.aiAction('點擊 "登錄" 按鈕'); // 點擊登錄按鈕
3.2. 數據提取(aiQuery)
提取結構化數據:
// 提取頁面時間
const data = await agent.aiQuery({time: '頁面左上角顯示的日期和時間,格式為字符串' // 定義提取目標
});
console.log(data); // 輸出:{ time: "2025-03-22 10:00 AM" }
復雜提取:
// 提取產品信息
const product = await agent.aiQuery({name: '產品名稱,字符串格式', // 商品名稱price: '產品價格,數字格式', // 商品價格stock: '庫存狀態,布爾值' // 是否有貨
});
console.log(product); // 輸出:{ name: "iPhone 15", price: 999, stock: true }
動態提取
const item = 'Sauce Labs Onesie'; // 定義商品名稱變量
// 根據變量提取價格
const price = await agent.aiQuery({price: `"${item}" 的價格,數字格式` // 動態生成查詢條件
});
console.log(price); // 輸出:{ price: 7.99 }
3.3. 條件斷言(aiAssert)
驗證頁面狀態:
// 驗證價格是否正確
await agent.aiAssert('"Sauce Labs Onesie" 的價格是 7.99');
復雜條件:
// 檢查購物車狀態
await agent.aiAssert('購物車中的商品數量大于 3'); // 驗證數量
await agent.aiAssert('"結賬" 按鈕可見且可用'); // 驗證按鈕狀態
錯誤處理
try {// 執行斷言await agent.aiAssert('頁面顯示 "登錄成功"');
} catch (e) {console.error('斷言失敗:', e.message); // 輸出錯誤信息
}
3.4. 等待條件(aiWaitFor)
等待特定狀態:
// 等待加載完成
await agent.aiWaitFor('加載動畫不再可見', {timeout: 30000, // 設置超時為 30 秒interval: 5000 // 每 5 秒檢查一次
});
結合操作
// 等待并執行
await agent.aiWaitFor('搜索結果已加載'); // 確保結果加載完成
await agent.aiAction('點擊第一個搜索結果'); // 點擊第一個結果
3.5. YAML 腳本(runYaml)
批量任務:
steps:- action: '點擊登錄按鈕' # 點擊登錄按鈕- action: '在用戶名輸入框中輸入 "user123"' # 輸入用戶名- action: '在密碼輸入框中輸入 "pass123"' # 輸入密碼- action: '點擊提交按鈕' # 提交表單
執行:
// 運行 YAML 腳本
await agent.runYaml('path/to/script.yaml');
復雜腳本
steps:- action: '前往 https://shop.com' # 訪問電商網站- waitFor: '產品列表已加載' # 等待產品加載- query:products: '頁面所有產品名稱和價格,格式為數組 {name: string, price: number}' # 提取產品數據- assert: '"iPhone 15" 的價格低于 1000' # 驗證價格
3.6. 可視化調試
生成動畫和報告:
// 啟用 LangSmith 調試模式
process.env.MIDSCENE_LANGSMITH_DEBUG = '1'; // 設置環境變量以輸出詳細日志
await agent.aiAction('點擊 "注冊" 按鈕'); // 執行操作并生成調試報告
調試輸出
報告示例:
- “定位到 ID 為 signup 的按鈕”
- “點擊坐標 (x: 100, y: 50),耗時 300ms”
四、支持的 AI 模型
4.1. GPT-4o
- 特點:OpenAI 多模態模型,支持文本和圖像處理。
- 適用:通用自動化任務。
- 配置:
// 配置 GPT-4o 模型
overrideAIConfig({model: 'gpt-4o', // 模型名稱OPENAI_API_KEY: '你的密鑰' // OpenAI API 密鑰
});
- 限制:無法操作跨域 iframe 或 canvas。
4.2. UI-TARS
- 特點:開源,支持圖像識別和拖拽。
- 適用:復雜 UI 交互。
- 配置:
// 配置本地 UI-TARS 模型
overrideAIConfig({model: 'ui-tars', // 模型名稱endpoint: 'http://localhost:5000' // 本地服務地址
});
4.3. Qwen2.5-VL
- 特點:阿里云視覺語言模型,擅長圖像和文本混合處理。
- 適用:圖像相關任務。
- 配置:
// 配置本地 Qwen2.5-VL 模型
overrideAIConfig({model: 'qwen2.5-vl', // 模型名稱endpoint: 'http://localhost:6000' // 本地服務地址
});
模型對比
模型 | 優點 | 缺點 | 適用場景 |
---|---|---|---|
GPT-4o | 易用、多模態、性能穩定 | 云依賴、跨域限制 | 快速原型、通用任務 |
UI-TARS | 本地部署、支持拖拽、隱私友好 | 配置復雜 | 企業應用、復雜 UI |
Qwen2.5-VL | 視覺能力強、無跨域限制 | 部署需技術支持 | 圖像處理、隱私敏感任務 |
五、實際應用案例
5.1. 社交媒體自動化
自動發布 X 帖子:
// 自動發帖流程
await agent.aiAction('點擊 "發帖" 按鈕'); // 打開發帖框
await agent.aiAction('在文本框中輸入 "Midscene.js 真好用!"'); // 輸入內容
await agent.aiAction('點擊 "發布" 按鈕'); // 提交帖子
定時發帖:
const cron = require('node-cron');
// 每天凌晨定時發帖
cron.schedule('0 0 * * *', async () => {await agent.aiAction('發布一條帖子,內容為 "每日更新 by Midscene.js"'); // 定時發布
});
5.2. 數據收集
收集音樂會信息:
// 訪問網站并提取數據
await agent.aiAction('前往 https://concert-site.com'); // 打開音樂會網站
const concertData = await agent.aiQuery({event: '音樂會名稱', // 提取活動名稱date: '音樂會日期', // 提取活動日期location: '舉辦地點' // 提取活動地點
});
console.log(concertData); // 輸出結果
5.3. 測試與驗證
驗證電商價格:
// 檢查價格和按鈕狀態
await agent.aiAssert('"iPhone 15" 的價格低于 1000 美元'); // 驗證價格
await agent.aiAssert('"加入購物車" 按鈕可見且可用'); // 驗證按鈕狀態
5.4. 電商價格監控
監控產品價格:
async function monitorPrice() {// 訪問產品頁面await agent.aiAction('前往 https://shop.com/product/123');// 提取當前價格const priceData = await agent.aiQuery({price: '當前產品價格,數字格式' // 獲取價格});console.log(`當前價格: ${priceData.price}`);// 檢查價格是否低于閾值if (priceData.price < 800) {console.log('價格低于 800,發送通知!');// 可集成郵件通知}
}
// 每小時檢查一次
setInterval(monitorPrice, 60 * 60 * 1000);
保存歷史價格
const fs = require('fs');
// 保存價格歷史到 CSV 文件
async function savePriceHistory() {const priceData = await agent.aiQuery({price: '當前產品價格,數字格式' // 提取價格});const record = `${new Date().toISOString()},${priceData.price}\n`; // 格式化記錄fs.appendFileSync('price_history.csv', record); // 追加到文件
}
setInterval(savePriceHistory, 60 * 60 * 1000); // 每小時執行
5.5. 表單自動填寫
自動填寫注冊表單:
async function autoFillForm() {// 訪問注冊頁面await agent.aiAction('前往 https://example.com/register');// 等待表單加載await agent.aiWaitFor('注冊表單已加載'); // 確保表單可見// 填寫表單字段await agent.aiAction('在用戶名輸入框中輸入 "testuser"'); // 輸入用戶名await agent.aiAction('在郵箱輸入框中輸入 "test@example.com"'); // 輸入郵箱await agent.aiAction('在密碼輸入框中輸入 "Password123"'); // 輸入密碼// 提交表單await agent.aiAction('點擊 "提交" 按鈕'); // 提交表單// 驗證結果await agent.aiAssert('頁面顯示 "注冊成功"'); // 檢查注冊是否成功
}
autoFillForm().catch(console.error);
批量填寫
const users = [{ username: 'user1', email: 'user1@example.com', password: 'Pass1' },{ username: 'user2', email: 'user2@example.com', password: 'Pass2' }
];
// 批量注冊多個用戶
for (const user of users) {await agent.aiAction(`在用戶名輸入框中輸入 "${user.username}"`); // 輸入用戶名await agent.aiAction(`在郵箱輸入框中輸入 "${user.email}"`); // 輸入郵箱await agent.aiAction(`在密碼輸入框中輸入 "${user.password}"`); // 輸入密碼await agent.aiAction('點擊 "提交" 按鈕'); // 提交表單
}
5.6. 動態網頁抓取
抓取動態加載內容:
async function scrapeDynamicContent() {// 訪問動態網頁await agent.aiAction('前往 https://dynamic-site.com');// 等待內容加載await agent.aiWaitFor('動態內容已加載', { timeout: 60000 }); // 等待 60 秒// 提取文章標題和摘要const articles = await agent.aiQuery({articles: '頁面所有文章標題和摘要,格式為數組 {title: string, summary: string}' // 提取動態數據});console.log(articles);// 保存到 JSON 文件fs.writeFileSync('articles.json', JSON.stringify(articles, null, 2)); // 寫入文件
}
scrapeDynamicContent().catch(console.error);
處理分頁
async function scrapeAllPages() {let page = 1;const allArticles = [];while (true) {// 等待當前頁面加載await agent.aiWaitFor(`第 ${page} 頁內容已加載`); // 確保頁面加載完成// 提取數據const articles = await agent.aiQuery({articles: '當前頁面所有文章標題,格式為數組 {title: string}' // 提取標題});allArticles.push(...articles.articles); // 追加到總數組// 檢查是否有下一頁const hasNext = await agent.aiQuery({hasNext: '是否存在 "下一頁" 按鈕,布爾值' // 檢查分頁按鈕});if (!hasNext.hasNext) break; // 無下一頁則退出// 點擊下一頁await agent.aiAction('點擊 "下一頁" 按鈕'); // 翻頁page++;}console.log(`共抓取 ${allArticles.length} 篇文章`); // 輸出總數
}
scrapeAllPages();
5.7. 自動化客服
模擬客服回復:
async function autoReply() {// 訪問客服頁面await agent.aiAction('前往 https://support.com/chat');// 等待新消息await agent.aiWaitFor('新客戶消息出現'); // 等待消息加載// 獲取消息內容const message = await agent.aiQuery({message: '最新客戶消息,字符串格式' // 提取最新消息});// 根據消息內容回復if (message.message.includes('價格')) {await agent.aiAction('在回復框中輸入 "我們的價格請查看官網"'); // 回復價格問題} else {await agent.aiAction('在回復框中輸入 "請稍等,我為您查詢"'); // 默認回復}await agent.aiAction('點擊 "發送" 按鈕'); // 發送回復
}
// 每分鐘檢查一次
setInterval(autoReply, 60 * 1000);
多語言支持
async function replyInLanguage(lang) {const message = await agent.aiQuery({message: '最新客戶消息,字符串格式' // 獲取最新消息});// 根據語言回復if (lang === 'zh') {await agent.aiAction(`在回復框中輸入 "感謝您的消息,請稍等"`);} else {await agent.aiAction(`在回復框中輸入 "Thank you for your message, please wait"`);}await agent.aiAction('點擊 "發送" 按鈕'); // 發送回復
}
replyInLanguage('zh'); // 中文回復
六、技術細節與集成
Puppeteer 集成
const puppeteer = require('puppeteer');
const { Midscene } = require('@midscene/web');
// 啟動瀏覽器
const browser = await puppeteer.launch({ headless: true }); // 無頭模式運行
const page = await browser.newPage();
const agent = new Midscene(page); // 創建 Midscene 實例
// 執行操作
await agent.aiAction('前往 https://example.com'); // 訪問頁面
await agent.aiAction('點擊 "注冊" 按鈕'); // 點擊注冊按鈕
自定義配置
// 配置代理和無頭模式
const browser = await puppeteer.launch({headless: true, // 無頭模式args: ['--proxy-server=http://proxy:8080'] // 設置代理
});
緩存機制
提升性能:
// 啟用默認內存緩存
overrideAIConfig({ cache: true });
// 自定義文件緩存
const fs = require('fs');
overrideAIConfig({cache: {get: (key) => fs.readFileSync(`cache/${key}.json`, 'utf8'), // 從文件讀取緩存set: (key, value) => fs.writeFileSync(`cache/${key}.json`, value) // 保存到文件}
});
緩存清理
// 每天清理緩存
setInterval(() => {fs.rmSync('cache', { recursive: true, force: true }); // 刪除緩存目錄console.log('緩存已清理');
}, 24 * 60 * 60 * 1000); // 每天執行一次
數據隱私
本地部署 Qwen2.5-VL:
docker run -p 6000:6000 qwen2.5-vl:latest # 啟動 Qwen2.5-VL 服務
配置:
// 使用本地模型保護隱私
overrideAIConfig({model: 'qwen2.5-vl',endpoint: 'http://localhost:6000'
});
隱私驗證
// 測試數據是否外泄
await agent.aiAction('在輸入框中輸入 "敏感數據"');
console.log('檢查本地服務日志,確保數據未上傳云端');
七、限制與優化
限制
- 交互類型:僅支持點擊、輸入、滾動等,拖拽限于 UI-TARS。
- AI 穩定性:自然語言解析可能出錯。
- 跨域限制:GPT-4o 無法操作跨域 iframe。
優化建議
- 提示優化:用“點擊藍色提交按鈕”代替“點擊按鈕”。
- 重試機制:
async function retryAction(action, retries = 3) {for (let i = 0; i < retries; i++) {try {// 執行操作await agent.aiAction(action);return;} catch (e) {console.warn(`第 ${i + 1}/${retries} 次重試: ${e.message}`); // 輸出重試信息await new Promise(resolve => setTimeout(resolve, 1000)); // 等待 1 秒}}throw new Error('操作失敗'); // 重試失敗拋出錯誤
}
// 重試點擊操作
await retryAction('點擊 "提交" 按鈕');
性能監控
// 記錄執行時間
const start = Date.now();
await agent.aiAction('點擊 "搜索" 按鈕');
console.log(`執行時間: ${Date.now() - start}ms`); // 輸出耗時
并行處理
// 并行執行多個任務
const tasks = [agent.aiAction('點擊 "產品" 菜單'),agent.aiAction('點擊 "關于我們" 菜單')
];
await Promise.all(tasks); // 同時執行多個操作
八、社區生態與貢獻
Midscene.js 的開源特性促成了活躍社區:
- 示例:X 發帖、數據收集(見 GitHub)。
- 問題跟蹤:通過 GitHub Issues 提交 bug。
- 貢獻:添加新模型或功能。
貢獻步驟
- Fork 倉庫。
- 修改代碼,例如添加模型:
// 添加自定義模型
overrideAIConfig({model: 'my-model',endpoint: 'http://my-server:8000'
});
- 提交 Pull Request。
社區案例
用戶貢獻的 YAML 示例:
steps:- action: '前往 https://news.com' # 訪問新聞網站- query:headlines: '頭條新聞標題,格式為數組 {title: string}' # 提取頭條
九、參考資料
- Midscene.js 官網
- GitHub 倉庫
- API 文檔
- 模型選擇