Chat from Chromium via DevTools

I have a page running on a headless instance of Chromium, and I manipulate it through the DevTools protocol using the NPP Puppeteer package in Node.

I am embedding a script on a page. At some point, I want the script to call me back and send me some information (through some kind of event opened by the DevTools protocol or other means).

What is the best way to do this? It would be great if this could be done using Puppeteer, but I do not mind my hands being dirty and listening to protocol messages manually.

I know that I can do this by manipulating the DOM and listening to DOM changes, but that doesn't seem like a good idea.

+5
source share
2 answers

Ok, I discovered a built-in way to do this in Puppeteer . Puppeteer defines an exposeFunction method.

 page.exposeFunction(name, puppeteerFunction) 

This method defines the function with the given name in the window object on the page. The async function on the page side. When it is called, the puppeteerFunction that you define is executed as a callback with the same arguments. The arguments are not JSON-serialized, but passed as JSHandles , so they themselves expose the objects. Personally, I chose JSON serialization of values ​​before sending them.

I looked at the code and it actually just works by sending console messages, as in the Pasi response, which ignore the Puppeteer console consoles. However, if you are listening to the console directly (i.e. via the stdout pipeline). You will still see them along with regular messages.

Since console information is actually sent by WebSocket, it is pretty efficient. I was a bit opposed to using it, because in most processes the console passes data through stdout, which have problems.

Example

Node

 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"); } 

Page

Inside the javascript page:

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

If the script sends all its data in one call, the simplest approach would be to use page.evaluate and return a promise from it:

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

This can be generalized to sending data twice, etc. To send an arbitrary stream of events, maybe console.log will be a little less hacking than DOM events? At least this is very easy to do with 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)`) 

(The example uses a magic prefix to distinguish these log messages from everyone else.)

+4
source

Source: https://habr.com/ru/post/1273767/


All Articles