通过 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.
.
我在无头 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.
.