Используйте безголовый хром для перехвата данных запроса изображения

У меня есть вариант использования, в котором необходимо использовать Headless Chrome Network (https://chromedevtools.github.io/devtools-protocol/tot/Network/), чтобы перехватить все запросы изображений и узнать размер изображения перед его сохранением (в основном отбросьте небольшие изображения, такие как значки).

Однако я не могу найти способ загрузить данные изображения в память перед их сохранением. Мне нужно загрузить его в объект Img, чтобы получить width и height. Network.getResponseBody принимает requestId, к которому у меня нет доступа в Network.requestIntercepted. Также Network.loadingFinished всегда дает мне "0" в переменной encodedDataLength. Понятия не имею почему. Итак, мои вопросы:

  1. Как перехватить все ответы от запроса jpg / png и получить данные изображения? Без сохранения файла через строку URL на диск и загрузка обратно.

  2. ЛУЧШИЙ: как получить размер изображения из ответа заголовка? Тогда мне вообще не нужно считывать данные в память.

Мой код ниже:

const chromeLauncher = require('chrome-launcher');
const CDP = require('chrome-remote-interface');
const file = require('fs');

(async function() {
  async function launchChrome() {
    return await chromeLauncher.launch({
      chromeFlags: [
        '--disable-gpu',
        '--headless'
      ]
    });
  }
  const chrome = await launchChrome();
  const protocol = await CDP({
    port: chrome.port
  });

  const {
    DOM,
    Network,
    Page,
    Emulation,
    Runtime
  } = protocol;
  await Promise.all([Network.enable(), Page.enable(), Runtime.enable(), DOM.enable()]);

  await Network.setRequestInterceptionEnabled({enabled: true});
  Network.requestIntercepted(({interceptionId, request, resourceType}) => {
    if ((request.url.indexOf('.jpg') >= 0) || (request.url.indexOf('.png') >= 0)) {
      console.log(JSON.stringify(request));
      console.log(resourceType);

      if (request.url.indexOf("/unspecified.jpg") >= 0) {
        console.log("FOUND unspecified.jpg");
        console.log(JSON.stringify(interceptionId));
        // console.log(JSON.stringify(Network.getResponseBody(interceptionId)));
      }

    }
    Network.continueInterceptedRequest({interceptionId});
  });

  Network.loadingFinished(({requestId, timestamp, encodedDataLength}) => {
    console.log(requestId);
    console.log(timestamp);
    console.log(encodedDataLength);
  });

  Page.navigate({
    url: 'https://www.yahoo.com/'
  });

  Page.loadEventFired(async() => {
    protocol.close();
    chrome.kill(); 
  });

})();

person HP.    schedule 08.08.2017    source источник
comment
Насколько я понимаю, в настоящий момент API не позволяет этого делать. Затем проголосуйте за закрытие.   -  person HP.    schedule 14.08.2017


Ответы (1)


Это должно привести вас к цели на 90%. Он получает тело каждого запроса изображения. Вам все равно нужно будет кодировать base64decode, проверять размер, сохранять и т. Д.

const CDP = require('chrome-remote-interface');

const sizeThreshold = 1024;

async function run() {
  try {
    var client = await CDP();
    const { Network, Page } = client;

    // enable events
    await Promise.all([Network.enable(), Page.enable()]);

    // commands
    const _url = "https://google.co.za";
    let _pics = [];
    Network.responseReceived(async ({requestId, response}) => {
      let url = response ? response.url : null;
      if ((url.indexOf('.jpg') >= 0) || (url.indexOf('.png') >= 0)) {
        const {body, base64Encoded} = await Network.getResponseBody({ requestId }); // throws promise error returning null/undefined so can't destructure. Must be different in inspect shell to app?
        _pics.push({ url, body, base64Encoded });
        console.log(url, body, base64Encoded);
      }
    });
    await Page.navigate({ url: _url });
    await sleep(5000);

    // TODO: process _pics - base64Encoded, check body.length > sizeThreshold, save etc...

  } catch (err) {
    if (err.message && err.message === "No inspectable targets") {
      console.error("Either chrome isn't running or you already have another app connected to chrome - e.g. `chrome-remote-interface inspect`")
    } else {
      console.error(err);
    }
  } finally {
    if (client) {
      await client.close();
    }
  }
}

function sleep(miliseconds = 1000) {
  if (miliseconds == 0)
    return Promise.resolve();
  return new Promise(resolve => setTimeout(() => resolve(), miliseconds))
}

run();
person Ilan    schedule 06.09.2017
comment
Хм, в прошлый раз, когда я тестировал, Network.getResponseBody не сделал то, что должен был делать, как сказано в названии ... Позвольте мне дважды проверить. - person HP.; 09.09.2017
comment
Андреа исправил REPL для возврата значений из команд, поэтому теперь вы должны иметь возможность запускать вышеуказанное прямо в REPL. NB. Я бы посоветовал не использовать деструкцию (const {body, base64Encoded} = ...). Иногда я получал пустые ответы и деструктивные броски, что приводило к необработанному исключению Promise. - person Ilan; 10.09.2017