用 Playwright 做多平台内容分发,我踩过的 8 个坑

用 Playwright 做多平台内容分发,我踩过的 8 个坑

最近在做一个「把文章自动发布到多个自媒体平台」的桌面工具,技术栈是 Electron + Playwright。 原以为难点在反爬,做完才发现:真正耗时间的是那些「看起来能用、实际静默出错」的细节。 把踩过的 8 个坑记下来,供同样在做 RPA 的同学避坑。

1. 编辑器在 iframe 里,你的选择器「全部失效」

第一次跑头条号,所有元素定位全 MISS,一度以为是反爬。 抓了 DOM 地图才发现:编辑器在 iframe 里,只在主框架找当然找不到。

解决办法是让解析器自动在主框架和子框架之间回退,并把「命中的框架」作为降级信号记录下来。

2. 内容不在 DOM 里(CodeMirror / Monaco)

掘金用的是 ByteMD + CodeMirror 5。它的隐藏 textarea 只是输入代理,正文内容在 CodeMirror 自己的模型里。

这意味着两件事:

  1. 往 textarea 的 value 里塞内容是没用的(编辑器不认)
  2. 校验也不能读 DOM ------ 读 textContent 永远是空的,会把「注入成功」误判成「注入失败」

正确做法是走编辑器自己的 API:向上找到持有 CodeMirror 实例的容器,调用 setValue。

3. 假成功:最危险的故障类型

有一次日志显示「发布成功」,我去平台上一看:根本没发出去。

原因是发布成功的判定复用了「草稿保存成功」的信号,而上一次草稿保存成功的提示还挂在页面上。

教训:成功判据必须高特异性。现在我的判定优先级是:

  1. URL 跳到明确的成功页(如 creation/success/文章ID)
  2. 出现明确的成功文案
  3. 两者都没有 → 如实报「结果未知,不得自动重试」,绝不猜

4. 坐标点击必须先滚动

为了模拟真人,我用贝塞尔曲线算鼠标轨迹,然后用原始坐标点击。 结果:页面下方的控件(分类、标签、封面)点了完全没反应。

原因:boundingBox() 对屏幕外元素返回的是相对页面的坐标,而鼠标点击用的是视口坐标。 加一行 scrollIntoViewIfNeeded() 就好了 ------ 但这一行让我排查了几个小时。

5. 隐藏的 file input 永远等不到 visible

上传图片用的 inputtype=file 几乎都是 0×0 的隐藏元素(样式美化需要)。 而自动化框架默认等元素「可见」,于是永远超时。改用等待「已挂载」(attached)即可。

顺带一提:有些平台的上传控件是点击按钮后才动态创建的,所以还得「先点按钮 → 再找 input」, 而且不能拿第一个 ------ 第一个往往是「上传封面」。

6. 单文件 input 不能一次传多张

setInputFiles 传多张时直接报错:Non-multiple file input can only accept single file。

得逐张传、逐张等(等图片真的回填成平台 CDN 地址再传下一张)。 必须校验回填的 URL 是不是平台域名 ------ 是 blob: 或本地路径就说明没传完,这种文章发出去就是「图裂」。

7. __name is not defined:page.evaluate 的隐形杀手

这个坑最隐蔽。用 tsx 跑 TypeScript 时,esbuild 为了保留函数名,会把 const clean = (s) => s.trim() 编译成 const clean = __name((s) => s.trim(), "clean")。

这个函数被 page.evaluate 序列化送进浏览器时,页面里没有 __name,整个回调直接抛 ReferenceError。 而外层通常写着 catch 兜底,于是表现为「数据莫名取不到」,极难定位。

两个解法:注入一个恒等 shim(window.__name = fn => fn),或者干脆别在 evaluate 里写具名函数表达式。 另外一条经验:evaluate 里的异常一定要带出来,不要静默吞掉。

8. 平台差异比想象的多

举几个真实例子:

| 差异 | 表现 | | --- | --- | | 草稿保存 | CSDN 有「保存草稿」按钮;掘金是自动保存,根本没有按钮 | | 登录入口 | CSDN 的密码登录藏在底部一个图标后面;掘金是明文 tab | | 发布流程 | 一步发布 vs「点发布 → 填分类标签 → 再点确定并发布」 | | 会话 Cookie | 有的平台 uuid_tt_dd 这类设备 ID 未登录也存在,拿它判登录会误判 |

所以「每个平台一个适配器 + 一份外置的选择器配置」是必须的,不能图省事写 if-else。

小结

回头看,这 8 个坑里有 4 个属于同一类:程序说成功,实际没成。

自动化系统里,假成功比失败危险得多:失败会重试,假成功会静默地污染数据、让用户以为事情办好了。 所以我现在给自己定了条规矩:

任何「成功」都必须有高特异性的证据;拿不到证据就如实报「未知」,绝不猜。

如果这篇对你有用,后面我会继续写「选择器热更新」和「审核状态回查」的工程实现。

相关推荐
康小汪9 小时前
AI 写 Vue 总写成同一个味道:给它装 Skills,装的不是知识是边界
前端·vue.js·人工智能·ai编程·最佳实践
智能运维指南10 小时前
研制过程“两张皮“怎么破:研制一体化支撑平台的定位、能力边界与落地前提
java·前端·人工智能·嘉为蓝鲸
白山编程大哥11 小时前
Java 后端如何优雅地把 List 集合返回给前端
java·前端·list
Flynt12 小时前
我把热榜那篇"禁用 axios"逐条实测了一遍:结论大体对,坑在它没讲的地方
前端·javascript·axios
Csvn13 小时前
bundle优化
前端
北冥有鱼13 小时前
发版后动态路由点击报 `Failed to fetch dynamically imported module`」问题上的完整排查与修复过程
前端·vue.js
静默回滚13 小时前
视频压到 25 MB 以内,码率要算、音轨要扣、余量要留
前端
用户372037492601813 小时前
把 10MB 中文语义模型塞进浏览器:STSB 做到教师模型的 93%,但线上 top1 只有 42%
前端
光锥智能14 小时前
海康机器人举行2026移动机器人新品发布会:箱式存拣升级,「明智」系列发布
java·前端·机器人
计算机魔术师14 小时前
Anthropic 上市文件最吓人的一幕,不是亏损 807 亿
前端