You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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失败,是因为传入的原型对象引用不正确,正确操作步骤如下:

  1. 启用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
});
  1. 保存实例列表的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 23:37:18