ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

AI Agent 浏览器自动化实战:Playwright 发布文章 227 次操作与 9 个坑

AI Agent 浏览器自动化实战:Playwright 发布文章 227 次操作与 9 个坑

1. 项目缘起:为什么要让 AI 自己去社区发稿

先说结论:这个项目的本质,是把"内容发布"这条重复度极高、但又充满细节坑的链路,交给一个能自己看页面、自己点按钮、自己填表单的 AI Agent 去跑。标题里那句"227 次操作、4.3 小时、9 个坑",不是营销话术,是我盯着日志一条条数出来的真实数据。

我做这件事的动机很朴素。手上有一批技术复盘稿,需要陆续发到腾讯云社区这类开发者内容平台。手动发一篇的流程大概是:登录、进创作中心、选专栏、填标题、填摘要、贴正文、调格式、传封面、选标签、预览、提交、等审核。单篇十分钟起步,遇到格式错乱、图片上传失败、标签选不上,二十分钟也打不住。发十篇就是两三个小时纯体力活,而且越到后面越容易手滑。

于是我想,能不能让 AI 自己去做这件事。注意,这里说的不是"让 AI 写一篇文章然后我复制粘贴",而是让 AI 真正接管浏览器——它自己打开页面、自己识别输入框、自己敲字、自己点提交。这就引出了两个核心技术选型:浏览器自动化和AI Agent 的决策能力。

浏览器自动化这块,我最终选了Playwright。原因后面细讲,简单说就是它对现代前端框架的兼容性、等待机制、多浏览器支持都比老一代方案省心。AI 决策这块,用的是 WorkBuddy 这类能调用工具、能看截图、能根据页面反馈调整下一步动作的 Agent 框架。

这篇文章适合谁看?三类人。第一类是想把重复性网页操作自动化、但不知道从哪下手的开发者;第二类是在用 Playwright 做自动化测试、想扩展到"真实业务操作"场景的测试工程师;第三类是单纯好奇"AI Agent 到底能不能干成一件完整的脏活累活"的技术爱好者。我会把 227 次操作拆开讲,把 9 个坑一个个摆出来,包括每个坑的报错、排查思路和最终解法。

提示:本文所有操作均针对公开的、允许用户正常发布内容的平台,属于个人账号的常规内容发布行为。请勿将相关技术用于任何违反平台规则或法律法规的场景。

2. 整体方案设计:为什么是 Playwright 加 AI Agent

2.1 传统脚本自动化的死穴在哪

在动手之前,我先评估了"纯脚本"方案,也就是用 Playwright 写死每一步的定位器和操作。这条路我走过,结论是:对付稳定页面可以,对付经常改版的富文本编辑器就是灾难。

问题出在三个地方。第一,富文本编辑器(比如各种基于 contenteditable 或 ProseMirror 的编辑器)的 DOM 结构极其复杂,一个"输入正文"的动作,背后可能是几十个嵌套 div,定位器稍微写死一点,页面一更新就全废。第二,登录环节有验证码、有滑块、有短信验证,纯脚本很难优雅处理。第三,也是最要命的——页面状态是动态的,弹窗什么时候出现、加载动画什么时候消失、按钮什么时候从 disabled 变可点,纯脚本只能靠waitForTimeout硬等,等短了报错,等长了浪费时间。

我统计过,纯脚本方案在第一次跑通后,平均每两周就会因为页面微调而失效一次,维护成本高得离谱。

2.2 AI Agent 接管决策层的思路

所以我把方案改成了"Playwright 负责执行,AI 负责决策"的分层结构。

具体来说,Playwright 提供的是"手和眼":它能截图、能获取页面可交互元素列表、能执行点击和输入。AI Agent 提供的是"脑子":它看截图和元素列表,判断"现在页面处于什么状态""下一步该点哪里""这个输入框是不是我要找的标题框"。

这个分层的好处非常明显。页面改版了?没关系,AI 看的是语义,不是写死的 CSS 选择器。它看到"一个带 placeholder 写着'请输入标题'的输入框",就知道这是标题框,哪怕这个框的 class 名从title-input改成了editor-title__field。弹窗出现了?AI 从截图里能看出来,会先处理弹窗再继续。

代价是速度慢。每一次决策都要截图、上传、推理、返回动作,单步耗时从纯脚本的几十毫秒涨到几秒。这也是为什么最终跑了 4.3 小时——227 次操作,平均每次操作加上决策和等待,接近 70 秒。这个数字后面还会细算。

2.3 关键选型对比:为什么不是别的方案

我把当时考虑过的几个方案列了个表,方便你对照自己的场景做选择。

方案决策方式页面改版适应性单步速度适合场景
纯 Playwright 脚本写死定位器差,易失效毫秒级稳定页面、批量数据抓取
Playwright + 录制回放录制操作序列较差毫秒级一次性任务
Playwright + AI AgentAI 实时决策好秒级复杂交互、频繁改版
纯 API 调用直接调接口不涉及页面最快有开放接口的平台

这里有个重要判断:如果目标平台有开放的内容发布 API,优先用 API,别碰浏览器自动化。浏览器自动化永远是最后的选择,因为它最脆弱、最慢。我之所以走浏览器这条路,是因为目标平台的发布链路涉及富文本编辑、图片上传、标签选择等交互,没有现成的发布接口可用。

注意:选型时先花半小时确认有没有 API。很多人一上来就写自动化脚本,结果发现平台早就提供了接口,白白浪费几天。

2.4 整体架构拆解

最终跑起来的架构分四层:

  • 任务层:定义"发一篇稿"这个目标,包含标题、正文、标签、封面图等素材。
  • 决策层:AI Agent,负责看页面状态、规划下一步动作。
  • 执行层:Playwright,负责截图、定位元素、点击、输入、滚动。
  • 观测层:日志和截图存档,记录每一次操作的输入输出,方便复盘。

这四层里,观测层是我强烈建议所有人都加上的一层。227 次操作如果没有完整日志,出了问题根本没法定位。我每步都存了截图和动作记录,最后才能把 9 个坑一个个还原出来。

3. 核心细节解析:227 次操作都花在哪了

3.1 操作次数的构成拆解

很多人看到"227 次操作"会觉得夸张,发一篇文章哪需要这么多步。我把日志按阶段统计了一下,数据如下。

阶段操作次数占比说明
启动与登录3816.7%含等待登录态、处理登录弹窗
进入创作中心219.3%多次导航与页面确认
填写标题与摘要198.4%含输入校验与重试
正文粘贴与格式化6428.2%最耗时,富文本处理
图片上传3314.5%含等待上传完成
标签与分类选择2711.9%下拉框交互复杂
预览与提交2511.0%含多次确认与错误处理

可以看到,正文处理占了将近三成,这是整个流程里最麻烦的部分。原因在于富文本编辑器对"粘贴"这件事的处理方式千奇百怪,有的支持直接粘贴 Markdown,有的会把 Markdown 当纯文本,有的会保留一堆内联样式。

3.2 单步耗时的真实分布

227 次操作跑 4.3 小时,平均每步 68 秒。但这个平均值有误导性,因为耗时分布极不均匀。

  • AI 决策步:每次截图加推理,平均 4 到 8 秒。
  • 页面等待步:等加载、等上传、等弹窗消失,平均 10 到 30 秒。
  • 纯执行步:点击、输入,平均 1 到 2 秒。
  • 异常重试步:一旦触发重试,单步可能 60 秒以上。

真正拖时间的是等待和重试。我算过,如果去掉所有重试和异常处理,理想情况下这个流程大概 40 分钟能跑完。多出来的三个多小时,全是跟页面"斗智斗勇"。

3.3 富文本正文处理的三种策略

正文处理是重头戏,我试了三种策略,最后选了第三种。

策略一:模拟键盘逐字输入。用 Playwright 的type方法一个字一个字敲。优点是兼容性最好,几乎所有编辑器都认。缺点是慢到无法接受,一篇三千字的稿子要敲十几分钟,而且遇到中文输入法相关的兼容问题容易乱码。

策略二:直接设置 innerHTML。用evaluate直接往编辑器里塞 HTML。优点是快。缺点是很多编辑器有内部状态管理,你直接改 DOM,它的数据模型没同步,一提交就发现内容丢了或者格式全乱。

策略三:剪贴板粘贴。先把内容写进剪贴板,然后模拟Ctrl+V。这是最接近真人操作的方式,编辑器会走它正常的粘贴处理逻辑。最终我用的就是这个。

// 把 Markdown 内容写入剪贴板再粘贴的核心思路 await page.evaluate(async (text) => { await navigator.clipboard.writeText(text); }, markdownContent); // 聚焦编辑器后触发粘贴 await editorElement.focus(); await page.keyboard.press('Control+V');

提示:剪贴板方案在无头模式下可能失效,建议用有头模式跑,或者用 Playwright 的权限授予机制显式允许剪贴板访问。

3.4 元素定位的语义化改造

AI Agent 决策依赖的是"页面可交互元素列表"。我一开始直接把所有 DOM 元素丢给 AI,结果 AI 被几千个元素淹没了,决策质量极差。

后来我做了语义化过滤,只保留真正可交互的元素,并且给每个元素生成一段人类可读的描述:

  • 元素类型(输入框、按钮、链接、下拉框)
  • 可见文本或 placeholder
  • 是否可见、是否可点击
  • 在页面中的大致位置

这样 AI 拿到的是一份几十条的精简清单,决策准确率大幅提升。这个改造是项目能跑通的关键之一,很多人做 AI 浏览器自动化失败,就是败在"喂给 AI 的信息太脏"。

4. 实操过程:从零跑通一次完整发布

4.1 环境准备与依赖安装

环境这块没什么玄学,Node.js 加 Playwright 是标配。我用的版本组合是 Node 18 加 Playwright 最新稳定版。

# 初始化项目 npm init -y # 安装 Playwright npm install -D @playwright/test # 安装浏览器内核 npx playwright install chromium

这里有个经验:优先用 Chromium。不是因为它最好,而是因为它在自动化场景下最稳定,社区资料最多,遇到问题最好搜。Firefox 和 WebKit 在富文本编辑器上的表现经常有意外。

AI Agent 那侧,我用的是支持工具调用的框架,把 Playwright 的截图、点击、输入封装成工具函数暴露给 Agent。工具函数的描述要写得非常清楚,比如"click_element(elementId):点击指定 id 的元素,elementId 来自最近一次页面元素列表"。

4.2 登录态的处理与复用

登录是第一个大坑。我的做法是:首次手动登录,之后复用登录态。

Playwright 支持把浏览器的 storageState 保存下来,下次启动直接加载,就跳过了登录流程。

// 首次:手动登录后保存状态 const context = await browser.newContext(); // ... 手动完成登录 ... await context.storageState({ path: 'auth.json' }); // 后续:加载已保存的登录态 const context = await browser.newContext({ storageState: 'auth.json' });

这个方案能省掉大量重复登录操作。但要注意,登录态会过期,一般几天到几周不等。我在流程里加了一个检测:进入创作中心后,如果发现被重定向到登录页,就暂停任务并提示人工介入,而不是傻乎乎地继续跑。

4.3 正文粘贴的完整实现

正文这块我把完整流程拆成了五步,每一步都有讲究。

第一步,先把 Markdown 转成平台能识别的格式。有的平台支持直接粘贴 Markdown 自动渲染,有的需要先转 HTML。我做了个判断:先尝试粘贴 Markdown,如果预览发现格式没渲染,再走 HTML 转换。

第二步,定位编辑器。这里不能写死选择器,而是让 AI 从元素列表里找"看起来像正文编辑区"的元素,判断依据是它面积大、可编辑、位置在标题下方。

第三步,聚焦并清空。粘贴前先确保编辑器是空的,否则会追加到旧内容后面。

第四步,执行粘贴。用前面说的剪贴板方案。

第五步,校验。粘贴后截图,让 AI 确认内容是否完整、格式是否正确。这一步很关键,因为粘贴失败是静默的,不校验的话你根本不知道内容丢了。

4.4 图片上传的等待策略

图片上传是第二个大坑。上传按钮点了之后,图片要经历"选择文件、上传、服务器处理、返回 URL、插入编辑器"几个阶段,每个阶段耗时不定。

我一开始用固定等待,等 5 秒就继续,结果大图经常没传完就往下走,导致图片丢失。后来改成状态轮询:每隔 2 秒截图一次,让 AI 判断"上传进度条是否消失""图片是否已出现在编辑器里",直到确认完成或超时。

async function waitForUploadComplete(page, maxWaitMs = 60000) { const start = Date.now(); while (Date.now() - start < maxWaitMs) { const done = await checkUploadStatus(page); // AI 判断 if (done) return true; await page.waitForTimeout(2000); } throw new Error('图片上传超时'); }

这个轮询策略把图片丢失率从最初的 30% 降到了接近零。

4.5 标签选择的交互细节

标签选择看着简单,其实坑不少。很多平台的标签是一个输入框加下拉建议,你输入关键词后要等建议列表出现,再点选。如果输入太快,建议列表还没出来你就点了,就会选空。

我的处理是:输入关键词后,等建议列表出现(同样用截图加 AI 判断),再点第一个匹配项。选完还要确认标签真的加上了,因为有些平台点选后标签会以 chip 形式显示,有些则只是填进输入框,需要再按回车。

4.6 提交前的最终校验清单

提交是不可逆的,所以我在提交前加了一道校验。让 AI 对照截图逐项确认:

  • 标题是否非空且与预期一致
  • 正文是否有内容且格式正常
  • 封面图是否已上传
  • 标签是否至少选了一个
  • 分类是否正确

任何一项不通过就暂停,不自动提交。这道校验帮我拦下了好几次"正文粘贴失败但流程还在继续"的情况。

5. 九个坑的完整复盘与排查技巧

5.1 坑一到坑三:登录与导航相关

坑一:登录态静默失效。表现是流程跑到一半突然所有元素都找不到。排查发现是登录态过期,页面被重定向到登录页,但脚本还在按原页面找元素。解法是加一个"页面身份校验",每次关键操作前先确认当前页面是不是预期页面。

坑二:多标签页干扰。点击某些链接会打开新标签页,Playwright 默认还在旧标签页操作,导致找不到元素。解法是监听page事件,及时切换上下文,或者干脆拦截新标签页打开。

坑三:导航等待不准确。用waitForLoadState('networkidle')有时候会一直等不到,因为页面有长轮询请求。解法是改用domcontentloaded加自定义的元素出现等待。

5.2 坑四到坑六:内容填写相关

坑四:富文本粘贴丢格式。前面讲过,最终用剪贴板方案解决。

坑五:输入框有字数限制。摘要框限制 100 字,我一开始没截断,超出的部分被静默丢弃,导致摘要不完整。解法是提交前校验字数,超长就自动截断并加省略号。

坑六:特殊字符导致输入异常。正文里有代码块包含反引号和花括号,某些编辑器会把这些当特殊语法处理。解法是粘贴前对内容做转义预处理。

5.3 坑七到坑九:提交与异常相关

坑七:提交按钮有防重复点击。快速点两次,第二次会触发风控提示。解法是点击后禁用按钮,等状态变化再继续。

坑八:提交后跳转不确定。有时跳转到成功页,有时弹 toast 提示,有时直接留在原页。解法是不依赖跳转判断成功,而是综合多种信号:URL 变化、toast 文本、页面元素变化。

坑九:网络抖动导致请求失败。偶发的网络问题会让某个操作失败。解法是给关键操作加重试机制,但重试要有上限,避免死循环。

5.4 常见问题速查表

现象可能原因排查方向解决手段
元素找不到页面未加载完/登录失效看当前截图和 URL加页面身份校验
内容丢失粘贴失败/编辑器未聚焦对比粘贴前后截图剪贴板方案加校验
图片不显示上传未完成查上传状态状态轮询等待
提交无反应按钮 disabled/风控看按钮状态点击后等待状态变化
流程卡死等待条件永不满足看日志最后一步加超时和兜底

提示:这张表建议打印出来贴在显示器边上。浏览器自动化的坑,八成都能在这张表里找到对应。

6. 经验总结与后续优化方向

跑完这一趟,我最大的体会是:AI 浏览器自动化的瓶颈不在 AI 智商,而在信息质量和异常处理。AI 足够聪明,但你喂给它的页面信息如果又脏又乱,它照样抓瞎;流程里的异常如果没兜住,它就会卡在那里干等。

几个我认为最有价值的经验。第一,观测层必须做扎实,每步截图加日志,这是复盘的前提。第二,等待要用状态判断,不要用固定时间,这是稳定性的关键。第三,提交类操作一定要加前置校验,宁可暂停等人工,也不要错误提交。

后续我打算优化的方向有三个。一是把常见页面的元素语义化提取做成可复用的模块,减少重复开发。二是引入更细粒度的重试策略,区分"可重试错误"和"需人工介入错误"。三是把整个流程的耗时数据做成看板,持续监控哪一步在变慢,提前发现页面改版。

最后分享一个小技巧:如果你也想做类似的事,先用有头模式跑通全流程,再考虑无头。有头模式下你能亲眼看到 AI 在干什么,出问题一眼就能定位。我前三个坑都是在有头模式下几分钟就找到原因的,如果一上来就无头,估计得排查一整天。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进