Premiers pas avec Headless Chrome

TL;DR

Headless Chrome est disponible dans Chrome 59. Il permet d'exécuter le navigateur Chrome dans un environnement headless. En gros, l'exécution Chrome sans Chrome ! Il intègre toutes les fonctionnalités modernes des plates-formes Web fournies par Chromium et le moteur de rendu Blink à la ligne de commande.

En quoi est-ce utile ?

Un navigateur headless est un excellent outil pour les tests automatisés et les environnements de serveur dans lesquels vous n'ont pas besoin d'un shell d'interface utilisateur visible. Par exemple, vous pouvez exécuter des tests sur une vraie page Web, en créer un PDF ou simplement inspecter la façon dont le navigateur affiche une URL.

Démarrage sans interface graphique (CLI)

Pour commencer à utiliser le mode headless, le plus simple est d'ouvrir le fichier binaire Chrome. depuis la ligne de commande. Si vous avez installé Chrome 59 ou une version ultérieure, démarrez Chrome avec l'indicateur --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 doit renvoyer vers votre installation de Chrome. L'emplacement exact sera varient d'une plateforme à l'autre. Comme je suis sous Mac, j'ai créé des alias pratiques pour chaque version de Chrome que j'ai installée.

Si vous utilisez la version stable de Chrome et que vous ne pouvez pas obtenir la version bêta, je vous recommande à l'aide de 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"

Téléchargez Chrome Canary ici.

Fonctionnalités de la ligne de commande

Dans certains cas, vous n'aurez peut-être pas besoin d'écrire un script pour Chrome sans interface graphique. Il existe des indicateurs de ligne de commande utiles. pour effectuer des tâches courantes.

Imprimer le DOM

L'indicateur --dump-dom imprime document.body.innerHTML sur stdout:

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

Créer un PDF

L'indicateur --print-to-pdf crée un PDF de la page:

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

Effectuer des captures d'écran

Pour effectuer une capture d'écran d'une page, utilisez l'indicateur --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/

L'exécution avec --screenshot génère un fichier nommé screenshot.png dans répertoire de travail actuel. Si vous recherchez des captures d'écran en pleine page, sont un peu plus impliquées. Il y a un super blog de David Schnurr pour vous aider. Départ Utiliser Headless Chrome comme outil automatisé de capture d'écran :

Mode REPL (boucle lecture-évaluation-impression)

L'option --repl s'exécute sans interface graphique dans un mode permettant d'évaluer les expressions JS. dans le navigateur, directement à partir de la ligne de commande:

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

Déboguer Chrome sans interface de navigateur ?

Lorsque vous exécutez Chrome avec --remote-debugging-port=9222, une instance démarre avec le protocole DevTools activé. La est utilisé pour communiquer avec Chrome Compute Engine. C'est aussi ce que des outils comme Sublime, VS Code et Node utilisent pour pour déboguer une application à distance. #synergy

Comme vous n'avez pas d'interface utilisateur de navigateur pour voir la page, accédez à http://localhost:9222 dans un autre navigateur pour vérifier que tout fonctionne correctement. Une liste de des pages Inspectable sur lesquelles vous pouvez cliquer pour voir le rendu Headless:

<ph type="x-smartling-placeholder">
</ph> Outils de développement distants <ph type="x-smartling-placeholder">
</ph> UI de débogage à distance des outils de développement
.

Vous pouvez alors utiliser les fonctionnalités familières des outils de développement pour inspecter, déboguer et ajuster la page selon la procédure habituelle. Si vous utilisez la programmation sans interface graphique, cette est également un puissant outil de débogage qui vous permet de voir tous les protocoles bruts des outils de développement des commandes à travers le fil, en communiquant avec le navigateur.

Utiliser de façon automatisée (nœud)

Marionnettiste

Puppeteer est une bibliothèque Node. développés par l'équipe Chrome. Il fournit une API de haut niveau pour contrôler le mode sans interface graphique Chrome (ou complet). Elle est semblable à d'autres bibliothèques de test automatisées comme Phantom. et NightmareJS, mais ne fonctionne qu'avec les dernières versions de Chrome.

Puppeteer permet, entre autres, de faire des captures d'écran, de créer des PDF, naviguer dans les pages et récupérer des informations sur ces pages. Je recommande la bibliothèque pour automatiser rapidement les tests des navigateurs. Elle masque les complexités du protocole DevTools et gère les tâches redondantes, comme le lancement l'instance de débogage de Chrome.

Installez-la:

npm i --save puppeteer

Exemple : imprimer le user-agent

const puppeteer = require('puppeteer');

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

Exemple : Faire une capture d'écran de la page

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

Consultez la documentation de Puppeteer. pour en savoir plus sur l'API complète.

La bibliothèque CRI

chrome-remote-interface est une bibliothèque de niveau inférieur à celui de l'API de Puppeteer. Je le recommande si vous voulez être à proximité du métal et utilisez directement le protocole DevTools.

Lancer Chrome

chrome-remote-interface ne lance pas Chrome. vous en occuper vous-même.

Dans la section CLI, nous avons démarré Chrome manuellement en utilisant --headless --remote-debugging-port=9222 Toutefois, pour automatiser entièrement les tests, que vous voulez générer Chrome depuis votre application.

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

Mais les choses se compliquent si vous voulez une solution portable compatible avec plusieurs plates-formes. Regardez simplement le chemin d'accès codé en dur vers Chrome :(

Utilisation de ChromeLauncher

Lighthouse est une merveille pour tester la qualité de vos applications Web. Un module efficace pour lancer Chrome a été développé dans Lighthouse et est désormais extrait pour une utilisation autonome. Module GPR de chrome-launcher trouvera Chrome est installé. Configurez une instance de débogage, lancez le navigateur et fermez-le. lorsque votre programme est terminé. Cerise sur le gâteau : elle fonctionne sur plusieurs plates-formes grâce à Node!

Par défaut, chrome-launcher tente de lancer Chrome Canary (s'il est installé), mais vous pouvez modifier cela pour sélectionner manuellement le navigateur Chrome à utiliser. À l'utiliser, installez d'abord à partir de npm:

npm i --save chrome-launcher

Exemple : Utilisation de chrome-launcher pour lancer sans interface graphique

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

L'exécution de ce script ne fait pas grand-chose, mais vous devriez voir une instance de Chrome est lancé dans le gestionnaire de tâches qui a chargé about:blank. Rappelez-vous, il y a il n'y aura aucune UI de navigateur. Nous n'avons pas de tête.

Pour contrôler le navigateur, nous avons besoin du protocole DevTools.

Récupérer des informations sur la page

Installons la bibliothèque:

npm i --save chrome-remote-interface
Exemples

Exemple : imprimer le user-agent

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

...

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

Vous obtenez un résultat semblable au suivant: HeadlessChrome/60.0.3082.0

Exemple : Vérifier si le site dispose d'un fichier manifeste d'application Web

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

})();

Exemple : Extrayez le <title> de la page à l'aide des API DOM.

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

})();

Utiliser Selenium, WebDriver et ChromeDriver

Pour le moment, Selenium ouvre une instance complète de Chrome. En d'autres termes, il s'agit automatisée, mais pas complètement headless. Cependant, le sélénium peut être configuré pour exécuter Chrome headless avec un peu de travail. Je recommande Course de Selenium avec Headless Chrome si vous souhaitez que des instructions complètes sur la façon de configurer les choses par vous-même, mais j'ai ici quelques exemples ci-dessous pour vous lancer.

Utiliser ChromeDriver

ChromeDriver 2.32 utilise Chrome 61 et fonctionne bien avec Headless Chrome.

Installer :

npm i --save-dev selenium-webdriver chromedriver

Exemple :

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

Utiliser WebDriverIO

WebDriverIO est une API de niveau supérieur qui vient compléter Selenium WebDriver.

Installer :

npm i --save-dev webdriverio chromedriver

Exemple: Filtrer les fonctionnalités CSS sur 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();

})();

Autres ressources

Voici quelques ressources utiles pour vous lancer:

Docs

Outils

  • chrome-remote-interface : nœud qui encapsule le protocole des outils de développement.
  • Lighthouse : outil de test automatisé la qualité des applications Web ; fait une utilisation intensive du protocole
  • chrome-launcher : module de nœud pour lancer Chrome, prêt pour l'automatisation

Démonstrations

  • "The Headless Web" - L'excellent blog de Paul Kinlan comment utiliser Headless avec api.ai.

Questions fréquentes

Ai-je besoin de l'option --disable-gpu ?

Uniquement sous Windows. Les autres plates-formes n'en ont plus besoin. L'option --disable-gpu est une une solution temporaire pour quelques bugs. Vous n'aurez plus besoin de cet indicateur dans les futures versions de Chrome. Consultez la page crbug.com/737678. pour en savoir plus.

J'ai toujours besoin de Xvfb ?

Non. Headless Chrome n'utilise pas de fenêtre. Un serveur d'affichage comme Xvfb ne sont plus nécessaires. Sans cela, vous pourrez exécuter vos tests automatisés sans problème.

Qu'est-ce que Xvfb ? Xvfb est un serveur d’affichage en mémoire pour les systèmes de type Unix qui vous permet pour exécuter des applications graphiques (comme Chrome) sans écran physique connecté. De nombreux utilisateurs utilisent Xvfb pour exécuter des versions antérieures de Chrome afin d'effectuer des opérations "headless". tests.

Comment créer un conteneur Docker qui exécute Headless Chrome ?

Regardez lighthouse-ci. Il comporte une Exemple de Dockerfile qui utilise node:8-slim comme image de base, installe + utilise Lighthouse sur l'environnement flexible App Engine.

Puis-je l'utiliser avec Selenium / WebDriver / ChromeDriver ?

Oui. Consultez la page Utiliser Selenium, WebDriver et ChromeDriver.

Quel est le lien avec PhantomJS ?

Headless Chrome est semblable à des outils tels que PhantomJS. Les deux pour des tests automatisés dans un environnement sans interface graphique. La principale différence entre les deux est que Phantom utilise une ancienne version de WebKit pour le rendu tandis que Headless Chrome utilise la dernière version de Blink.

Pour le moment, Phantom fournit une API de niveau supérieur à celle du protocole DevTools.

Où puis-je signaler des bugs ?

Si vous rencontrez un bug lié à Chrome sans interface graphique, signalez-le sur crbug.com.

Pour les bugs du protocole des outils de développement, signalez-les à l'adresse github.com/ChromeDevTools/devtools-protocol.