
大家好,我是若风。
先说个刚发生的事。我让 Claude 帮我读一下 Docker 公司最近的 LinkedIn 动态,就这一句话。几秒之后,它把 Docker 最近 23 条动态整理成了中文摘要,哪条在讲 AI Agent 沙箱,哪条在预热 AI Engineer World's Fair,互动数据都带出来了。

说实话,我盯着屏幕愣了一下。不是结果多惊艳,是我突然意识到,这一句话背后的工程量。
你想想看,AI 怎么知道 LinkedIn 上有什么?它又不会上网。它调用了一个叫 mcp-server-linkedin 的工具。这个工具底层不是 LinkedIn 官方 API,是用浏览器去抓页面的程序。而这个浏览器,被一个叫 Patchright 的库控制着,Patchright 又是 Playwright 的反检测分支,Playwright 通过一套叫 CDP 的协议,真正指挥 Chromium 这个浏览器内核干活,最后把页面渲染出来的,是 Blink。
一层套一层,我数了数,整整 8 层。

这篇文章就沿着这一句话,一层层拆开。看看 AI 从「想看一下 LinkedIn」到「真的打开浏览器抓回数据」,中间穿过了什么。
第 1 层 AI 模型,听懂你说话的那个大脑
一切从你那句话开始。「读一下 Docker 公司最近的 LinkedIn 动态」。
这句话进到 AI 模型里,它得做两件事。先听懂你要什么,再去想怎么拿。它自己上不了网,但它知道自己手里有一堆工具,其中有个能查 LinkedIn。
这层是整条链的大脑。没有它对意图的理解和对工具的调度,后面 7 层再强也启动不了。
第 2 层 MCP,让 AI 知道有工具可以调
AI 本身不会上网,不会读文件,不会操作浏览器。它能做的只有读文本、写文本。那它怎么完成「读 LinkedIn」这种事?
靠工具。但工具怎么告诉 AI 自己存在、自己能接收什么参数?这就得有一套约定。MCP,Model Context Protocol,就是这套约定。
我对 MCP 的理解就一句话。它不是让 AI 变强的魔法,是让 AI 和外部世界说同一种语言的翻译协议。它解决的不是智能问题,是接口问题。
具体到这个场景。mcp-server-linkedin 通过 MCP 把自己注册成 17 个能力,查公司资料、刷动态、搜职位、读私信。AI 看到的是 17 个工具的说明书,它判断「读 Docker 动态」该调 get_company_posts,把 docker 这个参数传进去。
这一层,AI 还没碰到浏览器。它只是递出了一张工单。
第 3 层 mcp-server-linkedin,把抓 LinkedIn 包装成 17 个工具
工单递到了 mcp-server-linkedin 手里。它接到 docker 这个参数,要去拿 Docker 公司的动态。
你可能会问,为什么不直接调 LinkedIn 的 API?坦率的讲,LinkedIn 没有给普通人开放这种 API。它的数据锁在登录态后面,想拿,就得像一个真人用户那样,登录,打开页面,等加载,然后把页面内容抓下来。
所以这个工具的底层,不是 API 调用,是浏览器自动化。它启动一个真实的浏览器,用你登录过的 LinkedIn 会话,导航到 Docker 公司主页,把动态一条条抓回来。
这里就埋下了后面所有层的根源。因为「用浏览器抓页面」这件事,远比调一个 API 复杂。
第 4 层 Patchright,为什么不能直接用 Playwright
浏览器自动化,第一反应是用 Playwright 或者 Puppeteer。但 mcp-server-linkedin 用的是 Patchright,一个多数人没听过的库。
为什么换掉 Playwright?因为反爬。
LinkedIn 不是傻子。它知道有人在用程序抓它的页面,所以部署了 Cloudflare 这类反爬服务。这些服务检测的,就是「这个浏览器是不是被程序控制着」。
而 Playwright 控制浏览器时,会留下一堆指纹。最致命的一个叫 Runtime.enable。Playwright 调这个 CDP 命令在页面里执行 JavaScript,但这个动作本身会在页面留下可检测的痕迹,反爬服务一查一个准,挺让人崩溃的。
Patchright 干的事就是把这些指纹抹掉。Runtime.enable 的泄漏,它改用隔离的执行上下文绕过。Console.enable 的泄漏,它直接把控制台 API 禁掉。命令行参数里那个会暴露自动化的 --enable-automation,它去掉。
讲到底,Patchright 就是 Playwright 的一个补丁版,API 完全一样,改个 import 就能用,但它在底层把所有「我是机器人」的标记都擦干净了。
这是整条链里我觉得最妙的一层。功能没变,能力没少,与其说它变强了,不如说它学会了伪装。

第 5 层 Playwright,这套 API 从哪来
Patchright 是 Playwright 的 fork,版本号都跟着 Playwright 走。那 Playwright 又是从哪来的?
往前追溯,2017 年 Google 发布了 Puppeteer,第一个把 CDP 封装成好用的 API 的库,迅速成了 Node.js 浏览器自动化的标配。三年后微软发布 Playwright,核心团队就是从 Puppeteer 那边过来的,早期代码直接 fork 自 Puppeteer。
Playwright 比 Puppeteer 走得更远。它不只支持 Chrome,还支持 Firefox 和 WebKit。不只 Node.js,还有 Python、.NET、Java。2026 年的 Playwright 甚至把 AI Agent 当成一等公民,专门提供了 CLI 和 MCP 两种形态给 AI 用。
所以这条链的框架层,其实是一部进化史。Puppeteer 是鼻祖,Playwright 是继任者,Patchright 是继任者的反检测分身。
第 6 层 CDP,真正控制浏览器的协议
不管是 Puppeteer、Playwright 还是 Patchright,它们那些花里胡哨的 API,page.goto、page.evaluate、page.screenshot,到底层全都翻译成同一种东西。CDP。
CDP 全称 Chrome DevTools Protocol,Chrome 的调试协议。你打开 Chrome 的开发者工具,那个面板背后跑的就是 CDP。它通过 WebSocket 和浏览器通信,用 JSON 格式传消息。
CDP 把浏览器的所有能力拆成了 58 个 domain。Page 管导航和截图,Runtime 管执行 JavaScript,Network 管网络请求,Target 管标签页。每个 domain 还得先 enable,浏览器才会给你推事件。
举几个真实的动词。想打开新标签页,调 Target.createTarget。想附着到这个标签页,调 Target.attachToTarget,传一个 flatten 等于 true,拿回一个 sessionId。想让页面跑一段 JavaScript,调 Runtime.evaluate。想截图,调 Page.captureScreenshot。
这才是浏览器自动化的底层语言。上面那些框架,不过是把这些动词包装成人话。
第 7 层 Chromium,CDP 控制的到底是个什么
CDP 控制的那个东西,是 Chromium。Google 的开源浏览器项目,Chrome 的基础,Edge、Brave、Opera 全家都建在它上面。
Chromium 最核心的设计是多进程。一个 Browser 进程管全局,多个 Renderer 进程各自渲染一个网页,还有 GPU 进程管图形加速。每个 Renderer 都被关在沙箱里,几乎没有系统权限,就算网页里有恶意代码,也难以逃出来影响你的电脑。
这个设计是为安全,但它也解释了为什么抓一个页面要等。你调 CDP 让页面导航,得等 Renderer 进程把 HTML 解析完,等 Page.loadEventFired 事件触发,还得等网络空闲,确定懒加载的内容都加载完了,才能动手抓。不然抓回来的就是半截页面。
所以 mcp-server-linkedin 抓 Docker 动态时,它在那等页面加载、等网络静默、还自动往下滚几下触发懒加载,都是有原因的。Chromium 的架构决定了,你得给它时间。
第 8 层 Blink,把 HTML 变成你能抓的 DOM
CDP 让浏览器导航、执行 JS,但页面最后长什么样,是一个叫 Blink 的渲染引擎决定的。Blink 在 Chromium 里,负责解析 HTML、排版、绘制,把一堆标签变成你眼睛看到的页面。
这里有个关键。mcp-server-linkedin 抓的,不是服务器返回的原始 HTML,是 Blink 渲染完之后的 DOM。
现在的网页,尤其是 LinkedIn 这种,首屏 HTML 里往往啥都没有,内容是 JavaScript 跑起来之后动态塞进去的。所以你不等 Blink 把 JS 执行完、把内容渲染进 DOM,抓到的就是空壳。这也是为什么前面那层要等网络空闲、要滚动触发懒加载,都是在等 Blink 把活干完。
横切一下,四种路线怎么选
拆到这,整条链清楚了。但有意思的是,实现「用程序控制浏览器」这件事,不止一条路。我见过四种,各有各的取舍。
| 路线 | 代表 | 特点 | 适合场景 |
|---|---|---|---|
| 鼻祖框架 | Puppeteer | Node.js,功能全 | 常规自动化 |
| 继任框架 | Playwright | 多语言多浏览器 | 跨浏览器测试 |
| 反检测 fork | Patchright | 抹掉自动化指纹 | 抓有反爬的站点 |
| 自研裸 CDP | baoyu-url-to-markdown | 零依赖,几百行 | 轻量工具分发 |

这第四种是我最近拆一个 Skill 时发现的。那个叫 baoyu-url-to-markdown 的工具,作者压根没用 Puppeteer,自己用几百行代码写了个 CDP 客户端。理由很实在,Puppeteer 要下载一百多兆的浏览器,作为一个要分发的 Skill 太重了。而且他想复用你已经打开的 Chrome,这个能力 Puppeteer 也不直接给。
所以选型这事没有标准答案。看你是在做产品、做测试、做爬虫,还是做个轻量工具。
这条链还没定型
写到这里,8 层拆完了。但我想多说一句,这条链其实还在快速变化。
CDP 是 Chrome 专有的,功能最全但绑死 Chromium。W3C 在推一个叫 WebDriver BiDi 的标准,目标是让 Firefox、Safari 也能用同一套双向协议,跨浏览器自动化。Puppeteer 和 Playwright 都在跟进。
更值得盯的是 MCP 和浏览器的融合。Puppeteer 已经支持实验性的 WebMCP,CDP 协议里也冒出了一个叫 WebMCP 的 domain 条目,虽然还是空的。如果这事成了,浏览器可能原生就暴露 MCP 接口,AI 调用浏览器都不用中间再套一层框架。
那现在这 8 层,以后说不定能少几层。
写在最后
回头看这次抓 LinkedIn。一句「读下 Docker 的动态」,背后是 AI 模型听懂意图,MCP 递工单,mcp-server-linkedin 接活,Patchright 伪装成真人,Playwright 提供人话 API,CDP 发底层指令,Chromium 多进程调度,Blink 把页面渲染成 DOM。
AI 长出手脚这件事,不是一句 prompt 搞定的。它是一整条协议栈,一层层把「想」翻译成「做」。
理解这条栈有什么用?下次你的 AI Agent 上网抓数据被抓了、超时了、抓到半截了,你知道该去哪一层找问题。而不是只能盯着一句报错发呆。
这大概就是拆解的意义,把魔法还原成工程。