Связь "выход" из хрома через инструменты разработчика протокола
У меня есть страница, запущенная в безголовом экземпляре Chromium, и я управляю ею с помощью протокола DevTools, используя пакет Puppeteer NPM в Node.
Я ввожу скрипт на страницу. В какой-то момент я хочу, чтобы скрипт перезвонил мне и отправил мне некоторую информацию (через какое-то событие, выставленное протоколом DevTools или каким-то другим способом).
Как лучше всего это сделать? Было бы здорово, если бы это можно было сделать с помощью Кукольника, но я не против испачкать руки и прослушивание сообщений протокола вручную.
Я знаю, что могу сделать это, манипулируя DOM и слушая изменения DOM, но это не кажется хорошей идеей.2 ответа:
Если скрипт отправляет все свои данные обратно в одном вызове, самым простым подходом было бы использовать
page.evaluate
и вернуть обещание из него: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? По крайней мере, это очень легко сделать с кукольником: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, который игнорируется консольными крючками Кукольника. Однако, если вы слушаете консоль непосредственно (то есть по трубе
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
Если executionContextId пуст, добавляет привязку с заданным именем на глобальные объекты всех обследованных контексты, в том числе созданные позже привязки переживают перезагрузки. Если указан executionContextId, добавляет привязку только к глобальному объекту данного контекста выполнения. Обязательный функция принимает ровно один аргумент, этот аргумент должен быть строковым, в случае любого другого ввода функция создает исключение. Каждая привязка вызов функции производит время выполнения.bindingCalled уведомление.
.