ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用FastMCP搭建Playwright MCP Server:让LLM直接操作浏览器

用FastMCP搭建Playwright MCP Server:让LLM直接操作浏览器 简介Playwright MCP Server 是基于 FastMCP 框架的专业级浏览器自动化 MCP 服务器面向需要为 LLM 应用提供网页交互与数据提取能力的开发者。依托 Playwright 技术支持 Chromium、Firefox、WebKit 跨浏览器自动化可完成表单填写、链接点击、内容抓取等操作并具备模块化设计、认证流程处理、会话管理与日志记录等高级能力适合复杂网页操作和大规模数据采集场景。压缩包共26个文件以 Python 源码和测试脚本为主另有 Markdown 文档、YAML/JSON 配置、HTML 说明、TOML 配置及 License 等整体约90KB目录组织清晰。该资源已有142人浏览学习。读者可从中了解 FastMCP 框架下搭建 MCP 服务的方法、Playwright 脚本的编写与扩展思路并参考示例将其接入 LLM 应用在多浏览器环境中稳定执行自动化任务。 我最近把项目里的浏览器自动化从 Selenium 整体迁到了 Playwright然后顺手用 FastMCP 搭了一个 Playwright MCP Server。以前想用自然语言指挥 AI 去操作网页通常只能让模型生成一段脚本再手动跑而现在通过 MCP 协议模型的每一步都可以直接调用浏览器工具整个过程就像给 AI 装了一双能点击、能输入、能截图的“手”。这篇文章就把这个项目的搭建思路、完整实现和踩过的坑梳理一遍适合已经在用 Playwright、或者想让 LLM 直接操作网页的同学参考。1. 项目整体思路为什么用 FastMCP 包一层 Playwright1.1 MCP Server 到底解决了什么问题MCPModel Context Protocol可以理解为 LLM 和外部工具之间的通用接口。没有 MCP 之前你想让模型调用浏览器需要在 prompt 里塞工具说明、写 JSON Schema、自己做工具路由和错误返回有了 MCP Server 之后工具的定义、参数的校验、结果的返回全部标准化。FastMCP 这个 Python 框架就是帮我快速实现这个标准服务的工具用装饰器就能把普通函数变成 MCP 工具省掉了大量样板代码。这里有一个生活化类比浏览器是一台带各种按钮的机器LLM 是你的助手MCP Server 就是机器旁边那块标准的控制面板助手只需要按面板上的按钮不需要知道机器内部怎么走线。这个项目就是把 Playwright 的能力全部映射到控制面板上。1.2 为什么是 Playwright 而不是 Selenium很多人会问Selenium 用得好好的为什么要迁。我实际用下来感受最深的三点内置浏览器驱动。Selenium 需要手动下载 chromedriver还要跟浏览器版本严格对应Playwright 装完库之后一条命令能装好 Chromium驱动在包里自动匹配没有“驱动版本不对”这个烦恼。自动等待和选择器更顺手。Playwright 的 locator 自带等待逻辑默认等待元素可操作不用自己写 sleeptext 选择器、role 选择器定位元素非常直观。支持现代 Web 特性。比如 iframe 切换、多标签页处理都比 Selenium 原生 API 舒服很多。如果项目里已经大量用了 Selenium没必要立刻推倒重来但新项目或者要接 MCP 场景Playwright 绝对值得优先考虑。两类框架在驱动管理、定位、等待机制上的差异我整理成了一个小表维度SeleniumPlaywright浏览器驱动手动下载 chromedriver版本严格对应内置驱动install 命令自动匹配等待机制依赖 WebDriverWait 显式等待locator 自带自动等待iframe 切换switch_to.frame 手动切换frame_locator 直接链式定位多标签页需要 window_handles 切换直接基于 page 对象管理1.3 FastMCP 这个框架选得值不值FastMCP 是 Python 生态里比较新的 MCP 框架底层用 FastAPI 做 Streamable HTTP 传输。我选择它除了 Python 技术栈统一以外还因为工具定义用 Python 装饰器代码量小可读性好支持 lifespan 管理资源比如在服务启动时创建浏览器实例池和现有的 Python 监控、日志体系能直接打通。官方也有playwright/mcp这个 Node.js 实现如果你整个项目是 Node 技术栈用官方包会更省事。我这边主栈是 Python而且后续要自己加业务校验和权限控制用 FastMCP 更合适。说白了这是一个“让 AI 和浏览器对话”的项目核心不是 MCP 本身而是你愿意给 AI 暴露多少浏览器控制能力FastMCP 只是帮我用最少的代码把它表达出来。2. 环境准备与依赖安装2.1 基础环境Python 依赖和浏览器内核最基础的环境需要 Python 3.10 以上。安装命令很简单pip install fastmcp playwright然后安装 Chromium 内核python -m playwright install chromium如果你是 Node 项目也可以用npx playwright install chromium。装完可以用python -m playwright --version验证。这里有个小坑有些服务器环境是精简版系统缺少系统依赖库Chromium 启动会失败。这时候要跑一遍python -m playwright install-deps它会自动把 Linux 底层的 so 库补全。我第一次在 CentOS 上部署就卡在这一步折腾了半个多小时才发现是系统依赖缺失。2.2 驱动逻辑差异不用再纠结 chromedriver 版本网上关于“web 自动化 selenium 浏览器驱动怎么判断下载哪个区别”的问题特别多核心原因就是 Selenium 需要单独下载 chromedriver而且 Chrome 升级之后驱动往往跟着报废每次都要去镜像站比对版本号。Playwright 则是把浏览器和驱动放在一起统一管理python -m playwright install chromium装的是官方钦定的 Chromium 版本和库本身天然兼容。只有需要连接本机 Chrome 时才要用channelchrome的方式但驱动依然不用你手动管。这一点在团队协作里特别重要。以前同事之间经常因为 Chrome 版本不一样导致脚本互相跑不了现在只要大家用同一个 Playwright 版本浏览器版本也一致杜绝了环境差异问题。2.3 在 MCP 客户端里注册服务服务写好后要在客户端侧配置 MCP Server。Claude Desktop 的配置在claude_desktop_config.json里加mcpServers节点Codex 则可以在项目根目录放.mcp.json。以 Codex 为例{ mcpServers: { playwright-mcp: { command: python, args: [path/to/server.py] } } }配置完成后Codex 启动时会自动发现这个 server 并加载里面的工具。有一点要注意MCP 客户端连接服务的最佳实践是让服务进程独立常驻不要每次对话临时启动否则工具列表加载和浏览器预热都会很慢。如果只是本地开发也可以用npx modelcontextprotocol/inspector python server.py打开调试面板直接看到服务端暴露了哪些工具、每个工具接收什么参数。3. 核心实现写一个可用的 Playwright MCP Server3.1 FastMCP 最小代码骨架下面这段是我线上项目里最早能跑起来的最小版本去掉了业务细节只保留了核心结构from fastmcp import FastMCP from playwright.async_api import async_playwright mcp FastMCP(playwright-server) mcp.tool() async def open_page(url: str) - str: async with async_playwright() as p: browser await p.chromium.launch(headlessTrue) page await browser.new_page() await page.goto(url, wait_untilnetworkidle) title await page.title() await browser.close() return ftitle: {title} if __name__ __main__: mcp.run(transportstdio)注意这里每次打开页面都新启一个浏览器性能其实不行只适合验证链路。如果是真实项目要把 browser 放到 lifespan 里做复用from contextlib import asynccontextmanager asynccontextmanager async def lifespan(server): p await async_playwright().start() browser await p.chromium.launch(headlessTrue) try: yield {browser: browser} finally: await browser.close() mcp FastMCP(playwright-server, lifespanlifespan)这样浏览器实例在整个服务生命周期内常驻每个工具调用只需要new_page速度会快一个量级。3.2 把浏览器操作封装成 MCP 工具我实际封装了open_page、fill_input、click_element、extract_text、screenshot、extract_links六个工具。封装的逻辑很简单每个 MCP 工具暴露给模型的最小参数集合内部再补默认值。比如点击元素模型只需要告诉我是哪个元素定位方式参数默认填 text内部用page.get_by_text去点。这样模型不容易生成非法参数减少报错。代码片段示意mcp.tool() async def click_element(element: str, by: str text) - str: browser mcp.state.browser page await browser.new_page() try: if by text: locator page.get_by_text(element, exactFalse) elif by css: locator page.locator(element) else: return unsupported by argument await locator.first.click(timeout5000) return fclicked: {element} finally: await page.close()这类默认值设计很重要。模型天然不适合处理“精确但复杂”的参数你让它传完整的 CSS 选择器它很可能编一个错误的东西但让它传“页面里的登录按钮”它基本都能定位对。所以服务端要把定位策略吃下来模型只传语义化参数。3.3 提升“专业级”的几个细节要称得上专业级光能跑通还不够。我后来补了三块统一错误包装。任何工具抛出 Playwright 异常都返回结构化的 error 字段避免模型看到一堆堆栈也方便日志系统做关键字检索。并发控制。同一时刻只允许一个浏览器操作在跑用 Python 的asyncio.Semaphore控制防止客户端多工具并发调用把浏览器玩坏。操作日志。每个工具调用都记录时间、参数、耗时方便排查到底是模型指令问题还是页面问题。这些细节才是浏览器自动化接入 LLM 后在真实环境里能稳定的关键。看起来不起眼但少了任何一个模型都会在生产环境给你整出各种奇怪的交互事故。4. 常见问题与排查技巧实录4.1 target closed 报错“playwright: target closed: target page, context or browser has been closed”是出现频率最高的问题之一热搜里甚至有人在代码里贴了完整报错。一般有三个原因一是操作还没结束浏览器对象被提前 close 了比如try/finally里 finally 先关了浏览器异步操作还在跑二是页面跳转后旧的 page 对象失效你拿着旧的 locator 去点击三是 MCP server 的超时时间太短模型等待过程中浏览器被强制关闭。我的排查套路是先看日志里操作是不是在 close 之后发生如果是把 close 挪到所有协程执行完如果是页面跳转导致的改用page.wait_for_load_state并重新定位元素如果是超时调大客户端超时时间同时让服务端在长时间空闲时自动关闭页面。这里有一个小技巧所有工具都要把page.close()放在finally里防止异常路径导致连接泄漏。4.2 iframe 和 span 定位Selenium 时代切 iframe 用switch_to.framePlaywright 更简单。抓取动态 iframe 内容时可以直接用frame_locatorpage.frame_locator(iframe[namecontent]).get_by_text(确认).click()span 这类非交互元素定位建议优先用get_by_text或者locator(span:has-text(关键字))。不要硬写 CSS 索引页面一改动就报废。我之前有个任务要批量抓页面里所有 span 标签的文本先用page.locator(span).all_text_contents()一把梭后来发现部分动态加载的 span 没渲染出来必须在获取前加page.wait_for_selector(span)。4.3 Python 打包 exe 时 Playwright 怎么处理用 PyInstaller 打包 playwright 程序最常遇到的问题是运行后找不到浏览器驱动。我的做法是先用python -m playwright install chromium装好浏览器后用 PyInstaller 的--add-data把 playwright 的 driver 目录和浏览器路径都打进去并加上--collect-all playwright。打包后的 exe 里还要在代码里指定PLAYWRIGHT_BROWSERS_PATH到解压后的临时目录。这块网上写的乱我的经验是不要直接用默认路径打包脚本里写死相对路径并在启动时创建临时浏览器目录比什么都稳。如果你是发给同事用建议把 PyInstaller 的 spec 文件一并提交到仓库这样别人改一下路径就能重新出包不用重新摸索。4.4 Node.js 版本和镜像问题如果在 Node 项目里遇到 playwright 相关包安装失败先检查 Node 版本官方要求 Node 18 以上。npm 源的问题可以考虑把 registry 切到国内镜像比如 npmmirror然后正常执行npm install和npx playwright install chromium。不要在这个环节折腾太久版本和源是出现诡异问题的高发区。我遇到过最神奇的一次是npx playwright install chromium反复提示“页面崩溃”后来发现是 Node 版本太低升级之后一次通过。所以遇到“玄学问题”先看版本。5. 场景延伸把 Playwright MCP 用到真实项目里5.1 在 Scrapy 中接入 Playwright 抓取动态 iframe热搜里有个词叫“scrapy playwright 动态 iframe”说明这确实是个高频需求。Scrapy 单靠 requests 拿不到 iframe 里的内容我们在下载中间件里用 Playwright 渲染页面再提取 iframe body。接入 Playwright MCP 后调试时可以直接让模型操作页面定位元素把 selector 回填给 Scrapy 配置整体效率高不少。比如先让 AI 打开某个后台页面把 iframe 里的关键字段名称抓出来再人工确认一遍省去反复写调试脚本的时间。5.2 Codegen 录制工具与 CLI 效率Playwright 自带的 codegen 是后期维护脚本的利器执行npx playwright codegen https://example.com后会打开一个录制窗口你手动操作页面左侧自动生成 Python 或 JS 代码。我在写 MCP 工具时经常用它来快速确认某个按钮的最稳选择器。Playwright CLI 本身也支持open、pdf、screenshot等命令很多杂活不需要写代码就能完成。官方文档也值得收藏遇到 API 记不清时直接查按钮的定位策略比硬靠记忆靠谱。5.3 一点个人心得我在这个项目里最大的收获是明白了一个道理MCP Server 的价值不在代码量而在工具边界的划分。你给模型暴露的每个浏览器操作都是一次“信任权限”如果一个工具能接收任意 JS 表达式并执行那基本等于把机器完全交给模型。专业级实现安全控制比功能丰富更重要。另外浏览器自动化和模型交互稳定性是个长期磨合的过程。我现在上线任何新工具都会先用固定场景跑 20 轮确认没有竞态和内存泄漏后再开放给团队。就像 Playwright 自己强调的“可靠选择器和显式等待”在 MCP 场景里各种奇怪的时序问题会被模型的不确定性放大提前做好防御永远值得。最后再分享一个小技巧如果你要调试 MCP Server别一上来就看客户端用npx modelcontextprotocol/inspector打开 Inspector它会直接把服务端返回的工具收发情况列出来比看日志高效得多。本文还有配套的精品资源点击获取
返回列表