“停留了 40 秒却没计分”:浏览器自动化里的两个隐形坑

举报
yd_257427043 发表于 2026/11/12 14:08:27 2026/11/12
【摘要】 从帧预算、会话卡死和页面可见性三个角度,记录浏览器自动化中最容易误判的隐形失败,以及把它们挡在流程外的守卫策略。

做浏览器自动化时,最容易误判的一类问题不是选择器失效,而是“页面看起来正常、动作也返回成功,但业务系统就是没记你一笔”。最近在华为云开发者成长中心做日常任务时,连续踩到两个这样的坑,一个来自自动化框架,一个来自页面自己的计时逻辑。把排查过程和实测数据记下来,给同样用 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 扩展)环境下的实测,页面的帧预算与计时条件均以当时抓到的源码为准。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。