Безголовый хромированный корпус

В Chrome 59 поставляется оболочка Headless Chrome . Это способ запуска браузера Chrome в безголовой среде. По сути, это запуск Chrome без самого Chrome! Она переносит все современные функции веб-платформы, предоставляемые Chromium и механизмом рендеринга Blink, в командную строку.

Безголовый браузер — отличный инструмент для автоматизированного тестирования и серверных сред, где не требуется видимый пользовательский интерфейс. Например, вы можете захотеть провести тесты на реальной веб-странице, создать её PDF-файл или просто проверить, как браузер отображает URL-адрес.

Запуск без графического интерфейса (CLI)

Проще всего начать работу в безголовом режиме — открыть исполняемый файл Chrome из командной строки. Если у вас установлен Chrome версии 59 или выше, запустите Chrome с флагом --headless :

chrome \
--headless \                   # Runs Chrome in headless mode.
--disable-gpu \                # Temporarily needed if running on Windows.
--remote-debugging-port=9222 \
https://www.chromestatus.com   # URL to open. Defaults to about:blank.

chrome должен указывать на вашу установленную версию Chrome. Точное местоположение может различаться в зависимости от платформы. Поскольку я использую Mac, я создал удобные псевдонимы для каждой установленной у меня версии Chrome.

Если вы используете стабильную версию Chrome и не можете получить доступ к бета-версии, воспользуйтесь chrome-canary :

alias chrome="/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome"
alias chrome-canary="/Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary"
alias chromium="/Applications/Chromium.app/Contents/MacOS/Chromium"

Скачайте Chrome Canary .

Функции командной строки

В некоторых случаях вам может не потребоваться программно создавать скрипты для Headless Chrome. Для выполнения распространенных задач можно использовать полезные флаги командной строки .

Печать DOM-дерева

Флаг --dump-dom выводит document.body.innerHTML в стандартный поток вывода:

    chrome --headless --disable-gpu --dump-dom https://www.chromestatus.com/


### Create a PDF

The `--print-to-pdf` flag creates a PDF of the page:

```shell
chrome --headless --disable-gpu --print-to-pdf https://www.chromestatus.com/

Создание скриншотов

Для создания снимка экрана страницы используйте флаг --screenshot :

chrome --headless --disable-gpu --screenshot https://www.chromestatus.com/

# Size of a standard letterhead.
chrome --headless --disable-gpu --screenshot --window-size=1280,1696 https://www.chromestatus.com/

# Nexus 5x
chrome --headless --disable-gpu --screenshot --window-size=412,732 https://www.chromestatus.com/

Запуск с --screenshot создаст файл screenshot.png в текущем рабочем каталоге. Если вам нужны скриншоты всей страницы, процесс немного сложнее. Есть отличная статья в блоге Дэвида Шнурра, которая вам поможет. Посмотрите статью «Использование безголового Chrome в качестве автоматизированного инструмента для создания скриншотов» .

Режим REPL (цикл чтения-вычисления-печати)

Флаг --repl запускает Headless в режиме, позволяющем выполнять вычисления выражений JavaScript в браузере прямо из командной строки:

$ chrome --headless --disable-gpu --repl --crash-dumps-dir=./tmp https://www.chromestatus.com/
[0608/112805.245285:INFO:headless_shell.cc(278)] Type a Javascript expression to evaluate or "quit" to exit.
>>> location.href
{"result":{"type":"string","value":"https://www.chromestatus.com/features"}}
>>> quit
$

Как отлаживать Chrome без пользовательского интерфейса браузера?

При запуске Chrome с --remote-debugging-port=9222 запускается экземпляр с включенным протоколом DevTools . Этот протокол используется для связи с Chrome и управления экземпляром браузера без графического интерфейса. Он также используется такими инструментами, как Sublime, VS Code и Node, для удаленной отладки приложений. #синергия

Поскольку у вас нет интерфейса браузера для просмотра страницы, перейдите по адресу http://localhost:9222 в другом браузере, чтобы проверить, всё ли работает. Вы увидите список страниц, которые можно проверить, перейдя по ссылкам и увидев, что отображает Headless:

DevTools Remote
Пользовательский интерфейс удаленной отладки DevTools

Отсюда вы можете использовать привычные функции DevTools для проверки, отладки и настройки страницы, как обычно. Если вы используете Headless программно, эта страница также является мощным инструментом отладки, позволяющим увидеть все необработанные команды протокола DevTools, передаваемые по сети и взаимодействующие с браузером.

Программное использование (Node)

Кукловод

Puppeteer — это библиотека Node, разработанная командой Chrome. Она предоставляет высокоуровневый API для управления безголовым (или полнофункциональным) Chrome. Она похожа на другие библиотеки для автоматизированного тестирования, такие как Phantom и NightmareJS, но работает только с последними версиями Chrome.

Среди прочего, Puppeteer можно использовать для удобного создания скриншотов, PDF-файлов, навигации по страницам и получения информации об этих страницах. Я рекомендую эту библиотеку, если вы хотите быстро автоматизировать тестирование браузера. Она скрывает сложности протокола DevTools и берет на себя выполнение рутинных задач, таких как запуск отладочного экземпляра Chrome.

Установите его:

npm i --save puppeteer

Пример - вывод пользовательского агента

const puppeteer = require('puppeteer');

(async() => {
  const browser = await puppeteer.launch();
  console.log(await browser.version());
  await browser.close();
})();

Пример — создание скриншота страницы.

const puppeteer = require('puppeteer');

(async() => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://www.chromestatus.com', {waitUntil: 'networkidle2'});
  await page.pdf({path: 'page.pdf', format: 'A4'});

  await browser.close();
})();

Чтобы узнать больше о полном API, ознакомьтесь с документацией Puppeteer .

Библиотека CRI

chrome-remote-interface — это библиотека более низкого уровня, чем API Puppeteer. Я рекомендую её, если вы хотите работать с аппаратным обеспечением и использовать протокол DevTools напрямую.

Запуск Chrome

chrome-remote-interface не запускает Chrome автоматически, поэтому вам придётся позаботиться об этом самостоятельно.

В разделе CLI мы запустили Chrome вручную, используя --headless --remote-debugging-port=9222 . Однако для полной автоматизации тестов вам, вероятно, потребуется запустить Chrome из вашего приложения.

Один из способов — использовать child_process :

const execFile = require('child_process').execFile;

function launchHeadlessChrome(url, callback) {
  // Assuming MacOSx.
  const CHROME = '/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome';
  execFile(CHROME, ['--headless', '--disable-gpu', '--remote-debugging-port=9222', url], callback);
}

launchHeadlessChrome('https://www.chromestatus.com', (err, stdout, stderr) => {
  ...
});

Но если вам нужно портативное решение, работающее на разных платформах, возникают сложности. Взгляните хотя бы на этот жёстко закодированный путь к Chrome :(

Использование ChromeLauncher

Lighthouse — это замечательный инструмент для тестирования качества ваших веб-приложений. В Lighthouse был разработан надежный модуль для запуска Chrome, который теперь доступен для автономного использования. Модуль NPM chrome-launcher определит место установки Chrome, настроит экземпляр отладки, запустит браузер и завершит его работу после завершения программы. Самое приятное — он работает на разных платформах благодаря Node!

По умолчанию chrome-launcher попытается запустить Chrome Canary (если он установлен), но вы можете изменить это, чтобы вручную выбрать, какой Chrome использовать. Для этого сначала установите его из npm:

npm i --save chrome-launcher

Пример — использование chrome-launcher для запуска безголового режима.

const chromeLauncher = require('chrome-launcher');

// Optional: set logging level of launcher to see its output.
// Install it using: npm i --save lighthouse-logger
// const log = require('lighthouse-logger');
// log.setLevel('info');

/**
 * Launches a debugging instance of Chrome.
 * @param {boolean=} headless True (default) launches Chrome in headless mode.
 *     False launches a full version of Chrome.
 * @return {Promise<ChromeLauncher>}
 */
function launchChrome(headless=true) {
  return chromeLauncher.launch({
    // port: 9222, // Uncomment to force a specific port of your choice.
    chromeFlags: [
      '--window-size=412,732',
      '--disable-gpu',
      headless ? '--headless' : ''
    ]
  });
}

launchChrome().then(chrome => {
  console.log(`Chrome debuggable on port: ${chrome.port}`);
  ...
  // chrome.kill();
});

Запуск этого скрипта мало что изменит, но в диспетчере задач вы должны увидеть запущенный экземпляр Chrome, который загрузил about:blank . Помните, что никакого интерфейса браузера не будет. Мы работаем в безголовом режиме.

Для управления браузером нам необходим протокол DevTools!

Получение информации о странице

Для установки библиотеки:

npm i --save chrome-remote-interface
Примеры

Пример - вывод пользовательского агента

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

...

launchChrome().then(async chrome => {
  const version = await CDP.Version({port: chrome.port});
  console.log(version['User-Agent']);
});

В результате получается что-то вроде: HeadlessChrome/60.0.3082.0

Пример : проверка наличия у сайта манифеста веб-приложения.

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

...

(async function() {

const chrome = await launchChrome();
const protocol = await CDP({port: chrome.port});

// Extract the DevTools protocol domains we need and enable them.
// See API docs: https://chromedevtools.github.io/devtools-protocol/
const {Page} = protocol;
await Page.enable();

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

// Wait for window.onload before doing stuff.
Page.loadEventFired(async () => {
  const manifest = await Page.getAppManifest();

  if (manifest.url) {
    console.log('Manifest: ' + manifest.url);
    console.log(manifest.data);
  } else {
    console.log('Site has no app manifest');
  }

  protocol.close();
  chrome.kill(); // Kill Chrome.
});

})();

Пример : извлечение заголовка страницы <title> с помощью DOM API.

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

...

(async function() {

const chrome = await launchChrome();
const protocol = await CDP({port: chrome.port});

// Extract the DevTools protocol domains we need and enable them.
// See API docs: https://chromedevtools.github.io/devtools-protocol/
const {Page, Runtime} = protocol;
await Promise.all([Page.enable(), Runtime.enable()]);

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

// Wait for window.onload before doing stuff.
Page.loadEventFired(async () => {
  const js = "document.querySelector('title').textContent";
  // Evaluate the JS expression in the page.
  const result = await Runtime.evaluate({expression: js});

  console.log('Title of page: ' + result.result.value);

  protocol.close();
  chrome.kill(); // Kill Chrome.
});

})();

Использование Selenium, WebDriver и ChromeDriver

В настоящий момент Selenium запускает полноценный экземпляр Chrome. Другими словами, это автоматизированное решение, но не полностью безголовое. Однако Selenium можно настроить для запуска Chrome без графического интерфейса, приложив немного усилий. Я рекомендую использовать команду «Запуск Selenium с Chrome без графического интерфейса», если вам нужны полные инструкции по самостоятельной настройке, но ниже я привел несколько примеров, чтобы вы могли начать.

Использование ChromeDriver

ChromeDriver 2.32 использует Chrome 61 и хорошо работает с безголовым Chrome.

Установить:

npm i --save-dev selenium-webdriver chromedriver

Пример:

const fs = require('fs');
const webdriver = require('selenium-webdriver');
const chromedriver = require('chromedriver');

const chromeCapabilities = webdriver.Capabilities.chrome();
chromeCapabilities.set('chromeOptions', {args: ['--headless']});

const driver = new webdriver.Builder()
  .forBrowser('chrome')
  .withCapabilities(chromeCapabilities)
  .build();

// Navigate to google.com, enter a search.
driver.get('https://www.google.com/');
driver.findElement({name: 'q'}).sendKeys('webdriver');
driver.findElement({name: 'btnG'}).click();
driver.wait(webdriver.until.titleIs('webdriver - Google Search'), 1000);

// Take screenshot of results page. Save to disk.
driver.takeScreenshot().then(base64png => {
  fs.writeFileSync('screenshot.png', new Buffer(base64png, 'base64'));
});

driver.quit();

Использование WebDriverIO

WebDriverIO — это API более высокого уровня, созданный поверх Selenium WebDriver.

Установить:

npm i --save-dev webdriverio chromedriver

Пример: фильтрация функций CSS на chromestatus.com

const webdriverio = require('webdriverio');
const chromedriver = require('chromedriver');

const PORT = 9515;

chromedriver.start([
  '--url-base=wd/hub',
  `--port=${PORT}`,
  '--verbose'
]);

(async () => {

const opts = {
  port: PORT,
  desiredCapabilities: {
    browserName: 'chrome',
    chromeOptions: {args: ['--headless']}
  }
};

const browser = webdriverio.remote(opts).init();

await browser.url('https://www.chromestatus.com/features');

const title = await browser.getTitle();
console.log(`Title: ${title}`);

await browser.waitForText('.num-features', 3000);
let numFeatures = await browser.getText('.num-features');
console.log(`Chrome has ${numFeatures} total features`);

await browser.setValue('input[type="search"]', 'CSS');
console.log('Filtering features...');
await browser.pause(1000);

numFeatures = await browser.getText('.num-features');
console.log(`Chrome has ${numFeatures} CSS features`);

const buffer = await browser.saveScreenshot('screenshot.png');
console.log('Saved screenshot...');

chromedriver.stop();
browser.end();

})();

Скачайте старую версию Headless Chrome как chrome-headless-shell

Начиная с версии Chrome 112, доступен новый безголовый режим ( --headless=new ) . Этот режим позволяет разработчикам запускать сам Chrome, а не отдельный исполняемый файл, в автоматической среде без видимого пользовательского интерфейса — это полезно для тестирования и автоматизации.

Для старого безголового режима и нового безголового режима существуют разные сценарии использования:

  1. Старая оболочка Headless представляет собой легковесную обертку над модулем //content в Chromium, и поэтому имеет значительно меньше зависимостей. В частности, она не требует X11/Wayland, D-Bus и в некоторых отношениях более производительна, чем полноценный браузер Chrome. Это делает ее подходящей для таких задач, как автоматическое создание скриншотов или веб-скрейпинг.
  2. Новый безголовый режим, с другой стороны, представляет собой настоящий браузер Chrome, а значит, более аутентичен, надежен и предлагает больше возможностей. Это делает его более подходящим для высокоточного сквозного тестирования веб-приложений или расширений для браузеров.

Другими словами, существует компромисс между производительностью и аутентичностью. Какой безголовый режим подходит вам больше всего? Это зависит от ваших конкретных задач.

Диаграмма, иллюстрирующая информацию, представленную в предыдущем списке.

Разработчикам и тестировщикам, которым не требуется полный функционал Chrome для автоматизации тестирования, возможно, стоит использовать старую версию Headless. В противном случае, новая версия Headless, вероятно, будет лучшим выбором.

Чтобы разработчики и тестировщики по-прежнему могли выбирать между этими двумя вариантами, мы рады сообщить, что старая реализация Headless теперь доступна в виде отдельного исполняемого файла chrome-headless-shell . Эти новые исполняемые файлы chrome-headless-shell генерируются для каждого выпуска Chrome, предназначенного для пользователей, и доступны для загрузки через инфраструктуру Chrome for Testing, начиная с Chrome 120.

Как мне получить бинарные файлы chrome-headless-shell ?

Как и в случае с другими исполняемыми файлами Chrome для тестирования, самый простой способ загрузить chrome-headless-shell для вашей платформы — использовать нашу утилиту командной строки @puppeteer/browsers , доступную через npm . Вот несколько примеров:

# Download the latest available `chrome-headless-shell` binary corresponding to the Stable channel.
npx @puppeteer/browsers install chrome-headless-shell@stable

# Download a specific `chrome-headless-shell` version.
npx @puppeteer/browsers install chrome-headless-shell@120.0.6098.0

Если вы предпочитаете создавать собственные автоматизированные скрипты для загрузки бинарных файлов chrome-headless-shell , мы вам поможем. Chrome for Testing предоставляет конечные точки JSON API с последними доступными версиями для каждого канала выпуска Chrome (Stable, Beta, Dev и Canary). Для быстрого ознакомления с текущим состоянием обратитесь к панели мониторинга доступности Chrome for Testing .

Обратная связь

Мы будем рады получить ваши отзывы о chrome-headless-shell . Если у вас возникнут какие-либо проблемы, пожалуйста, сообщите о них .

Дополнительные ресурсы

Вот несколько полезных ресурсов, которые помогут вам начать:

Документы

Инструменты

  • chrome-remote-interface — модуль Node.js, который использует протокол DevTools.
  • Lighthouse — автоматизированный инструмент для тестирования качества веб-приложений; активно использует протокол.
  • chrome-launcher — модуль Node.js для запуска Chrome, готовый к автоматизации.

Демонстрации

  • « Безголовая сеть » — отличная статья в блоге Пола Кинлана об использовании безголовой сети с api.ai.

Часто задаваемые вопросы

Нужен ли мне флаг --disable-gpu ?

Только в Windows. На других платформах это больше не требуется. Флаг --disable-gpu — это временное решение для нескольких ошибок. В будущих версиях Chrome этот флаг вам не понадобится. Дополнительную информацию см. на crbug.com/737678 .

Значит, мне всё ещё нужен Xvfb?

Нет. В безголовом Chrome не используется окно, поэтому сервер отображения, такой как Xvfb, больше не нужен. Вы можете спокойно запускать свои автоматизированные тесты и без него.

Что такое Xvfb? Xvfb — это сервер отображения в оперативной памяти для Unix-подобных систем, позволяющий запускать графические приложения (например, Chrome) без подключенного физического дисплея. Многие используют Xvfb для запуска более ранних версий Chrome в целях тестирования в «безголовом» режиме.

Как создать Docker-контейнер, в котором будет работать безголовый Chrome?

Посмотрите lighthouse-ci . Там есть пример Dockerfile , который использует node:8-slim в качестве базового образа, устанавливает и запускает Lighthouse на App Engine Flex.

Можно ли использовать это с Selenium / WebDriver / ChromeDriver ?

Да. См. раздел «Использование Selenium, WebDriver и ChromeDriver» .

Как это связано с PhantomJS?

Headless Chrome похож на такие инструменты, как PhantomJS . Оба можно использовать для автоматизированного тестирования в безголовой среде. Основное различие между ними заключается в том, что Phantom использует более старую версию WebKit в качестве движка рендеринга, в то время как Headless Chrome использует последнюю версию Blink.

В настоящий момент Phantom также предоставляет API более высокого уровня, чем протокол DevTools .

Куда мне сообщать об ошибках?

Сообщения об ошибках в Headless Chrome следует отправлять на crbug.com .

Сообщения об ошибках в протоколе DevTools следует отправлять на github.com/ChromeDevTools/devtools-protocol .