Cómo comenzar a usar Chrome sin interfaz gráfica

Resumen

Chrome sin interfaz gráfica se lanzará en Chrome 59. Es una manera de ejecutar el navegador Chrome en un entorno sin interfaz gráfica. Básicamente, ¡ejecuta Chrome sin Chrome! Incorpora todas las funciones modernas de plataformas web que proporcionan Chromium y el motor de renderización de Blink a la línea de comandos.

¿Por qué es útil?

Un navegador sin interfaz gráfica es una gran herramienta para pruebas automatizadas y entornos de servidor en los que no necesitas una shell de IU visible. Por ejemplo, es posible que desees ejecutar algunas pruebas en una página web real, crear un archivo PDF de esta o solo inspeccionar cómo el navegador renderiza una URL.

Inicia Headless (CLI)

La manera más fácil de comenzar a usar el modo sin interfaz gráfica es abrir el objeto binario de Chrome desde la línea de comandos. Si tienes Chrome 59 o una versión posterior instalado, inicia Chrome con la función experimental --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 debe dirigir a tu instalación de Chrome. La ubicación exacta variará de una plataforma a otra. Como estoy en Mac, creé alias convenientes para cada versión de Chrome que he instalado.

Si estás en el canal estable de Chrome y no puedes obtener la versión beta, te recomendamos usar 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"

Descarga Chrome Canary aquí.

Características de la línea de comandos

En algunos casos, es posible que no necesites iniciar una secuencia de comandos programática en Headless Chrome. Hay algunas marcas de línea de comandos útiles para realizar tareas comunes.

Cómo imprimir el DOM

La marca --dump-dom imprime document.body.innerHTML en stdout:

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

Crear un PDF

La marca --print-to-pdf crea un PDF de la página:

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

Toma capturas de pantalla

Para tomar una captura de pantalla de una página, usa la marca --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/

La ejecución con --screenshot producirá un archivo llamado screenshot.png en el directorio de trabajo actual. Si buscas capturas de pantalla de página completa, este proceso es un poco más complejo. Hay una excelente entrada de blog de David Schnurr que te ayudará. Consulta Cómo usar Chrome sin interfaz gráfica como herramienta automatizada de captura de pantalla .

Modo REPL (bucle de lectura-evaluación-impresión)

La marca --repl se ejecuta sin interfaz gráfica en un modo en el que puedes evaluar expresiones de JS en el navegador, directamente desde la línea de comandos:

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

¿Quieres depurar Chrome sin una IU del navegador?

Cuando ejecutas Chrome con --remote-debugging-port=9222, se inicia una instancia con el protocolo DevOps habilitado. El protocolo se usa para comunicarse con Chrome y controlar la instancia del navegador sin interfaz gráfica. También es lo que usan herramientas como Sublime, VS Code y Node para depurar una aplicación de manera remota. #synergy

Dado que no tienes una IU del navegador para ver la página, navega a http://localhost:9222 en otro navegador y comprueba que todo funcione correctamente. Verás una lista de páginas que se pueden inspeccionar en las que puedes hacer clic para ver qué se renderiza sin interfaz gráfica:

Herramientas remotas para desarrolladores
IU de depuración remota de Herramientas para desarrolladores

Desde aquí, puedes usar las funciones conocidas de Herramientas para desarrolladores para inspeccionar, depurar y modificar la página como lo harías normalmente. Si usas Headless de manera programática, esta página también es una herramienta de depuración potente para ver todos los comandos del protocolo de Herramientas para desarrolladores sin procesar que se comunican con el navegador.

Uso de manera programática (Nodo)

Titiritero

Puppeteer es una biblioteca de Node que desarrolló el equipo de Chrome. Proporciona una API de alto nivel para controlar Chrome sin interfaz gráfica (o completo). Es similar a otras bibliotecas de pruebas automatizadas, como Phantom y NightmareJS, pero solo funciona con las versiones más recientes de Chrome.

Entre otras funciones, Puppeteer se puede usar para tomar capturas de pantalla, crear PDF, navegar y recuperar información sobre ellas con facilidad. Te recomiendo la biblioteca si quieres automatizar con rapidez las pruebas del navegador. Oculta las complejidades del protocolo de Herramientas para desarrolladores y se encarga de las tareas redundantes, como iniciar una instancia de depuración de Chrome.

Instálalo:

npm i --save puppeteer

Ejemplo: imprime el usuario-agente

const puppeteer = require('puppeteer');

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

Ejemplo: Toma una captura de pantalla de la página

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 documentación de Puppeteer para obtener más información sobre la API completa.

La biblioteca de CRI

chrome-remote-interface es una biblioteca de nivel inferior que la API de Puppeteer. Te recomendamos esta opción si quieres estar cerca de ti y usar el protocolo de Herramientas para desarrolladores directamente.

Iniciando Chrome

chrome-remote-interface no inicia Chrome por ti, así que tendrás que encargarte de eso.

En la sección CLI, iniciamos Chrome de forma manual con --headless --remote-debugging-port=9222. Sin embargo, para automatizar las pruebas por completo, es probable que quieras generar Chrome desde tu aplicación.

Una forma es usar 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) => {
  ...
});

Sin embargo, las cosas se complican si quieres una solución portátil que funcione en varias plataformas. Solo mira esa ruta hard-coded a Chrome :(

Cómo usar ChromeLauncher

Lighthouse es una herramienta maravillosa para probar la calidad de las apps web. Se desarrolló un módulo sólido para lanzar Chrome dentro de Lighthouse, que ahora se extrae para uso independiente. El módulo chrome-launcher de NPM buscará la ubicación en la que está instalado Chrome, configurará una instancia de depuración, iniciará el navegador y lo cerrará cuando finalice el programa. Lo mejor es que funciona en varias plataformas gracias a Node.js.

De forma predeterminada, chrome-launcher intentará iniciar Chrome Canary (si está instalado), pero puedes cambiarlo para seleccionar manualmente qué Chrome usar. Para usarla, primero instálala desde npm:

npm i --save chrome-launcher

Ejemplo: Usa chrome-launcher para iniciar Headless (sin interfaz gráfica).

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

Ejecutar esta secuencia de comandos no es muy útil, pero deberías ver que se activa una instancia de Chrome en el Administrador de tareas que cargó about:blank. Recuerda que no habrá ninguna IU para navegadores. No tenemos una interfaz gráfica.

Para controlar el navegador, necesitamos el protocolo de Herramientas para desarrolladores.

Cómo recuperar información sobre la página

Instalemos la biblioteca:

npm i --save chrome-remote-interface
Ejemplos

Ejemplo: imprime el usuario-agente

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

...

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

Resultados como: HeadlessChrome/60.0.3082.0

Ejemplo: Verifica si el sitio tiene un manifiesto de aplicación 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.
});

})();

Ejemplo: Extrae la <title> de la página con las APIs de 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.
});

})();

Cómo usar Selenium, WebDriver y ChromeDriver

En este momento, Selenium abre una instancia completa de Chrome. En otras palabras, es una solución automatizada, pero no completamente sin interfaz gráfica. Sin embargo, Selenium se puede configurar para ejecutar Chrome sin interfaz gráfica con un poco de trabajo. Te recomendamos ejecutar Selenium con Headless Chrome si quieres obtener instrucciones completas para configurar las cosas por tu cuenta. Sin embargo, a continuación, incluimos algunos ejemplos para que puedas comenzar.

Con ChromeDriver

ChromeDriver 2.32 usa Chrome 61 y funciona bien con Chrome sin interfaz gráfica.

Instala:

npm i --save-dev selenium-webdriver chromedriver

Ejemplo:

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

Cómo usar WebDriverIO

WebDriverIO es una API de nivel superior además de Selenium WebDriver.

Instala:

npm i --save-dev webdriverio chromedriver

Ejemplo: Filtra las funciones de CSS en 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();

})();

Más recursos

A continuación, te presentamos algunos recursos útiles para empezar:

Documentos

Herramientas

  • chrome-remote-interface: Es el módulo de nodo que une el protocolo de Herramientas para desarrolladores.
  • Lighthouse: Es una herramienta automatizada para probar la calidad de las apps web que usa en gran medida el protocolo.
  • chrome-launcher: Módulo de nodo para iniciar Chrome, listo para la automatización

Demostraciones

  • "The Headless Web": una excelente entrada de blog de Paul Kinlan sobre el uso de Headless con api.ai.

Preguntas frecuentes

¿Necesito la marca --disable-gpu?

Solo en Windows. Otras plataformas ya no lo requieren. La marca --disable-gpu es una solución temporal para algunos errores. No necesitarás esta marca en versiones futuras de Chrome. Consulta crbug.com/737678 para obtener más información.

¿Todavía necesito XVfb?

No. Chrome sin interfaz gráfica no usa una ventana, por lo que ya no se necesita un servidor de visualización como Xvfb. Puedes ejecutar con gusto tus pruebas automatizadas sin él.

¿Qué es Xvfb? Xvfb es un servidor de visualización en la memoria para sistemas similares a Unix que te permite ejecutar aplicaciones gráficas (como Chrome) sin una pantalla física conectada. Muchas personas utilizan Xvfb para ejecutar versiones anteriores de Chrome para realizar pruebas "sin interfaz gráfica".

¿Cómo creo un contenedor de Docker que ejecute Headless Chrome?

Echa un vistazo a Lighthouse-ci. Tiene un Dockerfile de ejemplo que usa node:8-slim como imagen base, instala y ejecuta Lighthouse en App Engine Flex.

¿Puedo usar esta herramienta con Selenium, WebDriver o ChromeDriver?

Sí. Consulta Cómo usar Selenium, WebDriver y ChromeDriver.

¿Cómo se relaciona esto con PhantomJS?

Chrome sin interfaz gráfica es similar a herramientas como PhantomJS. Ambos se pueden usar para pruebas automatizadas en un entorno sin interfaz gráfica. La principal diferencia entre ambos es que Phantom usa una versión anterior de WebKit como motor de procesamiento, mientras que Headless Chrome usa la versión más reciente de Blink.

Por el momento, Phantom también proporciona una API de nivel superior que el protocolo DevOps.

¿Dónde puedo informar errores?

Si encuentras errores en Chrome sin interfaz gráfica, envíalos a crbug.com.

Si encuentras errores en el protocolo de Herramientas para desarrolladores, infórmalos en github.com/ChromeDevTools/devtools-protocol.