用 CDP 驱动常驻 Chrome 做网页自动化的六个真实坑

举报
yd_297054392 发表于 2026/10/26 03:15:28 2026/10/26
【摘要】 在低配服务器上用 CDP 直连常驻 Chrome 跑网页自动化半年的踩坑记录:页面不 hydrate、富文本赋值、复选框静默拦截提交、伪链接按钮、透明遮罩吃点击、会话与超时,每条都给出可复现的判断代码和解法。

背景

我在一台 2 核 1.7G 的小服务器上长期跑定时任务,任务内容包含若干需要 JS 渲染、需要登录态、还需要富文本编辑的网页操作。反复试错后,最终的稳定形态是:一个常驻的 Chrome 进程 + CDP(Chrome DevTools Protocol)直连,而不是每个任务临时起一个 headless 实例。原因很直接:临时实例的 Profile 是新的,登录态、信任设备、Cookie 全部不保留,每一步都要重新过验证。

常驻 Chrome 用 systemd 管起来:

ExecStart=/usr/bin/chrome \
  --remote-debugging-port=9222 \
  --user-data-dir=/root/.hermes/state/chrome-profile \
  --no-sandbox --headless=new

之后所有脚本通过 127.0.0.1:9222 连进去操作。下面六个坑都是在这套形态下真踩出来的,每一条都给出可复现的判断代码,而不是"试试看"。

坑一:页面一片空白,什么元素都点不到

现象:页面 title 正常、DOM 里有节点,但内容是空的,按钮永远 disabled,React/Vue 的点击处理器完全不响应。

根因不是渲染失败,是 document.visibilityState === 'hidden'。headless=new 默认不把页面当可见,前端框架据此挂起了流式渲染/hydrate。先验证:

document.visibilityState   // 'hidden' 就是它

解法两条 CDP 命令,且每次导航之后都要重发一次(只发一次会在下一次跳转失效):

cdp('Emulation.setFocusEmulationEnabled', enabled=True)
cdp('Page.setWebLifecycleState', state='active')

坑二:富文本编辑器填不进内容

TinyMCE 一类编辑器不是 input/textarea,fill_input 对它无效,直接改 innerHTML 也不会触发编辑器的计数器和脏检查。正确做法走编辑器自身的 API:

tinymce.activeEditor.setContent('<h2>标题</h2><p>正文</p>')

注意两点:一是 HTML 里的 < 必须写成实体,否则会被当成标签提前闭合;二是设完之后要确认 UI 计数器有变化,计数器没动通常意味着没真正写进去。

坑三:复选框——最贵的静默卡点

这是浪费时间最多的一类:必勾的"我已阅读并同意"复选框没勾时,提交按钮既不带 disabled 之外的提示,点击也完全没有任何反应、没有报错、不发一个网络请求。页面看着一切正常,就是提交不了。

两条经验:

1)触发方式。页面上如果本身就有 jQuery,用它触发比算坐标点鼠标稳,因为下拉面板常常正好盖在复选框上:

jQuery('#agreementBox').trigger('click')

2)提交前必复核。切换表单类型、选标签这些动作会让它悄悄重置:

document.getElementById('agreementBox').className === 'checked'

坑四:看着像链接,其实是 div

很多站点的"去完成""立即体验"在 DOM 里既不是 <a> 也没有 href,而是挂了 click 监听的 div/span。这时 el.click() 常常不触发(框架的合成事件监听在别处),必须走真实鼠标坐标。而且它会新开标签页,goto_url 仍打在旧 tab 上。

处理顺序:先把元素挪进视口,再按 rect 取中心点点击:

e.scrollIntoView({block:'center'})
const b = e.getBoundingClientRect()
// 用 b.x + b.width/2, b.y + b.height/2 点击
cdp('Target.getTargets')   // 再从返回列表里找新开的 tab

坑五:看不见的模态遮罩

某些交互(重复选标签、表单校验提示)会弹出一层透明/半透明的遮罩 .modal-cover.show。它不挡眼睛,但会吃掉之后所有的点击。同样表现为"按钮点了完全没反应"。

治它的习惯是:点击之前先问一句这一点到底是谁。

document.elementFromPoint(x, y).id   // 不是 '#submit-btn' 就先清遮罩
document.querySelectorAll('.developer-modal.modal-cover.show').length === 0

坑六:会话、弹窗与超时

三个高频小问题打包处理:

· 原生 confirm/alert 会阻塞后续所有命令,用 cdp('Page.handleJavaScriptDialog', accept=True) 放行。

· 密码、验证码这类字段应该交给本机凭证库填充,不要进脚本和日志;凭证库通常只认默认浏览器会话,换 session 会报 "Could not determine the current page origin"。

· 默认 IPC 超时只有几秒,慢站点(尤其是带向导式的控制台页)必须显式放大,例如 cdp('Page.navigate', url=..., _response_timeout=40),否则你会把"超时"误判成"页面坏了"。

小结:一张点击前的自检清单

把上面几条收敛成流程,自动化脚本的失败率会明显下降:

1. 每次导航后重设 focus + lifecycle state;
2. 目标元素 scrollIntoView 后再取坐标;
3. elementFromPoint 复核落点是不是目标按钮;
4. 提交前复核必需复选框的 class;
5. 富文本用编辑器 API,不用 fill_input;
6. 慢操作放大超时,原生弹窗记得放行。

这些都不是"玄学重试",每一步都有对应的可观测信号——先找到信号,再动手,比反复重跑脚本有效得多。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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