Pierwsze kroki z Chrome bez interfejsu graficznego

TL;DR

Chrome bez interfejsu graficznego jest dostępny w wersji 59. Umożliwia on uruchamianie przeglądarki Chrome w środowisku bez interfejsu graficznego. Krótko mówiąc, korzystanie z Chrome bez Chrome. Wprowadza do wiersza poleceń wszystkie nowoczesne funkcje platformy internetowej udostępniane przez Chromium i silnik renderowania Blink.

Dlaczego jest to przydatne?

Przeglądarka bez interfejsu graficznego to świetne narzędzie do automatycznego testowania i stosowania środowisk serwera, w których nie potrzebujesz widocznej powłoki UI. Możesz na przykład przeprowadzić testy na rzeczywistej stronie internetowej, utworzyć jej plik PDF lub po prostu sprawdzić, jak przeglądarka renderuje URL.

Uruchamiam bez interfejsu graficznego (interfejs wiersza poleceń)

Najłatwiejszym sposobem na rozpoczęcie pracy w trybie bez interfejsu graficznego jest otwarcie pliku binarnego Chrome z poziomu wiersza poleceń. Jeśli masz zainstalowaną przeglądarkę Chrome 59 lub nowszą, uruchom ją z flagą --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.

Adres chrome powinien wskazywać Twoją instalację Chrome. Dokładna lokalizacja różni się w zależności od platformy. Używam Maca, więc utworzyłem wygodne aliasy dla każdej zainstalowanej wersji Chrome.

Jeśli korzystasz z wersji stabilnej Chrome, a nie możesz pobrać wersji beta, zalecamy użycie 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 możesz pobrać stąd.

Funkcje wiersza poleceń

W niektórych przypadkach nie trzeba programować tworzenia skryptów w Chrome bez interfejsu graficznego. W wierszu poleceń jest kilka przydatnych flag wiersza poleceń, które ułatwiają wykonywanie typowych zadań.

Drukowanie DOM

Flaga --dump-dom pokazuje document.body.innerHTML do standardowego protokołu:

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

Utwórz PDF

Flaga --print-to-pdf tworzy plik PDF strony:

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

Robienie zrzutów ekranu

Aby zrobić zrzut ekranu strony, użyj flagi --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/

Wykonanie polecenia --screenshot spowoduje utworzenie w bieżącym katalogu roboczym pliku o nazwie screenshot.png. Zrzuty całej strony są nieco większe. Na blogu Davida Schnurra znajdziesz świetny post na blogu. Zapoznaj się z artykułem Używanie Chrome bez interfejsu graficznego jako automatycznego narzędzia do tworzenia zrzutów ekranu .

Tryb REPL (pętla odczytu-eval-print)

Flaga --repl uruchamia się w trybie bez interfejsu graficznego w trybie, w którym możesz oceniać wyrażenia JS w przeglądarce, bezpośrednio z wiersza poleceń:

$ 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
$

debugujesz Chrome bez użycia interfejsu przeglądarki?

Kiedy używasz Chrome z --remote-debugging-port=9222, uruchamiana jest instancja z włączonym protokołem Narzędzi deweloperskich. Protokół ten służy do komunikacji z Chrome i kierowania instancji przeglądarki bez interfejsu graficznego. Umożliwia ona też zdalne debugowanie aplikacji za pomocą takich narzędzi jak Sublime, VS Code i Node. #synergy

Ponieważ nie masz interfejsu przeglądarki, który pozwala wyświetlić stronę, otwórz http://localhost:9222 w innej przeglądarce, aby sprawdzić, czy wszystko działa. Pojawi się lista stron, które można sprawdzić, aby sprawdzić, co renderuje bez interfejsu graficznego:

Pilot do Narzędzi deweloperskich
Interfejs zdalnego debugowania w Narzędziach deweloperskich

Możesz tu sprawdzać, debugować i dostosowywać stronę w zwykły sposób, korzystając z znanych funkcji Narzędzi deweloperskich. Jeśli używasz interfejsu bez interfejsu graficznego, ta strona jest też zaawansowanym narzędziem do debugowania, w którym zobaczysz wszystkie nieprzetworzone polecenia protokołu DevNarzędzia przechodzące przez przewód, komunikujące się z przeglądarką.

Korzystanie z automatyzacji (węzeł)

Animator

Puppeteer to biblioteka węzłów opracowana przez zespół Chrome. Udostępnia ogólny interfejs API do sterowania Chrome bez interfejsu graficznego (lub z pełną funkcjonalnością). Działa ona podobnie do innych bibliotek testowania automatycznego, takich jak Phantom czy NightmareJS, ale działa tylko z najnowszymi wersjami Chrome.

Aplikacja Puppeteer umożliwia m.in. łatwe wykonywanie zrzutów ekranu, tworzenie plików PDF, poruszanie się po stronach i pobieranie informacji o nich. Polecam bibliotekę, jeśli chcecie szybko zautomatyzować testowanie przeglądarki. Ukrywa ono złożoność protokołu Narzędzi deweloperskich i zajmuje się nadmiarowymi zadaniami, takimi jak uruchamianie instancji debugowania Chrome.

Zainstaluj:

npm i --save puppeteer

Przykład – wydrukowanie informacji o kliencie użytkownika.

const puppeteer = require('puppeteer');

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

Przykład – zrzut ekranu strony

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

Więcej informacji o pełnej wersji interfejsu API znajdziesz w dokumentacji Puppeteer.

Biblioteka CRI

chrome-remote-interface to biblioteka niższego poziomu niż interfejs API Puppeteer. Polecam go, jeśli chcesz zbliżyć się do metalu i korzystać z protokołu DevTools.

Uruchamiam Chrome

chrome-remote-interface nie uruchamia za Ciebie Chrome, więc musisz zająć się tym samodzielnie.

W sekcji interfejsu wiersza poleceń uruchomiliśmy Chrome ręcznie za pomocą --headless --remote-debugging-port=9222. Jednak aby w pełni zautomatyzować testy, trzeba będzie prawdopodobnie wygenerować Chrome z aplikacji.

Jednym ze sposobów jest użycie polecenia 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) => {
  ...
});

Jednak jeśli potrzebujesz przenośnego rozwiązania działającego na wielu platformach, będziesz mieć problemy. Spójrzmy tylko na zakodowaną na stałe ścieżkę do Chrome :(

Korzystanie z ChromeLauncher

Lighthouse to wspaniałe narzędzie do testowania jakości aplikacji internetowych. W Lighthouse został opracowany rozbudowany moduł umożliwiający uruchamianie Chrome, który teraz można z niego wydobyć w trybie samodzielnym. Moduł NPM chrome-launcher znajdzie informacje o tym, gdzie zainstalowano Chrome, skonfiguruje instancję debugowania, uruchomi przeglądarkę i zamknie ją, gdy program będzie gotowy. A najlepsze jest to, że dzięki Node.js działa na wielu platformach.

Domyślnie chrome-launcher spróbuje uruchomić Chrome Canary (jeśli jest zainstalowana), ale możesz to zmienić, aby ręcznie wybrać Chrome, którego chcesz używać. Aby użyć tej funkcji, najpierw zainstaluj ją z npm:

npm i --save chrome-launcher

Przykład – użycie interfejsu chrome-launcher do uruchomienia interfejsu bez interfejsu graficznego

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

Uruchomienie skryptu niewiele daje, ale w menedżerze zadań, który wczytał about:blank, powinno się uruchomić wystąpienie Chrome. Pamiętaj, że nie będzie żadnego interfejsu przeglądarki. Nie mamy głowy.

Do kontrolowania przeglądarki potrzebny jest protokół Narzędzi deweloperskich.

Pobieranie informacji o stronie

Zainstalujmy bibliotekę:

npm i --save chrome-remote-interface
Przykłady

Przykład – wydrukowanie informacji o kliencie użytkownika.

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

...

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

Wyniki w rodzaju: HeadlessChrome/60.0.3082.0

Przykład – sprawdź, czy witryna ma manifest aplikacji internetowej

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

})();

Przykład: wyodrębnij <title> strony za pomocą interfejsów 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.
});

})();

Używanie Selenium, WebDriver i ChromeDriver

Obecnie Selenium otwiera pełną instancję Chrome. Innymi słowy, jest to rozwiązanie automatyczne, ale nie całkowicie pozbawione interfejsu graficznego. Selenium można jednak skonfigurować tak, żeby bez interfejsu graficznego Chrome działał z niewielkim nakładem pracy. Jeśli potrzebujesz szczegółowych instrukcji konfiguracji, zalecamy uruchomienie Selenium w Chrome bez interfejsu graficznego. Na początek skorzystam z poniższych przykładów.

Przy użyciu ChromeDriver

ChromeDriver 2.32 korzysta z Chrome 61 i działa dobrze w przypadku Chrome bez interfejsu graficznego.

Zainstaluj:

npm i --save-dev selenium-webdriver chromedriver

Przykład:

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();

Korzystanie z WebDriverIO

WebDriverIO to interfejs API wyższego poziomu będący alternatywą dla Selenium WebDriver.

Zainstaluj:

npm i --save-dev webdriverio chromedriver

Przykład: filtrowanie funkcji CSS na stronie 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();

})();

Dodatkowe zasoby

Oto kilka przydatnych zasobów na początek:

Dokumenty

Narzędzia

  • chrome-remote-interface – moduł węzła opakowujący protokół Narzędzi deweloperskich
  • Lighthouse – automatyczne narzędzie do testowania jakości aplikacji internetowych, które intensywnie korzysta z protokołu
  • chrome-launcher – moduł węzła do uruchamiania Chrome, gotowy do automatyzacji.

Przykłady

  • The Headless Web” – świetny post Paula Kinlana na temat korzystania z interfejsu Headless i api.ai.

Najczęstsze pytania

Czy potrzebuję flagi --disable-gpu?

Tylko w systemie Windows. Na innych platformach nie jest on już wymagany. Flaga --disable-gpu to tymczasowe rozwiązanie w przypadku kilku błędów. Ta flaga nie jest potrzebna w kolejnych wersjach Chrome. Więcej informacji znajdziesz na stronie crbug.com/737678.

Czy w dalszym ciągu potrzebuję Xvfb?

Nie. Chrome bez interfejsu graficznego nie używa okna, więc serwer wyświetlania taki jak Xvfb nie jest już potrzebny. Możesz bez niego przeprowadzać automatyczne testy.

Co to jest Xvfb? Xvfb to wewnętrzny serwer wyświetlania dla systemów typu Unix, który pozwala uruchamiać aplikacje graficzne (takie jak Chrome) bez podłączonego fizycznego wyświetlacza. Wiele osób używa Xvfb do uruchamiania wcześniejszych wersji Chrome do testów bez interfejsu graficznego.

Jak utworzyć kontener Dockera, który będzie obsługiwać Chrome bez interfejsu graficznego?

Spójrz na to miejsce: Lighthouse-ci. Zawiera przykładowy plik Dockerfile, który używa node:8-slim jako obrazu podstawowego, instaluje i uruchamia Lighthouse w App Engine Flex.

Czy mogę używać tego rozwiązania z usługą Selenium / WebDriver / ChromeDriver?

Tak. Zobacz Using Selenium, WebDriver i ChromeDriver.

Jaki jest związek z PhantomJS?

Chrome bez interfejsu graficznego jest podobny do narzędzi takich jak PhantomJS. Oba modele można stosować do automatycznych testów w środowisku bez interfejsu graficznego. {/1}

Obecnie Phantom zapewnia też interfejs API wyższego poziomu niż protokół narzędzi deweloperskich.

Gdzie zgłaszać błędy?

Błędy dotyczące Chrome bez interfejsu graficznego możesz zgłaszać na stronie crbug.com.

Błędy w protokole narzędzi deweloperskich możesz zgłaszać na stronie github.com/ChromeDevTools/devtools-protocol.