
1. 从 RPA 和 Selenium 迁移时我到底在痛什么如果你做过两年以上自动化大概率经历过这样的循环RPA 里拖拽出来的流程业务页面一改版就全红Selenium 脚本里写满time.sleep(3)跑一次要十分钟失败率还高得离谱。RPA 的问题在于它把「界面坐标」当契约Selenium 的问题在于它把「DOM 结构」当契约而这两样东西恰恰是前端迭代中最不稳定的部分。MCPModel Context Protocol加 Playwright 的组合换了一个思路不再让脚本去死记元素路径而是让模型理解「我要做什么」再由 Playwright 去执行浏览器动作。Playwright 本身自带自动等待、多浏览器内核、网络拦截能力比 Selenium 的显式等待稳得多MCP 则把「模型决策」和「浏览器执行」拆成两个可独立调试的进程出问题时你能清楚知道是模型理解错了还是页面真的没加载出来。这套方案适合谁适合已经写过 Selenium 或 RPA、想把手动维护脚本的精力转移到「描述任务」上的开发者也适合需要做数据采集、表单填报、页面巡检但不想再被 XPath 和 iframe 折磨的人。下面我从环境准备开始把 MCP 服务端配置、Playwright 启动参数、统一 Key 接入以及三步验证动作完整走一遍。2. TaoToken 前置统一 Key 与 MCP 服务端骨架MCP 服务端要调用模型能力就得有一个稳定的 API 入口。我试过把 Key 散落在各个脚本里后来统一收敛到 TaoToken 的 API 地址https://taotoken.net/api配合一个 Key 管理多个模型调用省去了到处改配置的麻烦。你可以在控制台创建 Key然后把它写进 MCP 的配置文件里。先看 MCP 服务端的config.toml骨架。这个文件决定了 MCP Server 启动时加载哪些工具、用哪个模型端点、超时和重试怎么设# config.toml - MCP Server 配置骨架 [server] name playwright-mcp version 0.1.0 transport stdio # 本地开发用 stdio部署可换 sse log_level info [model] provider openai-compatible base_url https://taotoken.net/api api_key ${TAOTOKEN_API_KEY} # 从环境变量读取不要硬编码 model claude-sonnet-4-20250514 max_tokens 4096 timeout_seconds 60 retry 2 [playwright] headless true browser chromium viewport_width 1440 viewport_height 900 navigation_timeout 30000 action_timeout 15000 [tools] enabled [navigate, click, fill, screenshot, extract_text]这里有几个点值得展开。transport stdio表示 MCP Server 通过标准输入输出和客户端通信适合本地调试如果你要远程调用可以改成sse并配端口。api_key用${TAOTOKEN_API_KEY}占位实际运行时从环境变量注入避免 Key 进版本库。model字段填你实际要用的模型名TaoToken 的 API 兼容 OpenAI 格式所以provider写openai-compatible即可。环境变量这样设置# Linux / macOS export TAOTOKEN_API_KEYsk-你的Key # Windows PowerShell $env:TAOTOKEN_API_KEYsk-你的Key如果你还没创建 Key去控制台的 API Keys 页面生成一个权限选「模型调用」就够了不需要开管理权限。这一步做完MCP 服务端就有了调用模型的能力接下来配 Playwright 的浏览器启动参数。3. 可复制配置Playwright 启动参数与 MCP 工具注册Playwright 的启动参数直接决定自动化稳不稳。默认配置下Chromium 以 headless 模式启动但很多页面会检测 headless 特征并返回不同内容。我的做法是保留 headless 但加上一组反检测参数同时把slow_mo设小一点方便观察# playwright_launcher.py from playwright.sync_api import sync_playwright def launch_browser(): pw sync_playwright().start() browser pw.chromium.launch( headlessTrue, args[ --disable-blink-featuresAutomationControlled, --no-sandbox, --disable-dev-shm-usage, --disable-gpu, --window-size1440,900, ], slow_mo50, # 每步操作间隔 50ms便于调试 ) context browser.new_context( viewport{width: 1440, height: 900}, user_agent( Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 ), localezh-CN, timezone_idAsia/Shanghai, ) context.set_default_timeout(15000) context.set_default_navigation_timeout(30000) return pw, browser, context--disable-blink-featuresAutomationControlled是减少自动化特征的关键参数配合自定义 user_agent能显著降低被识别概率。--disable-dev-shm-usage在容器环境里防止共享内存不足导致崩溃。slow_mo只在调试时开生产环境设 0。接下来把 Playwright 动作注册成 MCP 工具。MCP 的工具定义是一个 JSON Schema描述工具名、参数和返回值。下面是一个navigate工具的注册示例# mcp_tools.py from mcp.server import Server from mcp.types import Tool, TextContent server Server(playwright-mcp) server.list_tools() async def list_tools(): return [ Tool( namenavigate, description打开指定 URL 并等待页面加载完成, inputSchema{ type: object, properties: { url: {type: string, description: 目标网址}, wait_until: { type: string, enum: [load, domcontentloaded, networkidle], default: networkidle, }, }, required: [url], }, ), Tool( nameextract_text, description提取页面中指定选择器的文本内容, inputSchema{ type: object, properties: { selector: {type: string}, all: {type: boolean, default: False}, }, required: [selector], }, ), ] server.call_tool() async def call_tool(name: str, arguments: dict): if name navigate: page get_current_page() page.goto(arguments[url], wait_untilarguments.get(wait_until, networkidle)) return [TextContent(typetext, textf已打开 {arguments[url]})] if name extract_text: page get_current_page() if arguments.get(all): texts page.locator(arguments[selector]).all_inner_texts() else: texts [page.locator(arguments[selector]).inner_text()] return [TextContent(typetext, text\n.join(texts))] raise ValueError(f未知工具: {name})wait_untilnetworkidle比 Selenium 的implicitly_wait聪明得多它会等网络请求静默后才继续动态加载的页面也能抓到。工具注册完MCP 客户端就能用自然语言触发这些动作了。4. 验证请求三步确认调用链路正常配置写完不代表能跑我习惯用三步验证法确认整条链路。第一步启动 MCP 服务第二步跑一个最小抓取脚本第三步看日志确认模型调用和浏览器动作都发生了。4.1 第一步启动 MCP 服务# 确保环境变量已设置 echo $TAOTOKEN_API_KEY # 启动 MCP Server python -m mcp_server --config config.toml正常启动后终端会输出类似[INFO] MCP Server playwright-mcp v0.1.0 starting... [INFO] Transport: stdio [INFO] Model endpoint: https://taotoken.net/api [INFO] Tools loaded: navigate, click, fill, screenshot, extract_text [INFO] Server ready, waiting for requests...如果卡在Model endpoint那行不动多半是 Key 没读到或网络不通。先确认echo $TAOTOKEN_API_KEY有输出再检查base_url有没有拼错。4.2 第二步跑通首个页面抓取脚本写一个最小客户端通过 MCP 协议让模型决定抓什么Playwright 执行# first_scrape.py import asyncio from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client async def main(): params StdioServerParameters( commandpython, args[-m, mcp_server, --config, config.toml], ) async with stdio_client(params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() # 让模型理解任务打开页面并提取标题 result await session.call_tool( navigate, {url: https://example.com, wait_until: networkidle}, ) print(导航结果:, result.content[0].text) result await session.call_tool( extract_text, {selector: h1, all: False}, ) print(页面标题:, result.content[0].text) asyncio.run(main())运行python first_scrape.py预期输出导航结果: 已打开 https://example.com 页面标题: Example Domain这一步跑通说明 MCP 工具注册、Playwright 启动、页面加载三个环节都正常。4.3 第三步确认调用链路日志把config.toml里的log_level临时改成debug重启服务后再跑一次脚本你会看到类似日志[DEBUG] Received tool call: navigate [DEBUG] Model request - https://taotoken.net/api/v1/chat/completions [DEBUG] Model response: 200 OK, tokens used: 312 [DEBUG] Playwright: page.goto(https://example.com) [DEBUG] Playwright: navigation completed in 842ms [DEBUG] Tool result returned to client重点看三行Model request确认请求打到了 TaoToken 的 APIModel response: 200确认鉴权和模型调用成功Playwright: navigation completed确认浏览器动作执行完毕。三行都在链路就是通的。如果Model response返回 401检查 Key返回 429说明触发限流降低并发或换时间段。5. 本篇常见错排查迁移过程中我踩过的坑集中在几个地方列出来供你对照。报错ModuleNotFoundError: No module named mcpMCP 的 Python SDK 还在快速迭代用pip install mcp装最新版如果和 Playwright 版本冲突先建虚拟环境再装。Playwright 需要额外执行playwright install chromium下载浏览器内核只装 pip 包不装内核会报Executable doesnt exist。页面抓到的内容是空的八成是wait_until设成了load而目标页面是前端渲染的。改成networkidle或者显式等某个选择器出现page.wait_for_selector(.content, timeout10000)。Selenium 迁移过来的同学容易习惯性加time.sleep在 Playwright 里应该用wait_for_selector或expect断言。MCP 服务启动后客户端连不上transport设成stdio时客户端必须用同样的 stdio 方式启动子进程不能一个用 stdio 一个用 sse。如果你在容器里跑注意 stdio 需要保持进程存活别让主进程提前退出。模型返回的指令 Playwright 执行不了这是工具 schema 定义太宽泛导致的。比如click工具只接受selector字符串模型可能返回一段自然语言描述。解决办法是在工具description里写清楚参数格式并给几个示例模型会照着格式输出。Key 泄露风险永远不要把 Key 写进config.toml提交到 Git。用环境变量或.env文件并把.env加进.gitignore。TaoToken 控制台可以随时吊销旧 Key怀疑泄露就立即轮换。6. 接入文档与后续动作三步验证跑通后你手里就有了一套可用的 MCP Playwright 自动化骨架。接下来要做的是把具体业务动作注册成更多 MCP 工具比如表单填报、文件下载、截图对比。每加一个工具都按「定义 schema → 实现 Playwright 动作 → 用 debug 日志验证」的流程走一遍链路清晰出问题也好定位。如果你在配置 Key 或接入 MCP 时遇到鉴权、超时、限流这类问题可以直接查接入文档里面有各语言 SDK 的示例和错误码说明。需要生成或轮换 Key 就去 API Keys 页面。想先验证模型对话是否正常可以用模型对话页面发一条测试消息确认 Key 和端点都通。长期做编码和 Agent 任务的话Coding Plan 页面有更完整的额度方案适合把自动化任务跑在稳定配额上。从 RPA 和 Selenium 迁过来最大的心态转变是不再追求「一次写对脚本」而是把任务描述清楚让模型和 Playwright 去处理页面变化。脚本维护量降下来之后你才有精力去做真正有价值的自动化设计。