Puppeteer 获取 WebSocket 响应

发表时间

正如 Puppeteer 获取 http 请求的响应 一文里提到的方法,除了获取 HTTP 响应以外,还能获取 WebSocket 响应。因为有些页面通过 WebSocket 实现异步传输,仅仅从HTTP通道上获取响应是不够的,如何使用 Puppeteer 得到 WebSocket 响应内容,本文讨论一种方法。


基本原理

截止目前,Puppeteer 没有提供原生的用于处理 WebSocket 的 API 接口。只能通过更底层的 Chrome DevTool Protocol (CDP) 协议获得。

Puppeteer 使用 CDPSession 对象处理 CDP 协议相关

获取 WebSocket 响应

下面这段代码可以将接收到的 WebSocket 响应打印在控制台上:

// 打开浏览器,打开新页面
browser = await puppeteer.launch()
page = await browser.newPage()

// 创建 CDP 对象
const client = await page.target().createCDPSession()

// 打开网络跟踪,允许网络事件通知到浏览器
await client.send('Network.enable')

// 订阅收到 WebSocket 的事件,响应包含在 params 参数中
client.on('Network.webSocketFrameReceived', 
    function(params){
        console.log(params.response.payloadData)
    }
})

// 开始浏览
await page.goto(url)

上面这段代码首先启用了网络跟踪 Network.enable ,再订阅了 Network.webSocketFrameReceived 事件,当有进来的 WebSocket 帧时会触发,注意:包括握手、心跳帧,都会触发该事件。

其他事件

下面这段代码,可以观察到 WebSocket 的各种事件:

browser = await puppeteer.launch()
page = await browser.newPage()
const client = await page.target().createCDPSession()
await client.send('Network.enable')

client.on('Network.webSocketCreated', 
    function(params){
        console.log(`创建 WebSocket 连接:${params.url}`)
    }
)
client.on('Network.webSocketClosed', 
    function(params){
       console.log(`WebSocket 连接关闭`) 
    }
)
client.on('Network.webSocketFrameSent', 
    function(params){
       console.log(`发送 WebSocket 消息:${params.response.payloadData}`)
    }
)
client.on('Network.webSocketFrameReceived', 
    function(params){
       console.log(`收到 WebSocket 消息:${params.response.payloadData}`)
    }
)
client.on('Network.webSocketWillSendHandshakeRequest', 
    function(params){
       console.log(`准备发送 WebSocket 握手消息`)
    }
)
client.on('Network.webSocketHandshakeResponseReceived', 
    function(params){
       console.log(`接收到 WebSocket 握手消息`)
    }
)
// 开始浏览
await page.goto(url)

Promise 化

外面包一层 Promise 后,可以方便的使用 await 得到响应结果,放在其他的同步语句中。具体来说,首先准备类似下面的函数:

function getWebSocketResponse(resolve, reject){
  client.on('Network.webSocketFrameReceived', (params)=>{
    resolve(params.response.payloadData)
  })
}

然后在测试用例里可以像下面这么写:

describe('获取ws响应', function() {
    it('检查 ws 响应中的值', async function(){
         //其他语句

         wsResponse = new Promise(getWebSocketResponse)
         await page.goto(url, {waitUntil: 'networkidle0'})
         response = await wsResponse 
         expect(response).to.equal(someValue)
     })
})

这样,测试用例就可以写的很简洁、易读。

(完)

本页面内容采用 署名协议 CC-BY 授权。欢迎转载,请保留原文链接


分类

相关文章