Shell Headless Chrome

L'interface de ligne de commande Headless Chrome a été déployée dans Chrome 59. Elle permet d'exécuter le navigateur Chrome dans un environnement sans interface graphique. En d'autres termes, elle permet d'exécuter Chrome sans Chrome. Elle apporte toutes les fonctionnalités modernes de la plate-forme Web fournies par Chromium et le moteur de rendu Blink à la ligne de commande.

Un navigateur sans interface graphique est un excellent outil pour les tests automatisés et les environnements de serveur où vous n'avez pas besoin d'une interface utilisateur visible. Par exemple, vous pouvez exécuter des tests sur une page Web réelle, créer un PDF de cette page ou simplement inspecter la façon dont le navigateur affiche une URL.

Démarrer Headless (CLI)

Pour commencer à utiliser le mode headless, le plus simple est d'ouvrir le binaire Chrome à partir de la ligne de commande. Si vous avez installé Chrome 59 ou une version ultérieure, démarrez Chrome avec l'option --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 pointer vers votre installation de Chrome. L'emplacement exact varie d'une plate-forme à l'autre. Comme j'utilise un 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, utilisez 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.

Fonctionnalités de ligne de commande

Dans certains cas, vous n'aurez peut-être pas besoin de créer un script pour Headless Chrome. Il existe des options de ligne de commande utiles pour effectuer des tâches courantes.

Imprimer le DOM

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

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


### Create a PDF

The `--print-to-pdf` flag creates a PDF of the page:

```shell
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'option --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 le répertoire de travail actuel. Si vous recherchez des captures d'écran de pages entières, la procédure est un peu plus complexe. David Schnurr a écrit un excellent article de blog à ce sujet. Consultez Utiliser Headless Chrome comme outil de capture d'écran automatisé .

Mode REPL (boucle de lecture-évaluation-impression)

L'option --repl exécute Headless dans un mode où vous pouvez évaluer des 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 utilisateur de navigateur

Lorsque vous exécutez Chrome avec --remote-debugging-port=9222, une instance est lancée avec le protocole DevTools activé. Le protocole est utilisé pour communiquer avec Chrome et piloter l'instance de navigateur sans interface graphique. C'est également ce que des outils tels que Sublime, VS Code et Node utilisent pour déboguer une application à distance. #synergy

Comme vous n'avez pas d'interface utilisateur de navigateur pour afficher la page, accédez à http://localhost:9222 dans un autre navigateur pour vérifier que tout fonctionne. Une liste de pages inspectables s'affiche. Vous pouvez cliquer dessus pour voir ce que Headless affiche :

Outils pour les développeurs à distance
Interface utilisateur de débogage à distance DevTools

Vous pouvez ensuite utiliser les fonctionnalités DevTools habituelles pour inspecter, déboguer et ajuster la page comme vous le feriez normalement. Si vous utilisez Headless par programmation, cette page est également un outil de débogage puissant qui vous permet de voir toutes les commandes brutes du protocole DevTools qui transitent par le réseau et communiquent avec le navigateur.

Utiliser par programmation (Node)

Marionnettiste

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

Puppeteer peut, entre autres, être utilisé pour effectuer facilement des captures d'écran, créer des PDF, parcourir des pages et récupérer des informations sur ces pages. Je recommande cette bibliothèque si vous souhaitez automatiser rapidement les tests de navigateur. Elle masque la complexité du protocole DevTools et gère les tâches redondantes telles que le lancement d'une instance de débogage de Chrome.

Installez-la :

npm i --save puppeteer

Exemple : Imprimer l'user-agent

const puppeteer = require('puppeteer');

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

Exemple : Effectuer 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 à l'API de Puppeteer. Je la recommande si vous souhaitez être proche du métal et utiliser directement le protocole DevTools.

Lancer Chrome

chrome-remote-interface ne lance pas Chrome pour vous. Vous devrez donc vous en charger vous-même.

Dans la section CLI, nous avons démarré Chrome manuellement à l'aide de --headless --remote-debugging-port=9222. Toutefois, pour automatiser complètement les tests, vous devrez probablement générer Chrome à partir de 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 souhaitez une solution portable qui fonctionne sur plusieurs plates-formes. Regardez ce chemin codé en dur vers Chrome :(

Utiliser ChromeLauncher

Lighthouse est un formidable outil pour tester la qualité de vos applications Web. Un module robuste pour lancer Chrome a été développé dans Lighthouse et est désormais extrait pour une utilisation autonome. Le module NPM chrome-launcher trouve l'emplacement d'installation de Chrome, configure une instance de débogage, lance le navigateur et l'arrête lorsque votre programme est terminé. Le meilleur, c'est qu'il 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 ce paramètre pour sélectionner manuellement la version de Chrome à utiliser. Pour l'utiliser, installez-le d'abord à partir de npm :

npm i --save chrome-launcher

Exemple : Utiliser chrome-launcher pour lancer 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();
});

L'exécution de ce script n'a pas beaucoup d'effet, mais vous devriez voir une instance de Chrome s'ouvrir dans le gestionnaire des tâches qui a chargé about:blank. N'oubliez pas qu'il n'y aura pas d'interface utilisateur de navigateur. Nous sommes en mode headless.

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

Récupérer des informations sur la page

Pour installer la bibliothèque :

npm i --save chrome-remote-interface
Exemples

Exemple : Imprimer l'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']);
});

Résultat : HeadlessChrome/60.0.3082.0

Exemple : Vérifier si le site possède 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 : Extraire 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 d'une solution automatisée, mais pas complètement sans interface graphique. Toutefois, Selenium peut être configuré pour exécuter Headless Chrome avec un peu de travail. Je vous recommande d'exécuter Selenium avec Headless Chrome si vous souhaitez obtenir des instructions complètes sur la configuration, mais j'ai ajouté quelques exemples ci-dessous pour vous aider à démarrer.

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 au-dessus de 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();

})();

Télécharger l'ancienne version de Headless Chrome sous le nom chrome-headless-shell

À partir de la version 112 de Chrome, le nouveau mode headless de Chrome (--headless=new) est disponible. Ce mode permet aux développeurs d'exécuter Chrome lui-même plutôt qu'un binaire distinct dans un environnement sans surveillance et sans interface utilisateur visible. Il est utile pour les cas d'utilisation de test et d'automatisation.

Il existe des cas d'utilisation distincts pour l'ancienne interface de ligne de commande headless et le nouveau mode headless :

  1. L'ancienne interface de ligne de commande headless est un wrapper léger autour du module //content de Chromium. Elle comporte donc beaucoup moins de dépendances. Plus précisément, elle ne nécessite pas X11/Wayland, D-Bus et est, à certains égards, plus performante que le navigateur Chrome complet. Elle convient donc aux cas d'utilisation tels que la capture d'écran automatisée ou le Web scraping.
  2. Le nouveau mode headless, quant à lui, est le véritable navigateur Chrome. Il est donc plus authentique, plus fiable et offre plus de fonctionnalités. Il est donc plus adapté aux tests de bout en bout des applications Web de haute précision ou aux tests d'extensions de navigateur.

En d'autres termes, il existe un compromis entre performances et authenticité. Quel mode headless vous convient le mieux ? Cela dépend de votre cas d'utilisation.

Diagramme illustrant les informations fournies dans la liste précédente.

Les développeurs et les testeurs qui n'ont pas besoin de toutes les fonctionnalités de Chrome pour leurs cas d'utilisation d'automatisation peuvent utiliser l'ancienne version de Headless. Sinon, la nouvelle version de Headless est probablement le meilleur choix.

Pour que les développeurs et les testeurs puissent continuer à choisir entre ces deux options, nous sommes heureux d'annoncer que l'ancienne implémentation de Headless est désormais disponible en tant que binaire chrome-headless-shell autonome. Ces nouveaux chrome-headless-shell binaires sont générés pour chaque version de Chrome destinée aux utilisateurs et peuvent être téléchargés via l'infrastructure Chrome for Testing à partir de Chrome 120.

Comment obtenir les binaires chrome-headless-shell ?

Comme pour les autres binaires Chrome for Testing, le moyen le plus simple de télécharger chrome-headless-shell pour votre plate-forme consiste à utiliser notre utilitaire de ligne de commande, disponible à l'aide de npm.@puppeteer/browsers Voici quelques exemples :

# Download the latest available `chrome-headless-shell` binary corresponding to the Stable channel.
npx @puppeteer/browsers install chrome-headless-shell@stable

# Download a specific `chrome-headless-shell` version.
npx @puppeteer/browsers install chrome-headless-shell@120.0.6098.0

Si vous préférez créer vos propres scripts automatisés pour télécharger les binaires chrome-headless-shell, nous avons ce qu'il vous faut. Chrome for Testing propose des points de terminaison d'API JSON avec les dernières versions disponibles par canal de publication Chrome (stable, bêta, développement et Canary). Pour obtenir un aperçu rapide du dernier état, consultez le tableau de bord de disponibilité de Chrome for Testing.

Commentaires

Nous avons hâte de connaître votre avis sur chrome-headless-shell. Si vous rencontrez des problèmes, n'hésitez pas à les signaler.

Autres ressources

Voici quelques ressources utiles pour vous aider à démarrer :

Docs

Outils

  • chrome-remote-interface : module Node qui encapsule le protocole DevTools
  • Lighthouse : outil automatisé pour tester la qualité des applications Web, qui utilise beaucoup le protocole
  • chrome-launcher : module Node pour lancer Chrome, prêt pour l'automatisation

Démonstrations

  • "The Headless Web" : excellent article de blog de Paul Kinlan sur l'utilisation de Headless avec api.ai.

Questions fréquentes

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

Uniquement sous Windows. Les autres plates-formes ne l'exigent plus. L'option --disable-gpu est une solution de contournement temporaire pour quelques bugs. Vous n'aurez pas besoin de cette option dans les futures versions de Chrome. Pour en savoir plus, consultez crbug.com/737678.

Ai-je toujours besoin de Xvfb ?

Non. Headless Chrome n'utilise pas de fenêtre. Un serveur d'affichage tel que Xvfb n'est donc plus nécessaire. Vous pouvez 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 d'exécuter des applications graphiques (comme Chrome) sans écran physique connecté. De nombreuses personnes utilisent Xvfb pour exécuter des versions antérieures de Chrome afin d'effectuer des tests "headless".

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

Consultez lighthouse-ci. Il contient un exemple de Dockerfile qui utilise node:8-slim comme image de base, installe et exécute Lighthouse sur App Engine Flex.

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

Oui. Consultez Utiliser Selenium, WebDriver et ChromeDriver.

Quel est le rapport avec PhantomJS ?

Headless Chrome est semblable à des outils tels que PhantomJS. Les deux peuvent être utilisés 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 comme moteur de rendu, tandis que Headless Chrome utilise la dernière version de Blink.

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

Où puis-je signaler des bugs ?

Pour les bugs concernant Headless Chrome, signalez-les sur crbug.com.

Pour les bugs concernant le protocole DevTools, signalez-les sur github.com/ChromeDevTools/devtools-protocol.