Pierwsze kroki z Chrome bez interfejsu graficznego

TL;DR

Chrome bez interfejsu graficznego jest już dostępny w Chrome 59. Ten sposób pozwala na uruchamianie przeglądarki Chrome w środowisku bez interfejsu graficznego. W zasadzie uruchamianie Chrome bez Chrome! Obejmuje on wszystkie nowoczesne funkcje platformy internetowej udostępniane przez Chromium i mechanizm renderowania Blink.

Dlaczego to takie przydatne?

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

Uruchamianie interfejsu bez interfejsu graficznego (CLI)

Najłatwiejszym sposobem na rozpoczęcie korzystania z trybu bez interfejsu graficznego jest otwarcie pliku binarnego Chrome z poziomu wiersza poleceń. Jeśli masz Chrome w wersji 59 lub nowszej, 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.

Plik chrome powinien wskazywać instalację Chrome. Dokładna lokalizacja zależy od platformy. Używam Maca, więc utworzyłem wygodne aliasy dla każdej zainstalowanej wersji Chrome.

Jeśli używasz wersji stabilnej Chrome i nie możesz uzyskać dostępu do 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"

Pobierz Chrome Canary tutaj.

Funkcje wiersza poleceń

W niektórych przypadkach nie trzeba programować skryptu Chrome bez interfejsu graficznego. Istnieje kilka przydatnych flag wiersza poleceń do wykonywania typowych zadań.

Drukowanie DOM

Flaga --dump-dom drukuje element document.body.innerHTML, który wypuszcza:

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

Utwórz plik PDF

Flaga --print-to-pdf tworzy plik PDF ze stroną:

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/

Uruchomienie polecenia --screenshot spowoduje utworzenie pliku o nazwie screenshot.png w bieżącym katalogu roboczym. Jeśli szukasz zrzutów ekranu na całej stronie, jest to nieco bardziej skomplikowane. Na blogu Davida Schnurra znajdziesz świetny post na ten temat. Zapoznaj się z artykułem Używanie Chrome bez interfejsu graficznego jako automatycznego narzędzia do robienia zrzutów ekranu .

Tryb REPL (pętla odczytu i drukowania)

Flaga --repl działa w trybie bez interfejsu graficznego, 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
$

Debugować Chrome bez interfejsu przeglądarki?

Gdy uruchomisz Chrome z atrybutem --remote-debugging-port=9222, uruchamia się instancja z włączonym protokołem DevTools. Protokół służy do komunikacji z Chrome i napędzania instancji przeglądarki bez interfejsu graficznego. Narzędzia takie jak Sublime, VS Code i Node używają też narzędzi do zdalnego debugowania aplikacji. #synergy

W przeglądarce nie ma interfejsu, który pozwala wyświetlić tę stronę, dlatego w innej przeglądarce otwórz http://localhost:9222, aby sprawdzić, czy wszystko działa. Zobaczysz listę stron do sprawdzenia, które możesz kliknąć, aby sprawdzić, co jest renderowane bez interfejsu graficznego:

Narzędzia dla programistów – zdalne
Interfejs zdalnego debugowania w Narzędziach deweloperskich

Możesz tam w zwykły sposób badać, debugować i dostosowywać stronę za pomocą znanych funkcji Narzędzi deweloperskich. Jeśli korzystasz z interfejsu bez interfejsu graficznego, ta strona jest również zaawansowanym narzędziem do debugowania, które pozwala zobaczyć wszystkie nieprzetworzone polecenia protokołu DevTools, które komunikują się z przeglądarką.

Używanie programistyczne (węzeł)

Animator

Puppeteer to biblioteka węzłów opracowana przez zespół Chrome. Udostępnia ona ogólny interfejs API, który umożliwia sterowanie przeglądarką Chrome bez interfejsu graficznego (lub pełnego). Jest podobny do innych bibliotek do automatycznego testowania, takich jak Phantom czy NightmareJS, ale działa tylko w najnowszych wersjach Chrome.

Za pomocą aplikacji Puppeteer można między innymi łatwo robić zrzuty ekranu, tworzyć pliki PDF, poruszać się po stronach i pobierać o nich informacje. Polecam tę bibliotekę, jeśli chcesz szybko zautomatyzować testowanie przeglądarki. Ukrywa złożoność protokołu DevTools i zajmuje się nadmiarowymi zadaniami, takimi jak uruchamianie instancji debugowania Chrome.

Zainstaluj:

npm i --save puppeteer

Przykład – wydrukuj klienta użytkownika

const puppeteer = require('puppeteer');

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

Przykład – robienie zrzutu 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();
})();

Aby dowiedzieć się więcej o pełnym interfejsie API, zapoznaj się z dokumentacją Puppeteer.

Biblioteka CRI

chrome-remote-interface to biblioteka niższego poziomu niż interfejs API Puppeteer. Zalecamy korzystanie z tego narzędzia, jeśli chcesz korzystać z interfejsu bezpośredniego i bezpośrednio korzystać z protokołu DevTools.

Uruchamiam Chrome

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

W sekcji interfejsu wiersza poleceń uruchomiliśmy Chrome ręcznie przy użyciu przeglądarki --headless --remote-debugging-port=9222. Jednak aby w pełni zautomatyzować testy, musisz uruchomić Chrome z poziomu swojej aplikacji.

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

Jeśli jednak potrzebujesz przenośnego rozwiązania działającego na wielu platformach, może to być trudne. Spójrz na zakodowaną na stałe ścieżkę do Chrome :(

Korzystanie z ChromeLauncher

Lighthouse to świetne narzędzie do testowania jakości aplikacji internetowych. Niezawodny moduł do uruchamiania Chrome został opracowany w Lighthouse, a teraz jest wyodrębniany do użytku niezależnego. Moduł chrome-launcher NPM sprawdzi, gdzie zainstalowano Chrome, skonfiguruje instancję debugowania, uruchomi przeglądarkę i zamknie ją po zakończeniu programu. A najlepsze jest to, że dzięki Node działa na różnych platformach.

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

npm i --save chrome-launcher

Przykład – użycie 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 tego skryptu nie daje dużo, ale powinno być widoczne wystąpienie Chrome w menedżerze zadań, który załadował about:blank. Pamiętaj, że przeglądarka nie będzie miała interfejsu użytkownika. Brakuje nam głowy.

Do sterowania przeglądarką potrzebny jest protokół DevTools.

Pobieranie informacji o stronie

Zainstalujmy bibliotekę:

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

Przykład – wydrukuj klienta 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 podobne do tych: 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ębnianie <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.
});

})();

na podstawie Selenium, WebDriver i ChromeDriver.

W tej chwili Selenium otwiera pełną instancję Chrome. Innymi słowy, jest to automatyczne rozwiązanie, ale nie jest całkowicie pozbawione interfejsu graficznego. Selenium można jednak skonfigurować tak, aby przeglądarka Chrome działała bez interfejsu graficznego. Jeśli potrzebujesz szczegółowych instrukcji dotyczących konfiguracji tej funkcji, polecam uruchamianie Selenium z Chrome bez interfejsu graficznego. Na początek zamieszczam kilka przykładów.

Korzystanie z ChromeDriver

ChromeDriver 2.32 korzysta z Chrome 61 i działa dobrze z 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 niż 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 materiałów na początek:

Dokumentacja

Narzędzia,

  • chrome-remote-interface – moduł węzłów obejmujący protokół DevTools
  • 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” – Paul Kinlan opublikował na blogu posta na temat stosowania interfejsu graficznego z interfejsem api.ai.

Najczęstsze pytania

Czy potrzebuję flagi --disable-gpu?

Tylko w systemie Windows. Inne platformy nie potrzebują go już. Flaga --disable-gpu to tymczasowe rozwiązanie do usunięcia kilku błędów. Ta flaga nie będzie już potrzebna w kolejnych wersjach Chrome. Więcej informacji znajdziesz na crbug.com/737678.

Czyli nadal potrzebuję Xvfb?

Nie. Chrome bez interfejsu graficznego nie używa okna, więc serwer displayowy taki jak Xvfb nie jest już potrzebny. Możesz przeprowadzać testy automatyczne bez tego narzędzia.

Co to jest Xvfb? Xvfb to działający w pamięci serwer z wyświetlaczem dla systemów typu Unix, który pozwala uruchamiać aplikacje graficzne (np. Chrome) bez podłączonego fizycznego wyświetlacza. Wiele osób używa Xvfb do uruchamiania wcześniejszych wersji Chrome, aby przeprowadzać testy bez interfejsu graficznego.

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

Zajrzyj do: Lighthouse-ci. Zawiera przykładowy plik Dockerfile, który jako obraz podstawowy wykorzystuje node:8-slim, instaluje i uruchamia Lighthouse w App Engine Flex.

Czy mogę z tego korzystać w Selelenium, WebDriver lub ChromeDriver?

Tak. Zobacz Korzystanie z Selenium, WebDriver i ChromeDriver.

Jaki jest to związek z kodem PhantomJS?

Chrome bez interfejsu graficznego jest podobny do narzędzi takich jak PhantomJS. Z obu rozwiązań można korzystać do automatycznego testowania w środowisku bez interfejsu graficznego. Główna różnica między nimi polega na tym, że mechanizm renderowania wykorzystuje starszą wersję WebKit, a Chrome bez interfejsu graficznego korzysta z najnowszej wersji Blink.

Obecnie Phantom zapewnia też interfejs API wyższego poziomu niż protokół DevTools.

Gdzie zgłaszać błędy?

Błędy dotyczące Chrome bez interfejsu graficznego zgłaszaj na stronie crbug.com.

Błędy związane z protokołem DevTools zgłoś na stronie github.com/ChromeDevTools/devtools-protocol.