Erste Schritte mit Headless Chrome

Kurzfassung

Headless Chrome ist ab Chrome 59 verfügbar. Damit kann der Chrome-Browser in einer monitorlosen Umgebung ausgeführt werden. Chrome ohne Chrome auszuführen! Damit stehen alle modernen Webplattformfunktionen von Chromium und der Blink-Rendering-Engine in die Befehlszeile zur Verfügung.

Warum ist das nützlich?

Ein monitorloser Browser ist ein großartiges Tool für automatisierte Tests und Serverumgebungen, in denen Sie keine sichtbare UI-Shell benötigen. Sie können beispielsweise Tests mit einer echten Webseite durchführen, eine PDF-Datei davon erstellen oder einfach prüfen, wie der Browser eine URL rendert.

Monitorlos starten (CLI)

Die einfachste Möglichkeit für den Einstieg in den monitorlosen Modus besteht darin, das Chrome-Binärprogramm über die Befehlszeile zu öffnen. Wenn Sie Chrome 59 oder höher installiert haben, starten Sie Chrome mit dem 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.

chrome sollte auf Ihre Installation von Chrome verweisen. Der genaue Ort variiert je nach Plattform. Da ich mit einem Mac bin, habe ich praktische Aliase für jede installierte Chrome-Version erstellt.

Wenn Sie die stabile Version von Chrome verwenden und die Betaversion nicht erhalten können, empfehlen wir die Verwendung von 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 herunterladen

Befehlszeilenfunktionen

In einigen Fällen müssen Sie kein programmatisches Script für die monitorlose Chrome-Version erstellen. Es gibt einige nützliche Befehlszeilen-Flags, um gängige Aufgaben auszuführen.

DOM drucken

Das Flag --dump-dom gibt document.body.innerHTML in stdout aus:

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

PDF erstellen

Das Flag --print-to-pdf erstellt eine PDF-Datei der Seite:

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

Screenshots erstellen

Verwenden Sie das Flag --screenshot, um einen Screenshot einer Seite zu erstellen:

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/

Bei der Ausführung mit --screenshot wird im aktuellen Arbeitsverzeichnis eine Datei mit dem Namen screenshot.png erstellt. Wenn ihr nach ganzseitigen Screenshots sucht, ist die Sache etwas komplizierter. Es gibt einen großartigen Blogpost von David Schnurr, der Sie behandelt. Weitere Informationen finden Sie unter Die monitorlose Chrome-Version als automatisiertes Screenshot-Tool verwenden .

REPL-Modus (Lese-Eval-Print-Schleife)

Das Flag --repl wird monitorlos in einem Modus ausgeführt, in dem Sie JS-Ausdrücke im Browser direkt über die Befehlszeile auswerten können:

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

Sie beheben Chrome ohne Browser-UI?

Wenn Sie Chrome mit --remote-debugging-port=9222 ausführen, wird eine Instanz mit aktiviertem DevTools-Protokoll gestartet. Das Protokoll wird für die Kommunikation mit Chrome und zum Steuern der monitorlosen Browserinstanz verwendet. Es ist auch das, was Tools wie Sublime, VS Code und Node für das Remote-Debugging einer Anwendung verwenden. #synergy

Da Sie keine Browser-UI zum Anzeigen der Seite haben, rufen Sie http://localhost:9222 in einem anderen Browser auf, um zu prüfen, ob alles funktioniert. Sie sehen dann eine Liste von Inspectable-Seiten, auf die Sie klicken können, um zu sehen, was Headless rendert:

Entwicklertools-Fernbedienung
UI zur Remote-Fehlerbehebung in Entwicklertools

Von hier aus kannst du die bekannten Entwicklertools wie gewohnt verwenden, um die Seite zu untersuchen, zu debuggen und zu optimieren. Wenn Sie Headless programmatisch verwenden, ist diese Seite auch ein leistungsstarkes Debugging-Tool, mit dem alle unformatierten Entwicklertools-Protokollbefehle angezeigt werden, die mit dem Browser kommunizieren.

Programmgesteuert verwenden (Node)

Puppenspieler

Puppeteer ist eine Node-Bibliothek, die vom Chrome-Team entwickelt wurde. Es bietet eine API auf höchster Ebene zur Steuerung der monitorlosen Chrome-Version (oder einer vollständigen Version). Sie ähnelt anderen automatischen Testbibliotheken wie Phantom und NightmareJS, funktioniert jedoch nur mit den neuesten Versionen von Chrome.

Mit Puppeteer können unter anderem ganz einfach Screenshots aufgenommen, PDFs erstellt, Seiten aufgerufen und Informationen über diese Seiten abgerufen werden. Ich empfehle die Bibliothek, wenn du Browsertests schnell automatisieren möchtest. Es verbirgt die Komplexität des Entwicklertools-Protokolls und erledigt redundante Aufgaben wie das Starten einer Fehlerbehebungsinstanz von Chrome.

Installieren:

npm i --save puppeteer

Beispiel: User-Agent drucken

const puppeteer = require('puppeteer');

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

Beispiel – Screenshot der Seite erstellen

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

In der Dokumentation von Puppeteer erfahren Sie mehr über die vollständige API.

CRI-Bibliothek

chrome-remote-interface ist eine untergeordnete Bibliothek als die API von Puppeteer. Diese Vorgehensweise empfiehlt sich, wenn Sie nah an der Metal-Lösung sein und das DevTools-Protokoll direkt verwenden möchten.

Chrome wird gestartet

„chrome-remote-interface“ startet Chrome nicht für Sie, Sie müssen sich also selbst darum kümmern.

Im Abschnitt zur Befehlszeile haben wir Chrome manuell mit --headless --remote-debugging-port=9222 gestartet. Für die vollständige Automatisierung von Tests können Sie Chrome jedoch über Ihre Anwendung erstellen.

Eine Möglichkeit ist die Verwendung von 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) => {
  ...
});

Wenn Sie jedoch eine mobile Lösung suchen, die auf mehreren Plattformen funktioniert, wird es schwierig. Seht euch einfach den hartcodierten Pfad zu Chrome an :(

ChromeLauncher verwenden

Lighthouse ist ein hervorragendes Tool zum Testen der Qualität Ihrer Web-Apps. Ein robustes Modul für die Einführung von Chrome wurde in Lighthouse entwickelt und wird jetzt für die eigenständige Verwendung extrahiert. Das NPM-Modul chrome-launcher ermittelt, wo Chrome installiert ist, richtet eine Debug-Instanz ein, startet den Browser und beendet ihn, wenn das Programm beendet ist. Das Beste daran ist, dass es dank Node! plattformübergreifend funktioniert.

chrome-launcher versucht standardmäßig, Chrome Canary zu starten, sofern Chrome Canary installiert ist. Sie können dies jedoch ändern, um manuell auszuwählen, welcher Chrome-Browser verwendet werden soll. Installieren Sie zuerst aus npm, um sie zu verwenden:

npm i --save chrome-launcher

Beispiel – Verwendung von chrome-launcher zum Starten von Headless

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

Dieses Skript bewirkt nicht viel, aber im Task-Manager sollte eine Chrome-Instanz ausgelöst werden, über die about:blank geladen wurde. Denken Sie daran, dass es keine Browser-Benutzeroberfläche gibt. Wir sind monitorlos.

Zur Steuerung des Browsers benötigen wir das DevTools-Protokoll.

Informationen über die Seite abrufen

Installieren wir die Bibliothek:

npm i --save chrome-remote-interface
Beispiele

Beispiel: User-Agent drucken

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

...

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

Ergebnisse in etwa: HeadlessChrome/60.0.3082.0

Beispiel – Überprüfen Sie, ob die Website ein Web-App-Manifest hat.

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

})();

Beispiel: Extrahieren Sie die <title> der Seite mithilfe von DOM APIs.

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 und ChromeDriver verwenden

Im Moment öffnet Selenium eine vollständige Instanz von Chrome. Es handelt sich also um eine automatisierte Lösung, aber nicht vollständig monitorlos. Selenium kann jedoch mit ein wenig Arbeitsaufwand zur Ausführung von monitorlosem Chrome konfiguriert werden. Ich empfehle Selenium mit monitorlosem Chrome ausführen, wenn Sie eine vollständige Anleitung zur Einrichtung selbst benötigen. Für den Einstieg finden Sie unten einige Beispiele.

ChromeDriver verwenden

ChromeDriver 2.32 verwendet Chrome 61 und funktioniert gut mit monitorlosem Chrome.

Installieren:

npm i --save-dev selenium-webdriver chromedriver

Beispiel:

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 verwenden

WebDriverIO ist eine API auf höherer Ebene, die auf Selenium WebDriver basiert.

Installieren:

npm i --save-dev webdriverio chromedriver

Beispiel: CSS-Funktionen auf chromestatus.com filtern

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

})();

Weitere Ressourcen

Hier sind einige nützliche Ressourcen für den Einstieg:

Docs

Tools

  • chrome-remote-interface – Knotenmodul, das das Entwicklertools-Protokoll umschließt
  • Lighthouse: automatisiertes Tool zum Testen der Qualität von Webanwendungen; verwendet das Protokoll intensiv
  • chrome-launcher – Knotenmodul zum Starten von Chrome, bereit für die Automatisierung

Demos

  • The Headless Web: Paul Kinlans großer Blogpost zur Verwendung von Headless mit api.ai

Häufig gestellte Fragen

Benötige ich das Flag --disable-gpu?

Nur unter Windows. Für andere Plattformen ist sie nicht mehr erforderlich. Das Flag --disable-gpu ist eine vorübergehende Problemumgehung für einige Programmfehler. Dieses Flag wird in zukünftigen Chrome-Versionen nicht mehr benötigt. Weitere Informationen finden Sie unter crbug.com/737678.

Ich brauche also trotzdem Xvfb?

Nein. Da monitorloser Chrome-Browser kein Fenster nutzt, wird ein Anzeigeserver wie Xvfb nicht mehr benötigt. Sie können Ihre automatisierten Tests auch ohne diese Software ausführen.

Was ist Xvfb? Xvfb ist ein In-Memory-Anzeigeserver für Unix-ähnliche Systeme, mit dem Sie grafische Anwendungen wie Chrome ohne angeschlossenes physisches Display ausführen können. Viele Nutzer verwenden Xvfb, um frühere Versionen von Chrome für "monitorlose" Tests auszuführen.

Wie erstelle ich einen Docker-Container, in dem Headless Chrome ausgeführt wird?

Dann sehen Sie sich lighthouse-ci an. Es enthält ein Beispiel-Dockerfile, das node:8-slim als Basis-Image verwendet und Lighthouse auf der App Engine Flex installiert und ausführt.

Kann ich diese Funktion mit Selenium / WebDriver / ChromeDriver verwenden?

Ja. Weitere Informationen finden Sie unter Selenium, WebDriver und ChromeDriver verwenden.

In welchem Zusammenhang steht dies mit PhantomJS?

Das monitorlose Chrome ähnelt Tools wie PhantomJS. Beide können für automatisierte Tests in einer monitorlosen Umgebung verwendet werden. Der Hauptunterschied zwischen den beiden besteht darin, dass Phantom eine ältere Version von WebKit als Rendering-Engine verwendet, während Headless Chrome die neueste Version von Blink verwendet.

Derzeit bietet Phantom auch eine API auf höherer Ebene als das DevTools-Protokoll.

Wo kann ich Fehler melden?

Fehler in der monitorlosen Chrome-Version können Sie unter crbug.com melden.

Fehler im DevTools-Protokoll können Sie unter github.com/ChromeDevTools/devtools-protocol melden.