TL;DR
Chrome headless est disponible dans Chrome 59. Il s'agit d'un moyen d'exécuter le navigateur Chrome dans un environnement headless. En gros, exécuter Chrome sans Chrome ! Il apporte à la ligne de commande toutes les fonctionnalités modernes de la plate-forme Web fournies par Chromium et le moteur de rendu Blink.
Pourquoi est-ce utile ?
Un navigateur headless est un excellent outil pour les tests automatisés et les environnements serveur où vous n'avez pas besoin d'une coque d'UI visible. Par exemple, vous pouvez exécuter des tests sur une page Web réelle, en créer un PDF ou simplement inspecter la façon dont le navigateur affiche une URL.
Démarrer sans interface graphique (CLI)
Le moyen le plus simple de commencer à utiliser le mode headless est d'ouvrir le binaire Chrome à partir de la ligne de commande. Si vous avez installé Chrome 59 ou 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 pointer vers votre installation de Chrome. L'emplacement exact varie d'une plate-forme à l'autre. Étant donné que je suis sur 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 d'utiliser 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'avez pas besoin de écrire un script de manière programmatique pour Chrome headless. Il existe des options de ligne de commande utiles pour effectuer des tâches courantes.
Impression du 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'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 en pleine page, 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'indicateur --repl
exécute Headless dans un mode qui vous permet d'évaluer les expressions JS dans le navigateur, directement depuis 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 permet de communiquer avec Chrome et de piloter l'instance de navigateur sans interface utilisateur. C'est également ce que des outils tels que Sublime, VS Code et Node utilisent pour déboguer une application à distance. #synergy
Étant donné que 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:
Vous pouvez ensuite utiliser les fonctionnalités familières de DevTools pour inspecter, déboguer et modifier la page comme vous le feriez normalement. Si vous utilisez Headless de manière programmatique, cette page est également un outil de débogage puissant qui vous permet de voir toutes les commandes de protocole DevTools brutes transmises via le fil, qui communiquent avec le navigateur.
Utilisation par programmation (Node)
Marionnettiste
Puppeteer est une bibliothèque Node développée par l'équipe Chrome. Il fournit une API de haut niveau pour contrôler Chrome headless (ou complet). Il est semblable à d'autres bibliothèques de test automatisé telles que Phantom et NightmareJS, mais ne fonctionne qu'avec les dernières versions de Chrome.
Puppeteer peut être utilisé, entre autres, pour prendre facilement des captures d'écran, créer des PDF, parcourir des pages et extraire des informations sur ces pages. Je vous recommande cette bibliothèque si vous souhaitez automatiser rapidement les tests de navigateur. Il masque la complexité du protocole DevTools et gère les tâches redondantes, comme le lancement d'une instance de débogage de Chrome.
Installez-le:
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 : prendre 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.
Bibliothèque CRI
chrome-remote-interface est une bibliothèque de niveau inférieur à l'API Puppeteer. Je vous le recommande si vous souhaitez être au plus près du métal et utiliser directement le protocole DevTools.
Lancer Chrome
chrome-remote-interface ne lance pas Chrome à votre place. Vous devrez donc le faire 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 vers Chrome codé en dur :(
À l'aide de ChromeLauncher
Lighthouse est un outil formidable pour tester la qualité de vos applications Web. Un module robuste pour lancer Chrome a été développé dans Lighthouse et est maintenant extrait pour un usage autonome.
Le module NPM chrome-launcher
trouvera l'emplacement où Chrome est installé, configurera une instance de débogage, lancera le navigateur et l'arrêtera lorsque votre programme sera 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 le 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 ne sert pas à grand-chose, mais vous devriez voir une instance de Chrome démarrer dans le gestionnaire de tâches qui a chargé about:blank
. N'oubliez pas qu'il n'y aura pas d'interface utilisateur du navigateur. Nous sommes headless.
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 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 comporte 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 d'une solution automatisée, mais pas complètement headless. Toutefois, Selenium peut être configuré pour exécuter Chrome headless avec un peu de travail. Je vous recommande de consulter Exécuter Selenium avec Chrome headless si vous souhaitez obtenir des instructions complètes sur la configuration vous-même. Toutefois, j'ai ajouté quelques exemples ci-dessous pour vous aider à vous lancer.
À l'aide de ChromeDriver
ChromeDriver 2.32 utilise Chrome 61 et fonctionne bien avec Chrome sans interface utilisateur.
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 sur 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 aider à vous lancer:
Docs
- Lecteur de protocole DevTools : documentation de référence de l'API
Outils
- chrome-remote-interface : module Node qui encapsule le protocole DevTools
- Lighthouse : outil automatisé permettant de tester la qualité des applications Web. Utilise intensivement le protocole.
- chrome-launcher : module de nœud permettant de lancer Chrome, prêt à être automatisé
Démonstrations
- "The Headless Web" (Le Web sans interface graphique) : excellent article de blog de Paul Kinlan sur l'utilisation de Headless avec api.ai.
Questions fréquentes
Ai-je besoin de l'indicateur --disable-gpu
?
Uniquement sous Windows. Les autres plates-formes n'en ont plus besoin. L'option --disable-gpu
est un contournement temporaire de quelques bugs. Vous n'aurez plus besoin de cet indicateur dans les prochaines versions de Chrome. Pour en savoir plus, consultez crbug.com/737678.
J'ai donc toujours besoin de Xvfb ?
Non. Chrome sans tête 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 cela.
Qu'est-ce que Xvfb ? Xvfb est un serveur d'affichage en mémoire pour les systèmes 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 de réaliser des tests "sans tête".
Comment créer un conteneur Docker qui exécute Chrome sans interface ?
Consultez lighthouse-ci. Il contient un exemple de fichier Dockerfile qui utilise node:8-slim
comme image de base, installe et exécute Lighthouse sur App Engine Flex.
Puis-je utiliser cette fonctionnalité avec Selenium / WebDriver / ChromeDriver ?
Oui. Consultez Utiliser Selenium, WebDriver et ChromeDriver.
En quoi cela est-il lié à PhantomJS ?
Chrome headless est semblable à des outils tels que PhantomJS. Les deux peuvent être utilisés pour les tests automatisés dans un environnement headless. 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 que le protocole DevTools.
Où puis-je signaler des bugs ?
Pour les bugs liés à Chrome headless, envoyez-les sur crbug.com.
Pour signaler des bugs dans le protocole DevTools, accédez à github.com/ChromeDevTools/devtools-protocol.