“停留了 40 秒却没计分”:浏览器自动化里的两个隐形坑
做浏览器自动化时,最容易误判的一类问题不是选择器失效,而是“页面看起来正常、动作也返回成功,但业务系统就是没记你一笔”。最近在华为云开发者成长中心做日常任务时,连续踩到两个这样的坑,一个来自自动化框架,一个来自页面自己的计时逻辑。把排查过程和实测数据记下来,给同样用 CLI 驱动真实浏览器的人参考。
坑一:1.2 秒的帧预算,任何 iframe 都能拖垮整页
现象:打开任务页后,CLI 报 A page frame is not responding,换标签页、重开页面都没用,偶尔还会升级成 cdp_failed: Previous operation is still settling,之后连 reload、close、get-html 都发不出去。
根因在扩展的逐帧采集实现里:它会枚举标签页内所有 frame,对每个 frame 发消息取状态,并且给每次取状态设了 1200 毫秒的硬期限;只要有一个 frame 超时,整页判定失败。源码位置很直接:
const value = await Promise.race([
chrome.tabs.sendMessage(tabId, message, { documentId: frame.documentId }),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('A page frame is not responding')), 1200))
]);
也就是说,主文档再空闲也没用,一个广告位、一个视频 iframe、一个第三方统计组件,只要它在采集瞬间阻塞主线程超过 1.2 秒,整页就不可观测。
我做了个受控实验:本地页面里放一个 iframe,每 5 秒阻塞主线程 3 秒。
- 普通 iframe 页面:连续 5 次同步全部成功,单次 0.05–0.25 秒。
- 阻塞 iframe 页面:连续 5 次全部失败;
--settle重试 3 次分别耗时 6.44 秒、5.96 秒,第三次直接拖到 30 秒 RPC 上限。 - 更关键的是,一旦某次调用撞到 30 秒上限,会话的操作队列就卡住,
reload、tab close、get-html、tab list全部返回cdp_failed,只能弃掉整个会话重开。
反过来,如果 iframe 只是偶发阻塞 0.8–1.5 秒,连续同步反而 5/5 通过——因为页面没有变化时同步会走“unchanged”短路,不再重新采集全部帧。所以真正危险的是“首次采集”和“页面刚变化后的采集”,不是每次调用。
坑二:document.hidden 时,计时一秒都不记
第二个坑更隐蔽。任务要求“在案例详情页停留超过 30 秒”,我打开页面、等了 35 秒、断言页面标题正确,结果积分一分没涨。
抓页面代码才看到,计时上报有一个硬前置条件:
if (!(...) && !document.hidden && !L) {
const E = await heartbeat(I); // 每 5 秒一次
}
页面不可见时,心跳根本不会发出。而自动化窗口如果被其他窗口挡住,标签页的 visibilityState 就是 hidden——注意不是 visible 但未聚焦,而是直接 hidden。实测同一页面:
hidden状态停留 35 秒:points不变,任务卡片仍是“去完成”。- 把自动化窗口置于前台、确认
document.hidden === false后停留 40 秒:积分 +10,卡片变“已完成”。
这条经验可以推广到所有“停留时长/阅读时长”类任务:先断言可见性,再谈停留时间。否则脚本会安静地空跑,日志里一切正常。
怎么把这两类问题挡在流程外
我在外面加了一层会话守卫,策略只有四条:
1. 单命令 25 秒硬上限,永不触碰 30 秒 RPC 上限。实测 30 秒是“可恢复失败”和“会话卡死”的分界线。
2. 按错误类型分流:A page frame is not responding 最多重试一次(带 --settle);Page frames cannot be observed、timeout、cdp_failed 直接停会话,不重试、不重放。
3. 失败即弃:同一标签页第二次失败就停会话、开新会话新标签。卡死后的标签页连关闭都发不出去,抢救的成本高于重开。
4. 计时类任务先查 document.hidden,hidden 直接报错退出,不许静默继续。
加完守卫后,忙页面从“拖到 30 秒卡死整个会话”变成“12.4 秒内明确失败并自动停会话”,新会话可以立即恢复;轻页面仍然 5/5 通过。
小结
两个坑的共同点是:失败信号不在业务层。第一个坑里页面是好的,失败的是框架的采集预算;第二个坑里页面也是好的,失败的是浏览器对可见性的判定。两者都不会在页面 UI 上留下痕迹,只会表现为“动作成功但结果没发生”。
所以做这类自动化,判据要落在业务侧可验证的事实上——积分变了没有、卡片状态变了没有——而不是“脚本没报错”。另外,把失败预算和可见性检查写进工具层,比靠人记住“遇到就重开”可靠得多。
以上数据来自 Windows 11 + Edge(MV3 扩展)环境下的实测,页面的帧预算与计时条件均以当时抓到的源码为准。
- 点赞
- 收藏
- 关注作者
评论(0)