Midscene.js + Playwright:视觉AI驱动的智能浏览器自动化实战

📅 2026/7/3 8:23:17
Midscene.js + Playwright:视觉AI驱动的智能浏览器自动化实战
1. 项目概述当视觉AI遇上浏览器自动化最近在折腾一个电商数据抓取的项目传统的基于CSS选择器或XPath的自动化脚本在面对页面结构频繁变动、动态加载内容时简直是一场噩梦。一个微小的前端改动就能让精心编写的脚本全军覆没。就在我为此头疼不已时我注意到了两个名字Midscene.js和Playwright。前者是一个基于视觉和自然语言理解的AI驱动自动化框架后者是微软推出的现代浏览器自动化库。当我把它们俩结合起来用的时候感觉像是给自动化脚本装上了“眼睛”和“大脑”——脚本不再需要精确的DOM路径只需要告诉它“点击那个蓝色的登录按钮”或者“找到价格低于1000元的商品”它就能自己理解页面、定位元素并执行操作。这不仅仅是工具的组合更是一种从“精准定位”到“智能决策”的自动化范式突破。无论你是做UI测试、RPA流程自动化还是复杂的数据抓取这套组合拳都能显著提升脚本的健壮性和开发效率。接下来我就结合自己的踩坑经验带你从零开始深入理解如何将Midscene.js无缝集成到Playwright中并构建出真正智能、抗变化的自动化工作流。2. 核心思路拆解为什么是Midscene.js Playwright在深入代码之前我们得先搞清楚为什么这两个工具的结合能产生“112”的效果。这背后是对传统自动化痛点的一次精准打击。2.1 传统自动化框架的“阿喀琉斯之踵”无论是Selenium、Puppeteer还是Playwright本身其核心操作模式都是基于程序化定位。你需要告诉浏览器“去找到这个id‘submit-btn’的按钮然后点击它。”这种方式在静态、稳定的页面上非常高效。然而现代Web应用大量使用JavaScript动态渲染、组件化框架如React, Vue导致DOM结构不稳定。今天按钮的ID是submit-btn明天可能就变成了>mkdir midscene-playwright-demo cd midscene-playwright-demo npm init -y3.2 方式一独立脚本模式集成适合数据抓取/自动化任务这种方式最直接适合编写独立的自动化脚本。第一步安装依赖我们需要安装Playwright核心库、Midscene的Web集成包、以及一个TypeScript运行器这里用tsx。npm install playwright midscene/web tsx dotenv --save-dev # 安装Playwright的Chromium浏览器国内用户可换源加速 npx playwright install chromium国内网络优化技巧Playwright安装浏览器可能很慢。可以使用淘宝镜像加速这是非常实用的一个技巧# 设置环境变量使用国内镜像源 PLAYWRIGHT_DOWNLOAD_HOSThttps://npmmirror.com/mirrors/playwright npx playwright install chromium # 或者只安装必需的Chromium跳过Firefox和WebKit以节省时间 npx playwright install --with-deps chromium第二步配置AI模型Midscene.js本身不提供AI模型它需要连接一个支持视觉理解的大语言模型服务。常见的选择有OpenAI的GPT-4V、Anthropic的Claude 3系列、或开源的Qwen-VL等。你需要准备相应的API Key和基础URL。我强烈建议使用.env文件来管理这些敏感配置避免硬编码在代码中。在项目根目录创建.env文件MIDSCENE_MODEL_BASE_URLhttps://api.openai.com/v1 MIDSCENE_MODEL_API_KEYsk-your-openai-api-key-here MIDSCENE_MODEL_NAMEgpt-4-vision-preview # 或 gpt-4o MIDSCENE_MODEL_FAMILYopenaiMIDSCENE_MODEL_BASE_URL: 你的模型服务地址。对于OpenAI就是https://api.openai.com/v1如果你使用Azure OpenAI或本地部署的Ollama则需要修改为此。MIDSCENE_MODEL_API_KEY: 对应服务的API Key。MIDSCENE_MODEL_NAME: 具体的模型名称如gpt-4-vision-preview。MIDSCENE_MODEL_FAMILY: 模型系列用于适配不同的API格式。支持openai,claude,gemini,qwen等。重要提醒模型调用会产生费用。在开发调试阶段可以先使用截图和规划缓存功能Midscene支持来减少不必要的API调用等逻辑稳定后再进行全流程测试。第三步编写你的第一个智能脚本创建一个demo.ts文件我们来模拟在eBay上搜索耳机的流程。import { chromium } from playwright; import { PlaywrightAgent } from midscene/web/playwright; import dotenv/config; // 读取.env文件中的环境变量 // 一个简单的睡眠函数用于等待页面初始加载 const sleep (ms: number) new Promise((r) setTimeout(r, ms)); (async () { // 1. 启动浏览器 const browser await chromium.launch({ headless: false, // 开发时设为false可以看到浏览器操作便于调试 args: [--no-sandbox, --disable-setuid-sandbox], // 适用于Linux/Docker环境 }); // 2. 创建新页面 const page await browser.newPage(); // 设置一个合适的视口大小这对视觉定位很重要 await page.setViewportSize({ width: 1280, height: 768 }); // 3. 导航到目标网站 await page.goto(https://www.ebay.com); console.log(页面加载完成等待5秒让动态内容稳定...); await sleep(5000); // 等待JS渲染和初始网络请求 // 4. 初始化Midscene Agent这是智能的核心 const agent new PlaywrightAgent(page); console.log(Midscene Agent 初始化成功); try { // 5. 核心操作用自然语言驱动 // 告诉AI“在搜索框输入‘Headphones’并按回车” console.log(正在执行搜索...); await agent.aiAct(type Headphones in search box, hit Enter); console.log(搜索指令执行完毕); // 6. 等待结果用自然语言描述等待的状态 console.log(等待搜索结果加载...); await agent.aiWaitFor(there is at least one headphone item on page, { timeoutMs: 10000 }); // 也可以用传统的sleep但aiWaitFor更智能它会持续检查直到条件满足或超时 // await sleep(5000); // 7. 信息提取让AI理解页面内容并结构化返回 console.log(提取商品信息...); const items await agent.aiQueryArray{ itemTitle: string; price: number }( {itemTitle: string, price: Number}[], find item in list and corresponding price ); console.log(在售耳机:, JSON.stringify(items, null, 2)); // 8. 条件判断与断言 const isExpensive await agent.aiBoolean(Is there any headphone priced over 1000 dollars?); console.log(是否存在价格超过1000美元的耳机: ${isExpensive}); const firstItemPrice await agent.aiNumber(What is the price of the first headphone?); console.log(第一个耳机的价格: ${firstItemPrice}); const firstItemName await agent.aiString(What is the name of the first headphone?); console.log(第一个耳机的名称: ${firstItemName}); // 9. 视觉定位获取元素在屏幕上的位置可用于后续复杂操作 const location await agent.aiLocate(the Buy It Now button for the first item); if (location) { console.log(“Buy It Now”按钮位置: ${JSON.stringify(location)}); // 你可以用这个位置信息做更多事情比如用Playwright原生API点击 // await page.mouse.click(location.center.x, location.center.y); } // 10. AI断言验证页面是否符合预期 await agent.aiAssert(There is a category filter on the left side of the page); console.log(左侧类目筛选器验证通过); // 11. 执行点击操作 console.log(点击第一个商品查看详情...); await agent.aiTap(the first item in the list); await sleep(3000); // 等待详情页加载 } catch (error) { console.error(自动化过程出现错误:, error); } finally { // 12. 关闭浏览器 await browser.close(); console.log(浏览器已关闭任务完成。); // 控制台会输出报告文件路径例如Midscene - report file updated: /path/to/report/xxx.html } })();第四步运行与查看报告使用tsx运行这个TypeScript脚本npx tsx demo.ts脚本执行过程中你会在终端看到详细的日志。执行结束后控制台会输出一行关键信息Midscene - report file updated: /path/to/your/project/midscene_run/report/xxxx.html。用浏览器打开这个HTML文件你会得到一个极其详细的执行报告。这个报告是Midscene的一大亮点它包含了每一步操作前后的页面截图、AI的规划步骤、定位到的元素高亮、以及所有提取的数据。这对于调试和验证自动化流程是否按预期工作至关重要。3.3 方式二集成到Playwright Test测试框架如果你已经有一个Playwright测试项目或者打算构建一个正式的E2E测试套件那么这种集成方式更规范。第一步在现有项目中添加依赖和配置假设你已有Playwright测试项目结构通常由npx playwright init生成。安装Midscene包npm install midscene/web --save-dev修改playwright.config.ts配置文件添加Midscene的Reporter和适当超时import { defineConfig, devices } from playwright/test; export default defineConfig({ testDir: ./tests, timeout: 120000, // 整体测试超时时间AI操作可能较慢建议设置长一些 expect: { timeout: 10000 }, // 断言超时 fullyParallel: true, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 1 : undefined, reporter: [ [list], // 控制台列表输出 [html], // Playwright自带的HTML报告 [midscene/web/playwright-reporter, { type: merged }] // Midscene可视化报告 ], use: { baseURL: http://localhost:3000, // 你的应用地址 trace: on-first-retry, screenshot: only-on-failure, }, projects: [ { name: chromium, use: { ...devices[Desktop Chrome] }, }, ], });Reporter配置说明type: merged表示将所有测试用例的Midscene报告合并成一个文件。如果报告文件太大导致打开慢可以设置为type: separate为每个用例生成独立报告或使用outputFormat: html-and-external-assets将图片外置。第二步创建自定义Fixture扩展test对象在tests目录下创建fixtures.ts文件将Midscene的AI能力注入到Playwright的test对象中。// tests/fixtures.ts import { test as base } from playwright/test; import type { PlayWrightAiFixtureType } from midscene/web/playwright; import { PlaywrightAiFixture } from midscene/web/playwright; // 使用Midscene提供的Fixture扩展基础的test对象 export const test base.extendPlayWrightAiFixtureType( PlaywrightAiFixture({ // 可选配置交互后等待网络空闲的超时时间(ms)设为0则禁用 waitForNetworkIdleTimeout: 3000, // 可选配置aiAct操作失败后的重规划次数上限 replanningCycleLimit: 20, // 可以在这里统一配置Agent的模型参数会覆盖环境变量 // modelConfig: { ... } }) ); export { expect } from playwright/test; // 重新导出expect第三步编写AI驱动的测试用例现在你可以在测试用例中使用ai系列方法了。创建一个测试文件ebay-search.spec.ts。// tests/ebay-search.spec.ts import { test, expect } from ./fixtures; // 导入我们扩展过的test test.describe(eBay商品搜索AI测试, () { test.beforeEach(async ({ page }) { // 设置移动端视图测试响应式 await page.setViewportSize({ width: 390, height: 844 }); await page.goto(https://www.ebay.com); await page.waitForLoadState(networkidle); }); test(应能通过自然语言搜索耳机并验证结果, async ({ aiInput, aiTap, aiWaitFor, aiQuery, aiAssert, aiScroll, recordToReport, page }) { // 1. AI输入搜索词 // 无需知道搜索框的选择器直接用自然语言描述 await aiInput(Headphones, 搜索框); // 2. AI点击搜索按钮 await aiTap(搜索按钮); // 3. AI等待特定内容出现 await aiWaitFor(至少有一个耳机商品项显示在页面上, { timeoutMs: 15000 }); // 4. AI滚动页面直到底部 await aiScroll({ scrollType: untilBottom }, 商品列表区域); // 5. AI提取结构化数据 const searchResults await aiQueryArray{ title: string; price: number; shipping?: string }( 提取当前页面所有耳机商品的标题、价格和运费信息组成一个数组 ); console.log(搜索到的商品:, searchResults); // 使用Playwright原生断言验证业务逻辑 expect(searchResults?.length).toBeGreaterThan(0); expect(searchResults![0].price).toBeGreaterThan(0); // 6. AI验证页面元素存在 await aiAssert(页面顶部有面包屑导航); await aiAssert(存在价格排序筛选器); // 7. 记录当前状态到报告方便后续查看 await recordToReport(搜索结果页最终状态, { content: 共找到 ${searchResults?.length} 个商品首个商品价格: ${searchResults![0].price} }); }); test(应能处理下拉筛选等复杂交互, async ({ aiTap, aiWaitFor, aiSelect, page }) { // 此测试演示处理原生select下拉框 // 先导航到某个有筛选功能的品类页 await page.goto(https://www.ebay.com/b/Electronics/bn_7000259124); await aiWaitFor(页面加载完成); // 点击“排序方式”下拉框 - Midscene默认会处理原生select的渲染问题 await aiTap(排序方式下拉框); await aiWaitFor(“价格从高到低”选项可见); // 选择特定选项 await aiSelect(价格从高到低, 排序方式下拉列表); // 等待排序结果刷新 await aiWaitFor(商品列表已按价格降序排列, { timeoutMs: 10000 }); await recordToReport(排序后页面); }); });第四步运行测试并查看双份报告运行测试npx playwright test tests/ebay-search.spec.ts --headed # 有头模式方便观察 # 或 npx playwright test --ui # 使用Playwright UI模式运行结束后你会得到两份报告Playwright原生HTML报告(playwright-report/index.html)包含测试通过率、时间线、追踪信息。Midscene可视化报告(midscene_run/report/xxx.html)这是精华所在它以“视觉故事”的形式展示了AI如何一步步理解页面、执行操作。每一步都有截图、高亮定位和AI的“思考过程”对于调试“为什么AI点错了”或者“为什么没找到元素”这类问题有不可替代的价值。4. 高级特性与实战避坑指南掌握了基础集成后我们来看看一些高级用法和实际开发中必然会遇到的“坑”。4.1 连接远程浏览器与CDP调试有时你需要连接到一个已存在的浏览器实例进行调试或者在你的CI/CD环境如Docker容器中运行的浏览器。Playwright可以通过Chrome DevTools Protocol (CDP)连接远程浏览器Midscene也能完美适配。import { chromium } from playwright; import { PlaywrightAgent } from midscene/web/playwright; async function connectToRemoteBrowser() { // 远程浏览器的CDP WebSocket地址 // 来源可以是Browserless服务、本地以--remote-debugging-port启动的Chrome、Docker容器等 const cdpWsUrl ws://localhost:9222/devtools/browser/abc123def; // 1. 通过CDP连接浏览器 const browser await chromium.connectOverCDP(cdpWsUrl); // 通常连接到第一个上下文和页面 const context browser.contexts()[0]; const page context.pages()[0] || await context.newPage(); // 2. 创建Midscene Agent用法与本地完全一致 const agent new PlaywrightAgent(page, { // 可在此配置Agent参数 waitForNetworkIdleTimeout: 2000, }); // 3. 开始你的AI自动化 await page.goto(https://example.com); await agent.aiAct(click the login link); // ... 其他操作 // 4. 清理 await agent.destroy(); await browser.close(); }应用场景调试已打开的浏览器手动打开Chrome并启用远程调试然后用脚本连接上去操作方便调试。云浏览器服务使用BrowserStack、LambdaTest或自建的browserless服务。Docker化执行在Docker容器中启动浏览器宿主机运行测试脚本进行连接。4.2 扩展自定义交互动作Midscene内置了click,type,scroll等通用动作但有时你需要更复杂的操作比如“双击”、“拖拽”、“连续点击5次”。这时可以通过customActions进行扩展。import { getMidsceneLocationSchema, z } from midscene/core; import { defineAction } from midscene/core/device; import { PlaywrightAgent } from midscene/web/playwright; // 1. 定义自定义动作连续点击 const ContinuousClick defineAction({ name: continuousClick, description: 对同一目标进行多次连续点击, paramSchema: z.object({ locate: getMidsceneLocationSchema(), // 接收AI定位到的元素位置 count: z.number().int().positive().describe(点击次数), }), async call(param, context) { const { locate, count } param; const { page } context; // 从上下文中获取Playwright的page对象 const { center } locate; console.log(将在位置(${center.x}, ${center.y})连续点击${count}次); for (let i 0; i count; i) { await page.mouse.click(center.x, center.y); await page.waitForTimeout(200); // 每次点击间隔200毫秒 } }, }); // 2. 创建Agent时注入自定义动作 const agent new PlaywrightAgent(page, { customActions: [ContinuousClick], }); // 3. 在AI指令中直接使用 await agent.aiAct(click the refresh button 3 times quickly); // AI在规划时如果识别出“click ... N times”的语义可能会选择调用我们定义的continuousClick动作。通过这种方式你可以将任何复杂的、业务特定的交互封装成AI可调用的动作极大地扩展了自动化的边界。4.3 常见问题与排查技巧实录在实际使用中我遇到了不少问题这里总结出最典型的几个及其解决方案。问题1下拉框select点击后没反应报告里也看不到下拉选项。根因浏览器对原生select元素的下拉面板是调用操作系统原生控件渲染的这部分内容不在网页DOM内Playwright无法截图AI自然“看”不到。解决方案Midscene的PlaywrightAgent默认开启了forceChromeSelectRendering: true选项。它会强制Chrome使用自定义样式渲染下拉框使其出现在页面截图内。如果你发现下拉框样式变了但能正常操作那就是这个功能生效了。如果因某些原因需要禁用可以显式设置forceChromeSelectRendering: false但你可能需要寻找其他替代方案如通过page.select()直接设置值。问题2运行npx playwright install下载浏览器极慢甚至失败。解决方案使用国内镜像源这是最有效的方法。PLAYWRIGHT_DOWNLOAD_HOSThttps://npmmirror.com/mirrors/playwright npx playwright install chromium备选方案如果只需要Chromium可以只安装它跳过Firefox和WebKit。npx playwright install --with-deps chromium问题3执行page.screenshot()或Midscene截图时控制台卡住并报错Timeout ... exceeded ... waiting for fonts to load。根因这是Playwright的一个已知行为截图时会等待网页字体加载完成。在某些无头环境或网络受限的CI中字体可能永远加载不完。解决方案设置环境变量PW_TEST_SCREENSHOT_NO_FONTS_READY1告诉Playwright截图时不要等待字体。PW_TEST_SCREENSHOT_NO_FONTS_READY1 npx playwright test或者在playwright.config.ts中配置use: { // ... 其他配置 launchOptions: { env: { PW_TEST_SCREENSHOT_NO_FONTS_READY: 1 } } }问题4AI执行动作后页面状态不符合预期或者AI“找不到”元素。排查步骤首先查看Midscene报告这是最重要的调试工具。报告里会展示AI执行每一步前的页面截图、它“认为”应该操作的位置高亮显示、以及它的“规划步骤”思考过程。如果高亮位置错了说明AI理解有偏差。优化指令指令要清晰、无歧义。“点击那个按钮”不如“点击蓝色的、写着‘提交’的按钮”。如果页面上有多个相似元素加上相对位置描述如“点击搜索框右侧的放大镜图标按钮”。增加等待在关键操作如导航、提交表单后使用aiWaitFor明确等待某个标志性元素出现而不是用固定的sleep。检查视口ViewportAI是基于截图工作的。如果视口设置太小某些元素可能不在可视区域内。确保page.setViewportSize设置合理。启用headless: false模式亲眼观察自动化过程能直观发现很多问题。问题5AI操作耗时很长API调用费用高。优化策略利用缓存Midscene支持对AI规划plan和定位locate结果进行缓存。在开发阶段相同的页面和指令不会重复调用昂贵的视觉模型。确保项目目录有写入权限缓存会自动生效。精简指令将复杂操作拆分成多个aiAct不一定好。有时一个清晰的复合指令如“在顶部的搜索框输入‘手机’然后点击旁边的搜索按钮”比先aiInput再aiTap效率更高因为AI可以一次规划多个连贯动作。混合模式对于稳定不变的导航栏、登录框等元素可以仍用Playwright原生的page.locator(‘...’).click()来操作速度快且零成本。只对动态性强、易变的部分使用AI驱动。这种“传统自动化AI补丁”的模式是平衡成本与稳定性的实用策略。5. 架构思考从自动化脚本到智能体Agent将Midscene.js与Playwright结合我们实际上是在构建一个初级的Web交互智能体。这个智能体具备“感知环境截图”、“理解任务自然语言”、“规划步骤AI分解”、“执行动作Playwright”、“评估结果断言与重试”的基本能力。这种架构带来的深远影响在于测试用例的编写不再是开发者的专属产品、运营人员可以用自然语言描述测试场景由智能体自动转化为可执行的测试脚本极大降低了自动化测试的门槛。维护成本转移当UI发生变化时不再需要工程师逐一更新成千上万的选择器。只要AI模型能理解新的界面大部分脚本就能继续工作。维护的重点变成了优化提示词和提供更高质量的测试数据。探索性测试与监控你可以让智能体基于一个目标如“找出网站中所有无法点击的按钮”去自主探索发现人工难以察觉的可用性问题。当然目前这套方案也并非银弹。它对AI模型的视觉理解能力依赖很强API调用有成本和延迟对于极端复杂的交互逻辑如游戏、Canvas绘图可能力有不逮。但在处理大量业务表单、数据列表、内容管理系统、电商网站等标准Web交互场景时它的效率提升是革命性的。我的建议是不要试图用AI自动化完全替换所有传统E2E测试而是将其作为一把“瑞士军刀”用于那些变化频繁、选择器脆弱、或逻辑简单但用例量大的场景。将它与基于组件测试如Testing Library和API测试组成的测试金字塔结合起来才能构建出最健壮、最可维护的现代软件质量保障体系。