浏览器自动化技术全景:Playwright + Midscene.js 核心原理与实战方案
浏览器自动化技术全景:Playwright + Midscene.js 核心原理与实战方案
目录
-
1.1 Playwright 底层架构与通信机制
-
1.2 Playwright 三大核心机制(稳 / 快关键)
-
1.3 BrowserContext 隔离原理
-
1.4 CDP 协议(Chrome DevTools Protocol)核心
-
1.5 Midscene.js 纯视觉驱动原理
-
1.6 WebSocket 为何成为 CDP 默认协议
-
2.1 Playwright 常用 API 速查表
-
2.2 CDP 常用命令速查表
-
2.3 Midscene.js 脚本生成与执行
-
2.4 多账号 / 多会话隔离方案
-
2.5 脚本速度优化方案
-
2.6 脚本失败自动自愈方案
-
2.7 验证码处理方案
-
2.8 自然语言生成自动化脚本方案
一、核心技术原理
1.1 Playwright 底层架构与通信机制
Playwright 是微软开源的跨浏览器自动化工具,核心架构为「三层架构 + WebSocket 长连接」,实现高效、稳定的自动化控制。
架构图
┌─────────────────────────────────────────────────────────────┐
│ 你的业务代码 │
│ (Python / JS / Java / C# 任意语言) │
└───────────────────────────┬─────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Playwright Client SDK │
│ - 封装 API:click / fill / goto / locator 等 │
│ - 实现:自动等待、重试、可交互判断、超时管理 │
└───────────────────────────┬─────────────────────────────────┘
│
│ WebSocket 长连接(双向通信、事件推送)
▼
┌─────────────────────────────────────────────────────────────┐
│ Playwright Driver (Node.js 核心) │
│ - 多语言桥接层 │
│ - 协议转换:统一封装成浏览器可识别的指令 │
│ - 事件分发、日志、追踪、调试能力 │
└───────────────────────────┬─────────────────────────────────┘
│
┌───────────────┴───────────────┬───────────────┐
│ │ │
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Chromium │ │ Firefox │ │ WebKit │
│ 使用 CDP 协议 │ │ 自研调试协议 │ │ 自研调试协议 │
└────────┬─────────┘ └──────────────────┘ └──────────────────┘
│
│ Chrome DevTools Protocol (CDP) - WebSocket + JSON-RPC
▼
┌─────────────────────────────────────────────────────────────┐
│ 浏览器内核进程 │
│ (Renderer / Network / DOM / V8 / Input 等子系统) │
└─────────────────────────────────────────────────────────────┘
核心通信链路
你的代码 → Playwright SDK(自动等待)→ Driver(协议转换)→ 浏览器(CDP/自研协议)→ 内核执行
→ 浏览器主动推送事件 → Driver 分发 → SDK 接收 → 业务代码继续执行
1.2 Playwright 三大核心机制(稳 / 快关键)
1.2.1 自动等待(Auto-Wait)
- 实现原理:客户端判断 + 轻量状态获取 + 智能重试(非轮询)
-
调用
click/fill等操作时,SDK 通过 CDP 向浏览器查询元素完整状态(是否存在、可见、可交互、无遮挡、无动画); -
判断逻辑在客户端执行,不满足则极短间隔(几十 ms)再次查询;
-
满足条件立即执行,超时抛出可解读错误。
- 核心价值:无需手动写
sleep/wait,稳定性大幅提升。
1.2.2 事件驱动
- 实现原理:WebSocket 长连接 + 浏览器主动推送事件
-
Playwright 启动时与浏览器建立持久 WebSocket 连接;
-
客户端提前订阅事件(DOM 变化、网络请求、页面加载完成等);
-
浏览器事件发生时主动推送至客户端,触发回调 / 继续执行。
- 核心价值:无轮询开销,速度比 Selenium(HTTP 短连接 + 轮询)快 3~10 倍。
1.2.3 隔离上下文(BrowserContext)
- 实现原理:浏览器原生多分区存储 + 上下文 ID 隔离
-
一个 Browser 进程可创建多个 BrowserContext,每个 Context 对应独立的
StoragePartition; -
独立资源包括:Cookie Jar、LocalStorage/SessionStorage、缓存分区、代理、权限、UA;
-
底层依赖浏览器原生能力(Chromium 用
BrowserContextID + StoragePartition,Firefox/WebKit 由 Playwright 做兼容层)。
- 核心价值:测试用例互不干扰,支持多账号、多场景并行。
1.3 BrowserContext 隔离原理与共享机制
隔离核心
-
本质:每个 BrowserContext 是独立的「浏览器账号分身」,数据物理隔离(跨 Context 无数据泄漏);
-
层级关系:
Browser → N 个 Context → 每个 Context 下 N 个 Page(Tab); -
Page 永远属于创建它的 Context,无法跨 Context 迁移。
数据共享方案(显式导出 / 导入)
- 共享 Cookie:
const cookies = await context1.cookies(); // 导出
await context2.addCookies(cookies); // 导入
- 共享 LocalStorage/SessionStorage:
// 导出
const localStorage = await page1.evaluate(() => JSON.stringify(localStorage));
// 导入
await page2.evaluate((data) => {
const ls = JSON.parse(data);
for (const k in ls) localStorage.setItem(k, ls\[k]);
}, localStorage);
- 共享用户数据目录(profile):
// 两个浏览器实例共享同一 profile(注意:不可多进程同时写)
const browser1 = await chromium.launch({ userDataDir: './my-profile' });
const browser2 = await chromium.launch({ userDataDir: './my-profile' });
- 共享网络配置(代理、权限等):创建 Context 时传入相同配置。
1.4 CDP 协议(Chrome DevTools Protocol)核心
什么是 CDP?
-
Chromium 官方提供的底层调试 / 控制协议,Chrome DevTools(F12)的核心通信协议;
-
基于 WebSocket + JSON-RPC,双向通信,按「域(Domain)」组织能力。
Playwright 与 CDP 的关系
-
Chromium 场景:Playwright 所有高层 API(如
click/fill)最终都转为 CDP 命令执行; -
Firefox/WebKit 场景:Playwright 提供兼容层,行为与 CDP 一致;
-
支持直接调用原生 CDP 命令(高级用法):
const cdp = await page.context().newCDPSession(page);
await cdp.send("Network.enable"); // 启用网络域
cdp.on("Network.requestWillBeSent", (e) => console.log("请求URL:", e.request.url));
CDP 常用域与命令
| 域 | 核心能力 | 示例命令 |
|---|---|---|
| Page | 页面控制 | Page.navigate、Page.captureScreenshot |
| Network | 网络拦截 / 监控 | Network.enable、Network.setRequestInterception |
| Runtime | JS 执行 | Runtime.evaluate、Runtime.callFunctionOn |
| DOM | DOM 操作 | DOM.querySelector、DOM.getBoxModel |
| Input | 模拟输入 / 点击 | Input.dispatchMouseEvent、Input.insertText |
| Emulation | 设备 / UA 模拟 | Emulation.setDeviceMetricsOverride |
| Performance | 性能监控 | Performance.getMetrics |
1.5 Midscene.js 纯视觉驱动原理
核心定位:纯视觉(截图)驱动
-
不依赖 XPath/CSS 选择器,通过「截图 + 视觉大模型(VLM)」识别元素、返回坐标,映射为操作;
-
DOM 仅用于数据提取,UI 操作完全基于视觉。
工作流程
-
设备层(Playwright/Puppeteer/ADB 等)捕获页面 / 屏幕截图(Base64 格式);
-
截图预处理(压缩、隐藏鼠标、简化上下文),减少 Token 消耗;
-
截图 + 指令传给 VLM(如 Qwen3-VL、Doubao-vision),返回目标元素坐标;
-
坐标映射为设备可执行动作(click/tap/fill)。
脚本生成方式
- 自然语言(自动规划):
await aiAct('打开淘宝,搜索“手机”,点击第一个商品,提取价格');
- JavaScript SDK(工作流风格):
const agent = new Midscene(page);
await agent.aiTap('登录按钮');
await agent.aiInput('用户名输入框', 'test@example.com');
const price = await agent.aiQuery('提取商品价格');
- YAML 配置(低代码):
web: { url: https://example.com }
tasks:
- name: 登录流程
flow:
- ai: 点击登录按钮
- aiInput: \[用户名输入框, test@example.com]
- aiAssert: 页面显示“欢迎回来”
- 零代码生成:Chrome 插件(桥接模式)、Web/Android/iOS Playground 可视化操作生成脚本。
1.6 WebSocket 为何成为 CDP 默认协议
核心原因
WebSocket 是浏览器唯一能满足 CDP 需求的协议:全双工 + 长连接 + 低延迟 + 事件推送。
核心优势
-
全双工通信:客户端与浏览器可同时收发数据,无需等待请求 - 响应;
-
长连接:一次握手后持续通信,无 TCP 握手和 HTTP 头冗余开销;
-
浏览器主动推送事件:CDP 需浏览器实时推送 DOM 变化、网络请求等事件,WebSocket 是唯一支持该能力的浏览器原生协议;
-
轻量高效:帧头仅 2~10 字节,远小于 HTTP 头(几百~几千字节);
-
跨语言 / 跨平台:所有主流语言(JS/Python/Java 等)均支持,无平台差异。
为何不选择其他协议?
-
HTTP:半双工、短连接,服务器无法主动推送,只能轮询(慢、耗资源);
-
TCP 原生:浏览器不暴露原生 TCP 接口,无跨平台标准,不安全;
-
gRPC/QUIC/WebRTC:gRPC 太重、QUIC 生态不成熟、WebRTC 适用于音视频,均不适合调试协议场景。
二、实战技术方案
2.1 Playwright 常用 API 速查表
1. 启动与基础配置
// 启动浏览器
const browser = await chromium.launch({ headless: false, args: \['--no-sandbox'] });
// 新建隔离上下文
const context = await browser.newContext({
userAgent: 'Mozilla/5.0...',
viewport: { width: 1280, height: 800 },
proxy: { server: 'http://127.0.0.1:8888' }
});
// 新建页面
const page = await context.newPage();
await page.goto('https://www.baidu.com'); // 访问页面
2. 元素操作(自动等待)
await page.click('selector'); // 点击
await page.fill('selector', '文本'); // 输入(清空后填充)
await page.type('selector', '文本'); // 模拟打字输入
await page.press('selector', 'Enter'); // 按键
await page.selectOption('selector', 'value'); // 下拉选择
await page.check('selector'); // 勾选复选框
await page.hover('selector'); // 悬停
3. 等待与断言
await page.waitForSelector('selector'); // 等待元素出现
await page.waitForLoadState('networkidle'); // 等待网络空闲
await page.waitForURL('\*\*/result'); // 等待URL匹配
await page.isVisible('selector'); // 判断元素可见
await page.isEnabled('selector'); // 判断元素可用
4. 数据提取
await page.title(); // 获取页面标题
await page.url(); // 获取当前URL
await page.textContent('selector'); // 获取文本内容
await page.getAttribute('selector', 'href'); // 获取属性
await page.inputValue('selector'); // 获取输入框值
5. 网络拦截与模拟
// 拦截请求并修改响应
await page.route('\*\*/api/\*', async (route) => {
await route.fulfill({ body: JSON.stringify({ code: 200 }) });
// await route.abort(); // 中止请求
// await route.continue(); // 继续请求
});
6. 多页面 / 弹窗处理
// 监听新页面(如点击新标签页链接)
const \[newPage] = await Promise.all(\[
context.waitForEvent('page'),
page.click('a\[target="\_blank"]')
]);
// 处理弹窗(alert/confirm)
page.on('dialog', async (dialog) => await dialog.accept());
7. 截图与导出
await page.screenshot({ path: 'result.png', fullPage: true }); // 全屏截图
await page.pdf({ path: 'page.pdf', format: 'A4' }); // 导出PDF
2.2 CDP 常用命令速查表
1. 页面控制(Page 域)
await cdp.send('Page.navigate', { url: 'https://example.com' }); // 跳转页面
await cdp.send('Page.reload'); // 刷新页面
const screenshot = await cdp.send('Page.captureScreenshot'); // 截图(Base64)
await cdp.send('Page.setViewport', { viewport: { width: 1280, height: 800 } }); // 设置视口
2. 网络控制(Network 域)
await cdp.send('Network.enable'); // 启用网络监听
await cdp.send('Network.setRequestInterception', { patterns: \[{ urlPattern: '\*\*/api/\*' }] }); // 拦截请求
const cookies = await cdp.send('Network.getCookies'); // 获取Cookie
await cdp.send('Network.setCookies', { cookies: \[{ name: 'key', value: 'val', domain: 'example.com' }] }); // 设置Cookie
3. JS 执行(Runtime 域)
const result = await cdp.send('Runtime.evaluate', { expression: 'document.title' }); // 执行JS
console.log('页面标题:', result.result.value);
4. DOM 操作(DOM 域)
const root = await cdp.send('DOM.getDocument'); // 获取文档根节点
const element = await cdp.send('DOM.querySelector', { nodeId: root.root.nodeId, selector: '#login-btn' }); // 查找元素
const boxModel = await cdp.send('DOM.getBoxModel', { nodeId: element.nodeId }); // 获取元素位置/宽高
5. 输入模拟(Input 域)
// 模拟点击(坐标x=100, y=200)
await cdp.send('Input.dispatchMouseEvent', {
type: 'click',
x: 100,
y: 200,
button: 'left'
});
// 模拟输入文本
await cdp.send('Input.insertText', { text: 'test' });
6. 设备模拟(Emulation 域)
// 模拟手机设备
await cdp.send('Emulation.setDeviceMetricsOverride', {
width: 375,
height: 667,
deviceScaleFactor: 2,
mobile: true
});
// 修改UA
await cdp.send('Emulation.setUserAgentOverride', { userAgent: 'Mozilla/5.0...' });
2.3 Midscene.js 脚本生成与执行
核心 API 示例
const { Midscene } = require('@midscene/core');
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
const agent = new Midscene(page, { cache: true }); // 启用缓存提速
// 1. 自然语言自动规划
await agent.aiAct('打开百度,搜索“Playwright”,点击第一条结果');
// 2. 即时操作(跳过规划,更快)
await agent.aiTap('登录按钮', { deepThink: false }); // 关闭深度思考提速
await agent.aiInput('用户名输入框', 'test@example.com');
await agent.aiInput('密码框', '123456');
await agent.aiTap('提交按钮');
// 3. 数据提取与断言
const price = await agent.aiQuery('提取商品价格,返回数字');
await agent.aiAssert('页面包含“登录成功”');
await browser.close();
})();
零代码脚本生成(Chrome 插件)
-
安装 Midscene Chrome 插件;
-
开启桥接模式,在浏览器中手动操作目标流程;
-
插件自动记录操作、截图、生成 JS/YAML 脚本;
-
导出脚本直接用于自动化执行。
2.4 多账号 / 多会话隔离方案
Playwright 最优实现(单浏览器多 Context)
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: false });
// 账号1:上下文1(独立Cookie/存储)
const ctx1 = await browser.newContext();
const page1 = await ctx1.newPage();
await page1.goto('https://example.com/login');
await page1.fill('#username', 'user1');
await page1.fill('#password', 'pass1');
await page1.click('#login');
// 账号2:上下文2(完全隔离)
const ctx2 = await browser.newContext();
const page2 = await ctx2.newPage();
await page2.goto('https://example.com/login');
await page2.fill('#username', 'user2');
await page2.fill('#password', 'pass2');
await page2.click('#login');
// 并行操作,互不干扰
await Promise.all(\[
page1.goto('https://example.com/account'),
page2.goto('https://example.com/account')
]);
await browser.close();
})();
优势对比 Selenium
-
Selenium 需启动多个浏览器实例(内存占用高、速度慢);
-
Playwright 单浏览器多 Context(内存低、速度快、隔离彻底)。
2.5 脚本速度优化方案
Midscene.js 提速 10 倍方案
- 优先使用即时操作:替代
aiAct,跳过 AI 规划;
// 慢:自动规划
await agent.aiAct('点击登录按钮');
// 快:即时操作
await agent.aiTap('登录按钮');
- 关闭深度思考:简单场景禁用
deepThink;
await agent.aiTap('登录按钮', { deepThink: false });
- 启用缓存:缓存截图 / 模型结果,避免重复推理;
const agent = new Midscene(page, { cache: true });
-
压缩截图 + 减少上下文:降低截图质量(JPEG 70-80),关闭 DOM 附加;
-
无头模式 + 浏览器优化:
const browser = await chromium.launch({
headless: true,
args: \['--disable-gpu', '--no-sandbox', '--disable-extensions']
});
-
选择轻量 VLM 模型:优先
Qwen3-VL-8B/Doubao-1.6-vision-tiny; -
并行执行:用
Promise.all并行处理独立操作; -
合并 AI 调用:一次
aiQuery提取多个数据,减少模型调用次数。
Playwright 提速方案
-
禁用不必要的浏览器功能(如 GPU、扩展);
-
使用
BrowserContext复用浏览器进程,避免重复启动; -
网络拦截 mock 静态资源,减少加载时间;
-
避免
waitForTimeout,使用waitForSelector/waitForLoadState。
2.6 脚本失败自动自愈方案
核心原理
传统定位(CSS/XPath)失败 → 自动触发 AI 视觉定位 → 修复并更新缓存,实现 “页面变动不崩”。
工业级实现代码(Midscene 风格)
class AutoHealAgent {
constructor(page, vlmService) {
this.page = page;
this.vlmService = vlmService;
this.cache = new Map(); // 缓存:元素描述 → 选择器/坐标
}
// 检查元素是否存在
async exists(selector) {
try {
return await this.page.locator(selector).isVisible({ timeout: 1000 });
} catch (e) {
return false;
}
}
// 自动自愈的点击方法
async aiTap(desc) {
// 1. 优先使用缓存的选择器(快速路径)
const cachedSelector = this.cache.get(desc);
if (cachedSelector && await this.exists(cachedSelector)) {
await this.page.click(cachedSelector);
return;
}
// 2. 尝试生成传统选择器(基于描述)
const possibleSelectors = this.generateSelectors(desc);
for (const selector of possibleSelectors) {
if (await this.exists(selector)) {
await this.page.click(selector);
this.cache.set(desc, selector); // 更新缓存
return;
}
}
// 3. 传统定位失败 → 调用 AI 视觉定位(自愈)
const screenshot = await this.page.screenshot();
const aiResult = await this.vlmService.locate(screenshot, desc);
if (aiResult.found) {
const { x, y } = aiResult;
await this.page.mouse.click(x, y); // 坐标点击
// 缓存最佳选择器(供下次快速使用)
this.cache.set(desc, aiResult.bestSelector || \`\[data-ai-loc="\${desc}"]\`);
return;
}
throw new Error(\`未找到元素:\${desc}\`);
}
// 基于描述生成可能的选择器
generateSelectors(desc) {
const text = desc.replace(/按钮|输入框|链接/, '');
return \[
\`button:contains("\${text}")\`,
\`input\[placeholder\*="\${text}"]\`,
\`a:contains("\${text}")\`,
\`#\${text.toLowerCase()}-btn\`,
\`.\${text.toLowerCase()}-btn\`
];
}
}
// 使用示例
const agent = new AutoHealAgent(page, vlmService);
await agent.aiTap('登录按钮'); // 自动自愈,页面变动也能执行
2.7 验证码处理方案
工业级组合方案(Midscene + 验证码服务)
const { Midscene } = require('@midscene/core');
const ddddocr = require('ddddocr'); // 免费本地OCR
const CaptchaCloudService = require('./captcha-cloud'); // 云打码兜底
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
const agent = new Midscene(page);
const ocr = new ddddocr.DdddOcr();
const cloudCaptcha = new CaptchaCloudService({ apiKey: 'your-key' });
// 1. 触发登录,等待验证码出现
await agent.aiTap('登录按钮');
await agent.aiWait('验证码图片');
// 2. 截取验证码区域
const captchaLocator = page.locator('img\[alt\*="验证码"]');
const captchaBuffer = await captchaLocator.screenshot();
// 3. 验证码识别(本地OCR优先,云打码兜底)
let code;
try {
// 本地OCR识别字符验证码/滑块缺口
code = ocr.classify(captchaBuffer);
} catch (e) {
// 本地失败 → 云打码(支持复杂验证码)
code = await cloudCaptcha.recognize(captchaBuffer);
}
// 4. 输入验证码并提交
await agent.aiInput('验证码输入框', code);
await agent.aiTap('提交按钮');
// 5. 失败重试(验证码错误时)
if (await page.isVisible('text=验证码错误')) {
// 重新识别并输入
const newCaptchaBuffer = await captchaLocator.screenshot();
code = await cloudCaptcha.recognize(newCaptchaBuffer);
await agent.aiInput('验证码输入框', code, { force: true }); // 强制覆盖
await agent.aiTap('提交按钮');
}
await browser.close();
})();
主流验证码解决方案对比
| 方案 | 适用场景 | 速度 | 成本 | 成功率 |
|---|---|---|---|---|
| ddddocr(本地 OCR) | 字符、滑块验证码 | 快 | 免费 | 95%+ |
| 云打码(超级鹰等) | 复杂点选、行为验证码 | 中 | 收费 | 99%+ |
| VLM 大模型(GPT-4V) | 全类型验证码 | 中 | 中 | 98%+ |
| Playwright + stealth | 风控类无感验证码 | 快 | 免费 | 90%+ |
2.8 自然语言生成自动化脚本方案
最佳架构:自然语言 → Playwright 脚本 + AI 自愈兜底
-
自然语言输入:用户描述任务(如 “打开淘宝,搜索 iPhone,提取前 5 个商品价格”);
-
大模型生成脚本:LLM(GPT/Qwen/Doubao)拆解意图,生成 Playwright 脚本;
-
运行时自愈:脚本执行时选择器失效,自动触发 AI 视觉定位兜底。
生成示例(自然语言 → Playwright 脚本)
自然语言指令:打开百度,输入 “Playwright 教程”,点击搜索,等待结果加载,截图保存为 result.png。
生成的 Playwright 脚本:
const { chromium } = require('playwright');
(async () => {
// 启动浏览器
const browser = await chromium.launch({ headless: false });
const context = await browser.newContext();
const page = await context.newPage();
// 访问百度
await page.goto('https://www.baidu.com');
// 输入搜索关键词
await page.fill('#kw', 'Playwright 教程');
// 点击搜索按钮
await page.click('#su');
// 等待结果加载
await page.waitForSelector('#content\_left', { timeout: 5000 });
// 截图保存
await page.screenshot({ path: 'result.png', fullPage: true });
// 关闭浏览器
await browser.close();
})();
落地工具推荐
-
代码生成:GPT-4、Qwen-Code、Doubao-Code;
-
脚本执行:Playwright + Midscene 自愈引擎;
-
零代码工具:Midscene Playground、Chrome 插件。
三、常见问题与解决方案(FAQ)
1. Playwright 相关
Q1:Playwright 如何实现多账号隔离?
A1:通过 BrowserContext 实现,每个 Context 对应独立的存储分区(Cookie、LocalStorage、缓存等),单浏览器可创建多个 Context,实现多账号并行且隔离。
Q2:如何找到 Playwright 中对应的 Tab 页 / Context?
A2:
-
层级关系:
Browser → Context → Page(Tab); -
从 Page 反查 Context:
page.context(); -
从 Context 获取所有 Page:
context.pages(); -
监听新 Page:
context.on('page', (newPage) => { ... }); -
无需切换 Tab,直接操作 Page 对象。
Q3:Playwright 比 Selenium 快的核心原因是什么?
A3:
-
通信协议:Playwright 用 WebSocket 长连接 + 事件推送,Selenium 用 HTTP 短连接 + 轮询;
-
自动等待:Playwright 内置元素可交互判断,Selenium 需手动写等待;
-
架构优化:Playwright 直接控制浏览器内核,Selenium 依赖 WebDriver 中间层。
Q4:Playwright 如何共享不同 Context 的数据?
A4:显式导出 / 导入:
-
Cookie:
context1.cookies()导出 →context2.addCookies()导入; -
LocalStorage:通过
page.evaluate()序列化导出后导入; -
共享用户数据目录:启动浏览器时指定
userDataDir。
2. Midscene.js 相关
Q1:Midscene.js 是基于截图的吗?
A1:是,核心是纯视觉驱动,UI 操作(点击 / 输入)完全基于截图 + VLM 识别,DOM 仅用于数据提取。
Q2:Midscene 生成脚本后,下次运行还要调用 AI 吗?
A2:默认需要,但可通过缓存 + 传统定位降级优化:
-
第一次运行:AI 识别 → 缓存元素位置 / 选择器;
-
后续运行:优先使用缓存 / 传统选择器,失败再调用 AI,速度提升 10~50 倍。
Q3:Midscene 脚本失败(页面变动)如何自动修复?
A3:自动自愈机制:
-
传统定位失败 → 自动截图;
-
调用 VLM 重新识别元素;
-
执行操作并更新缓存;
-
下次直接使用新缓存,无需重复 AI 调用。
Q4:Midscene 如何与验证码结合?
A4:
-
触发登录后,等待验证码出现并截图;
-
调用验证码服务(ddddocr 本地 OCR / 云打码 / VLM)识别;
-
输入验证码并提交;
-
验证码错误时自动重试。
3. CDP 与协议相关
Q1:CDP 为什么默认使用 WebSocket 协议?
A1:WebSocket 是唯一满足 CDP 需求的协议:
-
全双工通信:客户端与浏览器可同时收发数据;
-
长连接:无频繁握手开销;
-
浏览器主动推送事件:支持 DOM 变化、网络请求等实时通知;
-
轻量高效:帧头开销小,跨语言 / 跨平台支持好。
Q2:Playwright 如何直接调用 CDP 命令?
A2:通过 CDPSession 建立会话,示例:
const cdp = await page.context().newCDPSession(page);
await cdp.send('Network.enable'); // 启用网络域
cdp.on('Network.requestWillBeSent', (e) => console.log('请求URL:', e.request.url));
4. 工具选型相关
Q1:为什么现在自动化插件都基于 Playwright 而非 Selenium/Puppeteer?
A1:
-
对比 Selenium:Playwright 更快(WebSocket 长连接)、更稳(自动等待)、支持多上下文隔离;
-
对比 Puppeteer:Playwright 跨浏览器(Chrome/Firefox/WebKit)、跨语言(JS/Python/Java 等)、生态更完善;
-
原生支持 CDP,架构现代,迭代速度快(微软维护)。
Q2:Midscene.js 与 Playwright 该如何选择?
A2:
-
选 Playwright:专业开发者、稳定页面、追求极致速度、需要精准控制;
-
选 Midscene.js:非开发者、频繁变动的页面、零代码 / 低代码场景、需要自然语言操作。
Q3:验证码处理有哪些推荐方案?
A3:
-
免费方案:ddddocr(本地 OCR,支持字符 / 滑块验证码);
-
稳定方案:云打码(超级鹰 / 图鉴,支持复杂点选 / 行为验证码);
-
通用方案:VLM 大模型(GPT-4V/Qwen-VL,全类型验证码);
-
风控方案:Playwright + stealth 插件(去除自动化特征)。
浙公网安备 33010602011771号