想给自己的账号做自动化——自动签到、自动填表、自动发内容?写爬虫要处理登录态、验证码、接口加密,门槛不低。其实有一条更省事的路:用 CDP(Chrome DevTools Protocol)直接操控你平时已经登录好的 Edge 浏览器。登录态、Cookie、本地存储全部现成,根本不需要碰逆向。
这套方案我最近在 Windows 上实战了一整套流程(自动往网页编辑器里注入长文并提交),本文把完整做法和踩过的坑一次讲清。文中的代码全部可复现。
关键是给 Edge 开一个远程调试端口,并使用独立的用户数据目录,这样既不影响日常使用的浏览器配置,又能长期保住登录态:
Start-Process "C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe" `
-ArgumentList "--remote-debugging-port=9222", `
"--user-data-dir=C:\edge_profile", `
"--no-first-run", `
"about:blank"启动后访问 http://127.0.0.1:9222/json/version,能拿到 JSON 就说明端口通了。每个标签页的调试地址在 http://127.0.0.1:9222/json 里列出,取 webSocketDebuggerUrl 字段连上即可。
Node 22 内置了 WebSocket,不需要装 puppeteer 或 playwright,几十行代码就能搭起一个最小客户端:
// cdp_eval.js:连接当前活动标签页,执行一段 JS 并返回结果
const http = require('http');
function getTarget() {
return new Promise((resolve, reject) => {
http.get('http://127.0.0.1:9222/json', (res) => {
let data = '';
res.on('data', (c) => (data += c));
res.on('end', () => {
const pages = JSON.parse(data).filter((t) => t.type === 'page');
resolve(pages[pages.length - 1]); // 最后打开的页面
});
}).on('error', reject);
});
}
async function evalJs(expr) {
const target = await getTarget();
const ws = new WebSocket(target.webSocketDebuggerUrl);
await new Promise((r) => (ws.onopen = r));
ws.send(JSON.stringify({
id: 1, method: 'Runtime.evaluate',
params: { expression: expr, returnByValue: true, awaitPromise: true }
}));
ws.onmessage = (ev) => {
const msg = JSON.parse(ev.data);
if (msg.id === 1) {
console.log(JSON.stringify(msg.result, null, 2));
ws.close();
}
};
}
evalJs(process.argv[2]);用法:node cdp_eval.js "document.title"。也可以把 JS 写进 .js 文件再传文件名,方便注入长代码。
很多后台编辑器(比如各类 Markdown 编辑器)基于 Monaco(VS Code 的编辑器内核)。直接 textarea.value = xxx 是没用的,因为 Monaco 不监听原生表单事件,React 状态也不会同步。
正确姿势分两步:
第一步,拿到 Monaco 的模型,一次性 setValue:
var models = window.monaco.editor.getModels();
models[0].setValue('完整的 markdown 内容');第二步,解决中文编码问题。 把长文通过命令行参数传给脚本,中文极易被 Windows 的编码坑掉。稳妥的办法是先把文章 UTF-8 转 base64,在页面里再解码:
(function(){
var b64 = '...文章的base64...';
var bytes = Uint8Array.from(atob(b64), function(c){ return c.charCodeAt(0); });
var text = new TextDecoder('utf-8').decode(bytes);
window.monaco.editor.getModels()[0].setValue(text);
return 'ok';
})()在 Node 侧生成 base64 只需一行:Buffer.from(fs.readFileSync('article.md', 'utf8')).toString('base64')。
这是我踩得最惨的一坑。页面上有个"确认发布"按钮,document.querySelector('button.publish').click() 执行后毫无反应,按钮明明存在、也不禁用,但弹窗就是不关。
排查思路:先用 Page.captureScreenshot 截图确认页面状态,排除选择器选错的可能;再加一个 MutationObserver 监听 DOM,看有没有一闪而过的 toast 提示。最后确认:这个按钮的事件监听绑在真实指针事件上,程序化 click() 合成的 trusted=false 事件被框架忽略了。
解法是用 CDP 的 Input.dispatchMouseEvent 模拟一次真实的鼠标按下和释放:
const x = 1690, y = 985; // 先截图/用 getBoundingClientRect 算出按钮坐标
await send('Input.dispatchMouseEvent', { type: 'mouseMoved', x, y });
await send('Input.dispatchMouseEvent', { type: 'mousePressed', x, y, button: 'left', clickCount: 1 });
await new Promise((r) => setTimeout(r, 80));
await send('Input.dispatchMouseEvent', { type: 'mouseReleased', x, y, button: 'left', clickCount: 1 });坐标可以通过 Runtime.evaluate 里执行 document.querySelector('button.publish').getBoundingClientRect() 拿到。同理,凡是 React 受控输入框,直接改 value 不生效,要用 Input.dispatchKeyEvent 逐字符发真实键盘事件(type: 'keyDown' + text 字段)。
顺带一提:Input.enable 这个方法并不存在,直接 dispatch 即可,别在这上面浪费时间。
整套流程里最值钱的三个手段:
Page.captureScreenshot 返回 base64,落盘成 PNG,每一步操作后都截一张,眼见为实;Runtime.evaluate 都让页面侧 return JSON.stringify({...}),把状态带回来打印,不要静默执行;typeof window.monaco !== 'undefined',编辑器没加载完就注入必然失败。CDP 方案的本质是:借用你自己已登录的浏览器会话,绕开一切登录和加密门槛,把自动化范围收敛到"操作页面"本身。配合独立 user-data-dir,登录态可以保存数月。
局限也要说清楚:页面改版脚本就会失效,需要维护选择器;它不适合大规模采集(那是爬虫的领域),但非常适合"给自己账号做定时自动化"这类场景——定时签到、定时发布、定时查状态,稳定性和开发成本都远优于逆向路线。
完整代码一共不到两百行,没有第三方依赖。有兴趣的朋友可以把这套骨架拿去改造成自己的自动化工具。觉得有用的话,点个赞再走。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。