1. 项目背景与设计目标
- 背景:需要对大批量目标网址进行自动化操作(如提取邮箱/填写表单)。由于各网站 DOM 结构与交互逻辑差异极大,传统基于固定 CSS/XPath 的脚本无法统一兼容,需引入 AI Agent 动态决策。
- 核心目标:
- 零 API 成本与高隐私:利用本地 RTX 3060 (12GB) 运行轻量模型,实现完全本地化的推理与执行。
- 极致的 Token 控制:全量 HTML 会导致 Context 爆炸和 AI 幻觉,必须在输入给 AI 前对 DOM 进行极致降噪与切片。
- 高并发与高可用:支持多任务并行抓取,具备完善的异常降级与防死循环机制。
2. 核心架构与选型
🚀 主选方案:Playwright-MCP 官方服务 + 指纹浏览器 CDP
- 实现方式:
打开指纹浏览器 $\rightarrow$ 获取 CDP 端口 $\rightarrow$ 拉起 npx @playwright/mcp@latest 服务 $\rightarrow$ 代码(C# / SK)创建 MCP Client $\rightarrow$ 将 MCP Tools 与浏览器指挥权彻底托管给本地小模型。
- 优点:开箱即用,开发成本极低。网页打标、DOM 降噪、物理点击、Shadow DOM 与 Iframe 均由微软官方底层闭环处理,无需手写页面清洗逻辑。
- 注意事项:多线程并发时,需在代码中为每个指纹环境动态分配独立 MCP 端口(如
8931, 8932),并控制好进程生命周期。
🥈 备选方案 A:原生 Accessibility Tree + 双向 ID 锚定 (Fallback 1)
- 实现方式:
通过 Playwright 的 page.Accessibility.SnapshotAsync() 抓取极简 A11y 树,作为上下文传给 AI。
- 解决 A11y 树“太简化导致无法定位 DOM”的技巧:
- 操作前注入轻量 JS,为所有可交互 DOM 打上
data-ai-id="X"。
- 顺手写入标准 ARIA 属性:
el.setAttribute('aria-description', 'ai-id-X')(防止被 A11y 树过滤)。
- A11y 树会天然携带此 Description,AI 决策后返回
ai-id-X,代码通过 page.Locator("[aria-description='ai-id-X']") 执行物理点击。
- 适用场景:当 Playwright-MCP 服务因特殊原因挂起,或需要更轻量级、无外部进程依赖的单机执行时。
🥉 备选方案 B:自定义 DOM 极简剪切脚本 (Fallback 2)
- 实现方式:
在页面中注入自研 JS 脚本:给 <a>, <button>, [onclick] 绑定唯一 data-ai-id,强行剥离 script, style, svg, path 以及无用空标签,拼接成专供 AI 阅读的极简 JSON 树。AI 决策时直接带入 data-ai-id。
- 优点:高度定制。可以把业务逻辑(如“自动保留包含
@ 符号的文本”)写死在前端清洗脚本里,极致省 Token。
- 缺点:调试成本极高,真实页面的边缘 Case(如隐藏元素、多层嵌套)需要大量时间适配。
🚷 弃用方案:OmniParser V2 纯视觉 Agent
- 实现方式:Playwright 截图 $\rightarrow$ 视觉模型画框标注坐标 $\rightarrow$ 大模型根据坐标指示点击。
- 弃用原因:
- 显存爆掉:视觉模型 + 检测模型会吃满 3060 显存,无法做多线程并发。
- 长图盲区:无法一次性截取 Footer(底部)区域的联系方式,AI 容易在“向下滚动”指令中陷入死循环。
- 延迟极高:单步截图与图像推理需耗时数秒,效率远低于文本流。
3. 关键工程细节与避坑指南
3.1 MCP 并发与状态隔离 (防串线与崩溃)
- 进程级隔离:由于
@playwright/mcp 默认是单实例状态,高并发时必须在 C# 端为每个抓取任务启动一个独立的 MCP Server 进程(通过 --port 分配不同的 SSE 端口,或使用 stdio 管道)。
- 浏览器上下文隔离:通过 CDP 连接指纹浏览器时,必须在 C# 端通过 CDP 协议(
Target.createBrowserContext)为每个任务创建独立的 Browser Context(相当于无痕模式的新窗口),并将该 Context 的 ID 传给 MCP,严防多个 AI Agent 操作同一个 Tab 导致“互相踩踏”。
3.2 Snapshot 后处理降噪 (防 Context 爆炸)
- 痛点:Playwright-MCP 的
browser_snapshot 默认返回完整的 AX Tree。对于包含巨大导航菜单或 Footer 的官网,单次 Snapshot 可能高达 10k+ Tokens,直接撑爆小模型的 Context。
- 对策:在 C# 端的 MCP Client 拦截层,对返回的 Snapshot 文本进行正则截断或裁剪。例如:移除所有
role="navigation" 且子节点超过 20 个的庞大菜单树,只保留核心内容区域的节点,强制将单次 Snapshot 控制在 3000 Tokens 以内。
3.3 Agent 防死循环硬性约束 (防算力空转)
- 痛点:小模型在复杂 SPA 页面极易陷入“点击 -> 弹窗 -> 关闭 -> 再次点击”的死循环。
- 对策:
- 硬限制:在 System Prompt 和 C# 循环代码中双重限制
MaxSteps = 6。
- URL 去重:在 C# 端维护一个
HashSet<string> visitedUrls。如果 AI 调用的 Tool 导致页面跳转到了已访问过的 URL,C# 端直接拦截并返回 Tool 错误信息:“该页面已访问过,请寻找其他链接”,强制 AI 改变策略。
4. 落地实施路线 (漏斗式执行流)
- 环境隔离与基础直达:
- 主程序调用指纹浏览器 API 唤醒环境(自带代理 IP 与伪装指纹)。
- 主程序通过 CDP 驱动浏览器直达目标首页(不浪费 AI 算力做基础导航)。
- AI 攻坚 (主路线):
- 启动
playwright-mcp,由本地小模型自动执行多轮工具调用,多跳深入 Contact Us 或社交媒体页面寻找邮箱。
- 降级兜底 (备选方案):
- 若 MCP 在特殊异常页面挂起或返回无效 Snapshot,自动降级为“自定义 DOM 剪切脚本 (备选方案 B)”,吐出 JSON 树让 AI 做最后攻坚。
- 结果提取与清理:
- 提取到邮箱后,通过 CDP 关闭当前 Browser Context,释放显存与内存,进入下一个任务。
5. 硬件与部署建议
- 硬件配置:
- GPU:NVIDIA RTX 3060 12GB (核心推理节点)。
- CPU/内存:建议 8核16线程以上,32GB+ 系统内存 (Playwright 多开浏览器实例极其吃内存)。
- 软件栈:
- 推理引擎:Ollama (
本地部署 Phi-4-mini 与 Qwen2.5-Coder-7B mac air 上使用 qwen3.5:9b-mlx 在 3060 win 机器上使用 qwen3.5:9b)。
- 浏览器控制:Node.js 环境 +
@playwright/mcp。
- 业务代码:.NET 8 (使用
Microsoft.Extensions.AI 抽象层接入 MCP 与 LLM)。
- 指纹浏览器:支持 CDP 协议导出的商业/开源指纹浏览器 (如 AdsPower, VMLogin 等)。