通过 DevTools 协议从 Chromium 通信 "out"

Communicate "out" from Chromium via DevTools protocol

我在无头 Chromium 实例中有一个页面 运行,我正在使用 Node 中的 Puppeteer NPM 包通过 DevTools 协议对其进行操作。

我正在向页面中注入脚本。在某些时候,我希望脚本给我回电话并向我发送一些信息(通过 DevTools 协议公开的某些事件或其他方式)。

最好的方法是什么?如果可以使用 Puppeteer 来完成就太好了,但我并不反对亲自动手并手动监听协议消息。

我知道我可以通过操纵 DOM 并聆听 DOM 变化来做到这一点,但这听起来不是个好主意。

如果脚本在一次调用中发回所有数据,最简单的方法是使用 page.evaluate 和 return 来自它的 Promise:

const dataBack = page.evaluate(`new Promise((resolve, reject) => {                                                  
  setTimeout(() => resolve('some data'), 1000)                                                                      
})`)
dataBack.then(value => { console.log('got data back', value) })

这可以概括为将数据发送回两次,等等。对于发送回任意事件流,也许 console.log 比 DOM 事件稍微不那么骇人听闻?至少使用 Puppeteer 非常容易:

page.on('console', message => {
  if (message.text.startsWith('dataFromMyScript')) {
    message.args[1].jsonValue().then(value => console.log('got data back', value))
  }
})
page.evaluate(`setInterval(() => console.log('dataFromMyScript', {ts: Date.now()}), 1000)`)

(该示例使用魔法前缀将这些日志消息与所有其他消息区分开来。)

好的,我在 Puppeteer 中发现了一种内置的方法来执行此操作。 Puppeteer 定义了一个名为 exposeFunction 的方法。

page.exposeFunction(name, puppeteerFunction)

此方法在页面的 window 对象上定义了一个具有给定名称的函数。该函数在页面端是异步的。调用时,您定义的 puppeteerFunction 作为回调执行,具有相同的参数。参数不是 JSON 序列化的,而是作为 JSHandles 传递的,因此它们自己公开了对象。就我个人而言,我选择 JSON- 在发送值之前对其进行序列化。

我查看了代码,它实际上只是通过发送控制台消息来工作,就像在 Pasi 的回答中一样,Puppeteer 控制台挂钩会忽略它。但是,如果您直接收听控制台(即通过管道 stdout)。您仍会看到它们以及常规消息。

由于控制台信息实际上是由WebSocket发送的,所以效率很高。我有点不喜欢使用它,因为在大多数进程中,控制台通过 stdout 传输数据,这有问题。

例子

节点

async function example() {
    const puppeteer = require("puppeteer");
    let browser = await puppeteer.launch({
        //arguments
    });
    let page = await browser.newPage();

    await page.exposeFunction("callPuppeteer", function(data) {
        console.log("Node receives some data!", data);
    });

    await page.goto("http://www.example.com/target");
}

页面内的javascript:

window.callPuppeteer(JSON.stringify({
    thisCameFromThePage : "hello!"
}));

更新:DevTools 协议支持

DevTools 协议支持类似 puppeteer.exposeFunction 的东西。

https://chromedevtools.github.io/devtools-protocol/tot/Runtime#method-addBinding

If executionContextId is empty, adds binding with the given name on the global objects of all inspected contexts, including those created later, bindings survive reloads. If executionContextId is specified, adds binding only on global object of given execution context. Binding function takes exactly one argument, this argument should be string, in case of any other input, function throws an exception. Each binding function call produces Runtime.bindingCalled notification.

.