Shell di Chrome headless

La shell di Chrome headless è stata rilasciata in Chrome 59. È un modo per eseguire il browser Chrome in un ambiente headless. In sostanza, esegui Chrome senza Chrome. Offre tutte le funzionalità moderne della piattaforma web fornite da Chromium e dal motore di rendering Blink alla riga di comando.

Un browser headless è uno strumento ideale per i test automatici e gli ambienti server in cui non è necessaria una shell UI visibile. Ad esempio, potresti voler eseguire alcuni test su una pagina web reale, creare un PDF o semplicemente controllare il rendering di un URL da parte del browser.

Avvio di Headless (interfaccia a riga di comando)

Il modo più semplice per iniziare a utilizzare la modalità headless è aprire il file binario di Chrome dalla riga di comando. Se hai installato Chrome 59 o versioni successive, avvia Chrome con il 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 deve rimandare all'installazione di Chrome. La posizione esatta varia da piattaforma a piattaforma. Poiché utilizzo un Mac, ho creato alias pratici per ogni versione di Chrome che ho installato.

Se utilizzi il canale stabile di Chrome e non riesci a ottenere la versione beta, utilizza 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"

Scarica Chrome Canary.

Funzionalità della riga di comando

In alcuni casi, potrebbe non essere necessario creare script di Chrome headless in modo programmatico. Esistono alcuni flag della riga di comando utili per eseguire attività comuni.

Stampa del DOM

Il flag --dump-dom stampa document.body.innerHTML su 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/

Acquisizione di screenshot

Per acquisire uno screenshot di una pagina, utilizza il flag --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'esecuzione con --screenshot genererà un file denominato screenshot.png nella directory di lavoro corrente. Se stai cercando screenshot a pagina intera, la procedura è un po' più complessa. Esiste un ottimo post del blog di David Schnurr che ti spiega come fare. Consulta Utilizzo di Chrome headless come strumento di screenshot automatico .

Modalità REPL (ciclo di lettura-valutazione-stampa)

Il flag --repl esegue Headless in una modalità in cui puoi valutare le espressioni JS nel browser direttamente dalla riga di comando:

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

Eseguire il debug di Chrome senza un'interfaccia utente del browser

Quando esegui Chrome con --remote-debugging-port=9222, viene avviata un'istanza con il protocollo DevTools abilitato. Il protocollo viene utilizzato per comunicare con Chrome e gestire l'istanza del browser headless. È anche ciò che strumenti come Sublime, VS Code e Node utilizzano per il debug remoto di un'applicazione. #synergy

Poiché non hai un'interfaccia utente del browser per visualizzare la pagina, vai a http://localhost:9222 in un altro browser per verificare che tutto funzioni. Verrà visualizzato un elenco di pagine ispezionabili su cui puoi fare clic per vedere il rendering di Headless:

DevTools Remote
Interfaccia utente di debug remoto di DevTools

Da qui, puoi utilizzare le funzionalità di DevTools che conosci per ispezionare, eseguire il debug e modificare la pagina come faresti normalmente. Se utilizzi Headless in modo programmatico, questa pagina è anche un potente strumento di debug per visualizzare tutti i comandi del protocollo DevTools non elaborati che vengono inviati tramite la rete e comunicano con il browser.

Utilizzo in modo programmatico (Node)

Burattinaio

Puppeteer è una libreria Node sviluppata dal team di Chrome. Fornisce un'API di alto livello per controllare Chrome headless (o completo). È simile ad altre librerie di test automatici come Phantom e NightmareJS, ma funziona solo con le versioni più recenti di Chrome.

Tra le altre cose, Puppeteer può essere utilizzato per acquisire facilmente screenshot, creare PDF, navigare tra le pagine e recuperare informazioni su queste pagine. Ti consiglio di utilizzare questa libreria se vuoi automatizzare rapidamente i test del browser. Nasconde le complessità del protocollo DevTools e si occupa di attività ridondanti come l'avvio di un'istanza di debug di Chrome.

Installala:

npm i --save puppeteer

Esempio : stampa lo user agent

const puppeteer = require('puppeteer');

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

Esempio : acquisizione di uno screenshot della pagina

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

Consulta la documentazione di Puppeteer per saperne di più sull'API completa.

La libreria CRI

chrome-remote-interface è una libreria di livello inferiore rispetto all'API di Puppeteer. Ti consiglio di utilizzarla se vuoi essere vicino al metal e utilizzare direttamente il protocollo DevTools.

Avvio di Chrome

chrome-remote-interface non avvia Chrome, quindi dovrai occupartene tu.

Nella sezione CLI, abbiamo avviato Chrome manualmente utilizzando --headless --remote-debugging-port=9222. Tuttavia, per automatizzare completamente i test, probabilmente dovrai generare Chrome dalla tua applicazione.

Un modo è utilizzare 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) => {
  ...
});

Tuttavia, le cose si complicano se vuoi una soluzione portatile che funzioni su più piattaforme. Basta guardare il percorso hardcoded a Chrome :(

Utilizzo di ChromeLauncher

Lighthouse è uno strumento straordinario per testare la qualità delle tue app web. È stato sviluppato un modulo robusto per l'avvio di Chrome all'interno di Lighthouse, che ora è stato estratto per l'utilizzo autonomo. Il modulo NPM chrome-launcher troverà la posizione di installazione di Chrome, configurerà un'istanza di debug, avvierà il browser e lo chiuderà al termine del programma. La parte migliore è che funziona multipiattaforma grazie a Node.

Per impostazione predefinita, chrome-launcher tenterà di avviare Chrome Canary (se è installato), ma puoi modificare questa impostazione per selezionare manualmente la versione di Chrome da utilizzare. Per utilizzarlo, installalo prima da npm:

npm i --save chrome-launcher

Esempio : utilizzo di chrome-launcher per avviare 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'esecuzione di questo script non fa molto, ma dovresti vedere un'istanza di Chrome avviata in Task Manager che ha caricato about:blank. Ricorda che non ci sarà alcuna UI del browser. Siamo in modalità headless.

Per controllare il browser, abbiamo bisogno del protocollo DevTools.

Recupero di informazioni sulla pagina

Per installare la libreria:

npm i --save chrome-remote-interface
Esempi

Esempio : stampa lo 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']);
});

Il risultato è simile a: HeadlessChrome/60.0.3082.0

Esempio : verifica se il sito ha un manifest dell'app 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.
});

})();

Esempio : estrai il <title> della pagina utilizzando le 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.
});

})();

Utilizzo di Selenium, WebDriver e ChromeDriver

Al momento, Selenium apre un'istanza completa di Chrome. In altre parole, è una soluzione automatizzata, ma non completamente headless. Tuttavia, Selenium può essere configurato per eseguire Chrome headless con un po' di lavoro. Ti consiglio di consultare Esecuzione di Selenium con Chrome headless per istruzioni complete su come configurare tutto, ma di seguito ho inserito alcuni esempi per iniziare.

Utilizzo di ChromeDriver

ChromeDriver 2.32 utilizza Chrome 61 e funziona bene con Chrome headless.

Installa:

npm i --save-dev selenium-webdriver chromedriver

Esempio:

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

Utilizzo di WebDriverIO

WebDriverIO è un'API di livello superiore basata su Selenium WebDriver.

Installa:

npm i --save-dev webdriverio chromedriver

Esempio: filtra le funzionalità CSS su 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();

})();

Scarica la versione precedente di Chrome headless come chrome-headless-shell

A partire dalla versione 112 di Chrome, è disponibile la nuova modalità headless di Chrome (--headless=new). Questa modalità consente agli sviluppatori di eseguire Chrome stesso anziché un file binario separato in un ambiente automatico senza alcuna UI visibile, utile per i casi d'uso di test e automazione.

Esistono casi d'uso distinti per la shell headless precedente e la nuova modalità headless:

  1. La shell headless precedente è un wrapper leggero intorno al modulo //content di Chromium e, pertanto, ha molte meno dipendenze. In particolare, non richiede X11/Wayland, D-Bus e, in alcuni casi, è più efficiente del browser Chrome completo. Ciò lo rende adatto a casi d'uso come l'acquisizione automatica di screenshot o lo scraping web.
  2. La nuova modalità headless, d'altra parte, è il vero browser Chrome e, pertanto, è più autentica, affidabile e offre più funzionalità. Ciò lo rende più adatto per i test end-to-end di app web ad alta precisione o per i test delle estensioni del browser.

In altre parole, esiste un compromesso tra prestazioni e autenticità. Qual è la modalità headless più adatta a te? Dipende dal tuo caso d'uso.

Un diagramma che illustra le informazioni fornite nell'elenco precedente.

Gli sviluppatori e i tester che non richiedono la funzionalità completa di Chrome per i loro casi d'uso di automazione potrebbero voler utilizzare la versione precedente di Headless. In caso contrario, la nuova versione di Headless è probabilmente la scelta migliore.

Per garantire che gli sviluppatori e i tester continuino ad avere la possibilità di scegliere tra queste due opzioni, siamo lieti di annunciare che la vecchia implementazione di Headless è ora disponibile come file binario autonomo chrome-headless-shell. Questi nuovi file binari chrome-headless-shell vengono generati per ogni release di Chrome rivolta agli utenti e sono disponibili per il download tramite l'infrastruttura di Chrome for Testing a partire da Chrome 120.

Come posso ottenere i file binari chrome-headless-shell?

Come per gli altri file binari di Chrome for Testing, il modo più semplice per scaricare chrome-headless-shell per la tua piattaforma è utilizzare la nostra @puppeteer/browsers utilità della riga di comando, disponibile tramite npm. Ecco alcuni esempi:

# 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

Se preferisci creare i tuoi script automatici per scaricare i file binari chrome-headless-shell, non preoccuparti. Chrome for Testing offre endpoint API JSON con le versioni più recenti disponibili per canale di rilascio di Chrome (stabile, beta, Dev e Canary). Per una rapida panoramica dello stato più recente, consulta la dashboard di disponibilità di Chrome for Testing.

Feedback

Non vediamo l'ora di ricevere il tuo feedback su chrome-headless-shell. Se riscontri problemi, non esitare a segnalarli.

Ulteriori risorse

Ecco alcune risorse utili per iniziare:

Documenti

Strumenti

  • chrome-remote-interface: modulo Node che esegue il wrapping del protocollo DevTools
  • Lighthouse: strumento automatico per testare la qualità delle app web; utilizza molto il protocollo
  • chrome-launcher: modulo Node per avviare Chrome, pronto per l'automazione

Demo

  • "The Headless Web": l'ottimo post del blog di Paul Kinlan sull'utilizzo di Headless con api.ai.

Domande frequenti

Ho bisogno del flag --disable-gpu?

Solo su Windows. Le altre piattaforme non lo richiedono più. Il flag --disable-gpu è una soluzione temporanea per alcuni bug. Non avrai bisogno di questo flag nelle versioni future di Chrome. Per saperne di più, consulta crbug.com/737678.

Quindi ho ancora bisogno di Xvfb?

No. Chrome headless non utilizza una finestra, quindi non è più necessario un server di visualizzazione come Xvfb. Puoi eseguire i test automatici senza problemi.

Che cos'è Xvfb? Xvfb è un server di visualizzazione in memoria per sistemi di tipo Unix che consente di eseguire applicazioni grafiche (come Chrome) senza un display fisico collegato. Molte persone utilizzano Xvfb per eseguire versioni precedenti di Chrome per eseguire test "headless".

Come faccio a creare un container Docker che esegue Chrome headless?

Dai un'occhiata a lighthouse-ci. Contiene un Dockerfile di esempio che utilizza node:8-slim come immagine di base, installa ed esegue Lighthouse su App Engine Flex.

Posso utilizzare questo strumento con Selenium / WebDriver / ChromeDriver?

Sì. Consulta Utilizzo di Selenium, WebDriver e ChromeDriver.

Che relazione esiste con PhantomJS?

Chrome headless è simile a strumenti come PhantomJS. Entrambi possono essere utilizzati per i test automatici in un ambiente headless. La differenza principale tra i due è che Phantom utilizza una versione precedente di WebKit come motore di rendering, mentre Chrome headless utilizza l'ultima versione di Blink.

Al momento, Phantom fornisce anche un'API di livello superiore rispetto al protocollo DevTools.

Dove posso segnalare i bug?

Per i bug relativi a Chrome headless, segnalali su crbug.com.

Per i bug nel protocollo DevTools, segnalali su github.com/ChromeDevTools/devtools-protocol.