Erste Schritte mit Headless Chrome

.

Kurzfassung

Headless Chrome ist in Chrome 59 verfügbar. So können Sie den Chrome-Browser in einer headless-Umgebung ausführen. Im Grunde wird Chrome ohne Chrome ausgeführt. Es bietet alle modernen Webplattformfunktionen, die von Chromium und der Blink-Rendering-Engine bereitgestellt werden, in der Befehlszeile.

Warum ist das nützlich?

Ein headless Browser ist ein hervorragendes Tool für automatisierte Tests und Serverumgebungen, in denen keine sichtbare UI-Shell erforderlich ist. Sie können beispielsweise einige Tests auf einer echten Webseite ausführen, eine PDF-Datei davon erstellen oder einfach prüfen, wie der Browser eine URL rendert.

Headless starten (Befehlszeile)

Am einfachsten starten Sie den headless-Modus, indem Sie die Chrome-Binärdatei über die Befehlszeile ö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 Chrome-Installation verweisen. Die genaue Position variiert je nach Plattform. Da ich einen Mac verwende, habe ich für jede installierte Chrome-Version praktische Aliasse erstellt.

Wenn Sie die stabile Version von Chrome verwenden und die Betaversion nicht erhalten können, empfehle ich Folgendes: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"

Hier können Sie Chrome Canary herunterladen.

Befehlszeilenfunktionen

In einigen Fällen müssen Sie Headless Chrome möglicherweise nicht programmgesteuert scripten. Es gibt einige nützliche Befehlszeilenoptionen für gängige Aufgaben.

DOM drucken

Mit dem Flag --dump-dom wird document.body.innerHTML an stdout ausgegeben:

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

PDF erstellen

Mit dem Flag --print-to-pdf wird eine PDF-Datei der Seite erstellt:

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

Screenshots erstellen

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

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/

Wenn Sie das Tool mit --screenshot ausführen, wird im aktuellen Arbeitsverzeichnis eine Datei namens screenshot.png erstellt. Wenn Sie Screenshots der gesamten Seite benötigen, ist das etwas aufwendiger. David Schnurr hat dazu einen hilfreichen Blogpost verfasst. Weitere Informationen finden Sie unter Headless Chrome als automatisiertes Screenshot-Tool verwenden .

REPL-Modus (Read-Eval-Print-Loop)

Mit dem Flag --repl wird Headless in einem Modus ausgeführt, in dem Sie JS-Ausdrücke direkt über die Befehlszeile im Browser 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
$

Chrome ohne Browser-Benutzeroberfläche debuggen?

Wenn Sie Chrome mit --remote-debugging-port=9222 ausführen, wird eine Instanz mit aktiviertem DevTools-Protokoll gestartet. Das Protokoll wird verwendet, um mit Chrome zu kommunizieren und die Headless-Browserinstanz zu steuern. Außerdem wird es von Tools wie Sublime, VS Code und Node für das Remote-Debuggen einer Anwendung verwendet. #synergy

Da Sie keine Browseroberfläche haben, um die Seite zu sehen, rufen Sie http://localhost:9222 in einem anderen Browser auf, um zu prüfen, ob alles funktioniert. Es wird eine Liste der zu prüfenden Seiten angezeigt, durch die Sie sich klicken können, um zu sehen, was mit Headless gerendert wird:

DevTools Remote
Remote-Debugging-Benutzeroberfläche der DevTools

Hier können Sie die bekannten DevTools-Funktionen verwenden, um die Seite wie gewohnt zu prüfen, zu debuggen und zu optimieren. Wenn Sie Headless-Browser programmatisch verwenden, ist diese Seite auch ein leistungsstarkes Tool zum Entfernen von Fehlern, mit dem Sie alle Rohbefehle des DevTools-Protokolls sehen können, die über die Verbindung mit dem Browser gesendet werden.

Programmatische Verwendung (Node)

Puppeteer

Puppeteer ist eine Node-Bibliothek, die vom Chrome-Team entwickelt wurde. Sie bietet eine High-Level-API zur Steuerung von headless (oder vollständigem) Chrome. Sie ähnelt anderen Bibliotheken für automatisierte Tests wie Phantom und NightmareJS, funktioniert aber nur mit den neuesten Versionen von Chrome.

Mit Puppeteer können Sie unter anderem ganz einfach Screenshots erstellen, PDFs erstellen, Seiten aufrufen und Informationen zu diesen Seiten abrufen. Ich empfehle die Bibliothek, wenn Sie Browsertests schnell automatisieren möchten. Es verbirgt die Komplexität des DevTools-Protokolls und übernimmt redundante Aufgaben wie das Starten einer Debug-Instanz von Chrome.

So installieren Sie es:

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

Weitere Informationen zur vollständigen API finden Sie in der Dokumentation zu Puppeteer.

CRI-Bibliothek

chrome-remote-interface ist eine Bibliothek auf niedrigerer Ebene als die API von Puppeteer. Ich empfehle es, wenn Sie direkt auf die Hardware zugreifen und das DevTools-Protokoll verwenden möchten.

Chrome starten

chrome-remote-interface startet Chrome nicht automatisch. Sie müssen das selbst tun.

Im Abschnitt zur Befehlszeile haben wir Chrome manuell mit --headless --remote-debugging-port=9222 gestartet. Wenn Sie Tests jedoch vollständig automatisieren möchten, sollten Sie Chrome über Ihre Anwendung starten.

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

Es wird jedoch schwierig, wenn Sie eine tragbare Lösung benötigen, die auf mehreren Plattformen funktioniert. Sehen Sie sich nur diesen hartcodierten Pfad zu Chrome an :(

ChromeLauncher verwenden

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

Standardmäßig versucht chrome-launcher, Chrome Canary zu starten, sofern es installiert ist. Sie können dies jedoch ändern und manuell auswählen, welche Chrome-Version verwendet werden soll. Wenn Sie es verwenden möchten, müssen Sie es zuerst über npm installieren:

npm i --save chrome-launcher

Beispiel: Headless-Anwendung mit chrome-launcher starten

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

Das Ausführen dieses Scripts bewirkt nicht viel, aber im Task-Manager sollte eine Chrome-Instanz gestartet werden, in der about:blank geladen wurde. Denken Sie daran, dass es keine Browseroberfläche gibt. Wir sind headless.

Um den Browser zu steuern, benötigen wir das DevTools-Protokoll.

Informationen zur Seite abrufen

Installieren Sie 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']);
});

Die Ausgabe sieht in etwa so aus: HeadlessChrome/60.0.3082.0

Beispiel: Prüfen, 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: <title> der Seite mithilfe von DOM-APIs extrahieren

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

Derzeit öffnet Selenium eine vollständige Instanz von Chrome. Mit anderen Worten: Es handelt sich um eine automatisierte Lösung, die aber nicht vollständig ohne Benutzeroberfläche auskommt. Selenium kann jedoch mit etwas Aufwand so konfiguriert werden, dass es Headless Chrome ausführt. Ich empfehle den Artikel Selenium mit Headless Chrome ausführen, wenn Sie eine vollständige Anleitung zur Einrichtung benötigen. Unten finden Sie jedoch einige Beispiele, die Ihnen den Einstieg erleichtern.

ChromeDriver verwenden

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

Installation:

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.

Installation:

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 finden Sie einige hilfreiche Ressourcen für den Einstieg:

Docs

Tools

  • chrome-remote-interface: Node-Modul, das das DevTools-Protokoll umschließt
  • Lighthouse: Automatisiertes Tool zum Testen der Qualität von Webanwendungen, das das Protokoll intensiv nutzt
  • chrome-launcher: Node-Modul zum Starten von Chrome, für Automatisierung geeignet

Demos

  • The Headless Web: Der großartige Blogpost von Paul Kinlan zur Verwendung von Headless mit api.ai

FAQ

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

Nur unter Windows. Auf anderen Plattformen ist das nicht mehr erforderlich. Das Flag --disable-gpu ist eine vorübergehende Lösung für einige Fehler. In zukünftigen Versionen von Chrome ist dieses Flag nicht mehr erforderlich. Weitere Informationen finden Sie unter crbug.com/737678.

Benötige ich also weiterhin Xvfb?

Nein. Headless Chrome verwendet kein Fenster, sodass ein Displayserver wie Xvfb nicht mehr erforderlich ist. Sie können Ihre automatisierten Tests auch ohne diese Funktion ausführen.

Was ist Xvfb? Xvfb ist ein In-Memory-Displayserver 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 ältere Versionen von Chrome für „headless“-Tests auszuführen.

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

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

Kann ich das mit Selenium, WebDriver oder ChromeDriver verwenden?

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

Welcher Zusammenhang besteht zwischen diesem und PhantomJS?

Headless Chrome ähnelt Tools wie PhantomJS. Beide können für automatisierte Tests in einer headless-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?

Melden Sie Fehler in Headless Chrome unter crbug.com.

Melden Sie Fehler im DevTools-Protokoll unter github.com/ChromeDevTools/devtools-protocol.