不用 Puppeteer,我直接用 CDP 写了个浏览器自动化脚本
最近要给几个平台做日常任务自动化,本来想上 Puppeteer,结果本机 npm 拉不下来(代理问题)。索性直接对 Chrome DevTools Protocol 下手,写完发现反而更省事——零依赖,只用 Node 内置的 WebSocket 和 fetch。
起一个能被控制的 Chrome
核心就一行参数:
chrome.exe --remote-debugging-port=9333 --user-data-dir=./profile
user-data-dir 单独开一个目录很关键,一是不污染日常用的浏览器,二是登录态会留在里面,下次直接复用。
起来之后访问 http://127.0.0.1:9333/json/list 就能拿到所有标签页,每个标签页带一个 webSocketDebuggerUrl,连上去就能发命令。
点击:别用 element.click()
这是我踩的第一个坑。很多站点的按钮是自定义 div,直接调 element.click() 不触发。我一开始还纳闷「明明点了怎么没反应」。
后来改成老老实实发真实鼠标事件:
await send('Input.dispatchMouseEvent', { type: 'mousePressed', x, y, button: 'left', clickCount: 1 });
await send('Input.dispatchMouseEvent', { type: 'mouseReleased', x, y, button: 'left', clickCount: 1 });
坐标从 getBoundingClientRect() 算中心点。这样才是「可信事件」,框架的合成事件系统才会认。
填表单:也别直接赋值
第二个坑。有个页面用 Angular,我把 input.value = 'xxx' 设上去,界面明明显示了内容,但提交按钮一直是灰的——因为框架的模型跟踪压根没收到输入事件。
正确做法是用 Input.insertText:
await send('Input.insertText', { text: '要填的内容' });
它走浏览器的真实编辑管线,会正常触发 input 事件。要清空就先 Ctrl+A 再 Backspace,同样用 dispatchKeyEvent 发。
按文字找元素,别写死选择器
页面改版是常态,写死 div.card > div:nth-child(3) > button 迟早要重写。我改成按可见文字找:
const hits = [...document.querySelectorAll('a,button,div,span')]
.filter(e => e.offsetWidth && e.textContent.trim() === '立即报名');
两个细节:必须过滤不可见元素(页面上经常有好几套隐藏模板),以及别要求是叶子节点——按钮里塞个 svg 图标就成了「有子元素」,得按后代数量从小到大排,取最小的那个。
一个真实的教训
有个登录按钮,DOM 里文字是「登录」,渲染出来却显示「登录/注册」。我按文字找,命中的是隐藏的模板元素,点了十几次都没反应,排查了半小时。
所以:文字匹配和真实可点元素不一定是一回事,关键操作尽量用稳定的 class 选择器。
顺带一提
Page.enable 之后记得监听 Page.javascriptDialogOpening,不然页面上弹个原生 alert 会把渲染线程卡死,后续所有 evaluate 全部超时——这个现象很像「脚本挂了」,其实是对话框在等你点确定。
整套下来不到 300 行,没有任何第三方依赖,比装一套 Puppeteer 省心。如果你也遇到 npm 装不动的环境,可以直接对 CDP 下手。
- 点赞
- 收藏
- 关注作者
评论(0)