如何用TypeScript结合Puppeteer获取当前页面所有WebSocket并直接发消息
问题描述
想要获取当前页面的所有WebSocket实例并保存到变量中,以便直接通过socket.send发送消息。尝试过以下代码:
const prototype = await page.evaluateHandle("WebSocket.prototype"); var socketInstances = await page.queryObjects(prototype); await page.evaluate((instances:Array<WebSocket>) => { instances[0].send("message"); }, socketInstances); const cdp = await page.createCDPSession(); await cdp.send('Network.enable'); await cdp.send('Page.enable'); cdp.on("Network.webSocketFrameReceived", response => console.log(response)); cdp.on('Network.webSocketFrameSent', response => console.log(response));
这段代码可以监听WebSocket接收的所有消息,但尚未找到无需每次调用page.evaluate就能通过WebSocket发送消息的方法。还尝试过以下方式获取WebSocket引用,但无法正常工作:
await cdp.send('Runtime.enable'); const { exceptionDetails, result: remoteObject } = await cdp.send('Runtime.evaluate', { expression: 'WebSocket' }); await cdp.send("Runtime.queryObjects", {prototypeObjectId: remoteObject.objectId});
请问有没有可行的解决方法?或者更简便的实现方式?
解决方法
方案一:通过CDP Runtime API直接调用实例方法
你之前用CDP的Runtime.queryObjects失败,是因为传入的原型对象引用不正确,正确操作步骤如下:
- 启用Runtime并获取WebSocket原型的远程对象引用:
const cdp = await page.createCDPSession(); await cdp.send('Runtime.enable'); // 获取WebSocket.prototype的远程对象(必须禁用序列化,保留引用) const { result: protoRemoteObj } = await cdp.send('Runtime.evaluate', { expression: 'WebSocket.prototype', returnByValue: false }); // 查询所有WebSocket实例 const { result: instancesRemoteObj } = await cdp.send('Runtime.queryObjects', { prototypeObjectId: protoRemoteObj.objectId });
- 保存实例列表的
objectId,后续直接用Runtime.callFunctionOn调用send方法,无需重复调用page.evaluate:
// 给第一个实例发送消息 await cdp.send('Runtime.callFunctionOn', { objectId: instancesRemoteObj.objectId, functionDeclaration: '(instances) => instances[0].send("your message")', arguments: [] }); // 如果需要复用单个实例,可以先获取该实例的独立引用 const { result: singleSocket } = await cdp.send('Runtime.callFunctionOn', { objectId: instancesRemoteObj.objectId, functionDeclaration: '(instances) => instances[0]', returnByValue: false }); // 后续直接用这个引用发送消息 await cdp.send('Runtime.callFunctionOn', { objectId: singleSocket.objectId, functionDeclaration: '(socket) => socket.send("another message")', arguments: [] });
方案二:页面注入全局变量缓存WebSocket实例
这是更简便的实现方式,通过拦截WebSocket构造函数,将所有新创建的实例存入全局数组,后续直接操作该数组即可:
// 页面加载时注入拦截代码 await page.evaluate(() => { window.__allWebSockets = []; const OriginalWebSocket = window.WebSocket; window.WebSocket = function(...args) { const socket = new OriginalWebSocket(...args); window.__allWebSockets.push(socket); return socket; }; // 继承原构造函数的静态属性 Object.assign(window.WebSocket, OriginalWebSocket); }); // 获取第一个实例的引用 const socket = await page.evaluateHandle('window.__allWebSockets[0]'); // 直接调用send方法 await socket.evaluate(s => s.send('direct message'));
该方案代码简洁,操作直观,无需依赖CDP的复杂API,适合大多数场景。
内容的提问来源于stack exchange,提问作者Sushiaaa
相关产品推荐
相关产品推荐

