Modo headless do Chrome

Peter Kvitek
Peter Kvitek

Em 2017, o Chrome 59 lançou o modo headless, que permite executar o navegador em um ambiente autônomo, sem nenhuma interface visível. Basicamente, era possível executar o Chrome sem ele.

O modo headless é uma escolha comum para automação de navegadores, por meio de projetos como o Puppeteer ou o ChromeDriver. Veja um exemplo mínimo de linha de comando que usa o modo headless para criar um arquivo PDF de um determinado URL:

chrome --headless --print-to-pdf https://developer.chrome.com/

Como o Headless funciona

Antes de revisar como o Headless funciona agora, é importante entender como o "antigo" funcionava. O snippet de linha de comando anterior usa a sinalização de linha de comando --headless, sugerindo que a versão headless é apenas um modo de operação do navegador Chrome normal. Surpreendentemente, isso não é realmente verdade. Na verdade, o antigo Headless era uma implementação de navegador alternativa separada que era enviada como parte do mesmo binário do Chrome. Ele não compartilha nenhum código do navegador Chrome em //chrome.

Implementar e manter um navegador headless separado gerou muita sobrecarga de engenharia. E, como o Headless era uma implementação separada, ele tinha os próprios bugs e recursos que não estavam presentes no headful do Chrome. Isso criava confusão para os testes automatizados do navegador, que podem passar no modo headful, mas falhar no modo headless, ou vice-versa.

Além disso, a Headless excluiu todos os testes automatizados que dependiam da instalação de extensões do navegador. O mesmo vale para qualquer outra função no nível do navegador. A menos que o Headless tivesse uma implementação própria separada, ele não era compatível.

A equipe do Chrome unificou os modos headless e headful.

O novo Chrome Headless não é mais uma implementação de navegador separada e agora compartilha o código com o Chrome.

O novo modo headless está disponível a partir do Chrome 112. Nesse modo, o Chrome cria, mas não exibe, nenhuma janela de plataforma. Todas as outras funções, atuais e futuras, estão disponíveis sem limitações.

Usar o modo headless

Para usar o novo modo headless, transmita a flag de linha de comando --headless=new:

chrome --headless=new

Por enquanto, o antigo modo headless ainda está disponível com:

chrome --headless=old

No Puppeteer

Para ativar o novo modo headless no Puppeteer:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({
  headless: 'new',
  // `headless: true` (default) enables old Headless;
  // `headless: 'new'` enables new Headless;
  // `headless: false` enables "headful" mode.
});

const page = await browser.newPage();
await page.goto('https://developer.chrome.com/');

// …

await browser.close();

No Selenium-WebDriver

Para usar o novo modo headless no Selenium-WebDriver:

const driver = await env
  .builder()
  .setChromeOptions(options.addArguments('--headless=new'))
  .build();

await driver.get('https://developer.chrome.com/');

// …

await driver.quit();

Consulte a postagem do blog da equipe do Selenium (em inglês) para mais informações, incluindo exemplos que usam outras vinculações de linguagem.

Sinalizações de linha de comando

As sinalizações de linha de comando a seguir estão disponíveis no novo modo headless.

--dump-dom

A sinalização --dump-dom imprime o DOM serializado da página de destino na stdout. Exemplo:

chrome --headless=new --dump-dom https://developer.chrome.com/

Isso é diferente de exibir o código-fonte HTML, que é possível fazer com curl. Para exibir a saída de --dump-dom, primeiro o Chrome analisa o código HTML em um DOM, executa qualquer <script> que possa alterar o DOM e depois transforma esse DOM novamente em uma string serializada de HTML.

--screenshot

A flag --screenshot faz uma captura de tela da página de destino e a salva como screenshot.png no diretório de trabalho atual. Isso é especialmente útil em combinação com a sinalização --window-size.

Exemplo:

chrome --headless=new --screenshot --window-size=412,892 https://developer.chrome.com/

--print-to-pdf

A sinalização --print-to-pdf salva a página de destino como um PDF chamado output.pdf no diretório de trabalho atual. Exemplo:

chrome --headless=new --print-to-pdf https://developer.chrome.com/

Se quiser, adicione a sinalização --no-pdf-header-footer para omitir o cabeçalho de impressão (com a data e a hora atuais) e o rodapé (com o URL e o número da página).

chrome --headless=new --print-to-pdf --no-pdf-header-footer https://developer.chrome.com/

Não: a funcionalidade por trás da sinalização --no-pdf-header-footer estava disponível anteriormente com a sinalização --print-to-pdf-no-header. Se você estiver usando uma versão anterior, talvez seja necessário voltar para o nome antigo da sinalização.

--timeout

A sinalização --timeout define o tempo máximo de espera (em milissegundos) para capturar o conteúdo da página por --dump-dom, --screenshot e --print-to-pdf, mesmo que a página ainda esteja carregando.

chrome --headless=new --print-to-pdf --timeout=5000 https://developer.chrome.com/

A sinalização --timeout=5000 informa ao Chrome para aguardar até cinco segundos antes de imprimir o PDF. Assim, esse processo leva no máximo 5 segundos para ser executado.

--virtual-time-budget

O --virtual-time-budget atua como um "avanço rápido" para qualquer código que depende de tempo (por exemplo, setTimeout/setInterval). Ele força o navegador a executar qualquer código da página o mais rápido possível, enquanto faz a página acreditar que o tempo realmente passa.

Para ilustrar o uso, considere esta demonstração, que incrementa, registra e exibe um contador a cada segundo usando setTimeout(fn, 1000). Confira o código relevante:

<output>0</output>
<script>
  const element = document.querySelector('output');
  let counter = 0;
  setInterval(() => {
    counter++;
    console.log(counter);
    element.textContent = counter;
  }, 1_000);
</script>

Depois de um segundo, a página contém "1", depois de dois segundos, "2" e assim por diante. Veja como capturar o estado da página após 42 segundos e salvá-lo como PDF:

chrome --headless=new --print-to-pdf --virtual-time-budget=42000 https://mathiasbynens.be/demo/time

--allow-chrome-scheme-url

A sinalização --allow-chrome-scheme-url é necessária para acessar URLs chrome://. Essa sinalização está disponível desde o Chrome 123. Veja um exemplo:

chrome --headless=new --print-to-pdf --allow-chrome-scheme-url chrome://gpu

Depuração

Como o Chrome é efetivamente invisível no modo headless, pode parecer complicado resolver um problema. É possível depurar o Headless Chrome de uma maneira muito parecida com a headful do Chrome.

Inicie o Chrome no modo headless com a sinalização de linha de comando --remote-debugging-port.

chrome --headless=new --remote-debugging-port=0 https://developer.chrome.com/

Isso imprime um URL WebSocket exclusivo em stdout, por exemplo:

DevTools listening on ws://127.0.0.1:60926/devtools/browser/b4bd6eaa-b7c8-4319-8212-225097472fd9

Em uma instância headful do Chrome, podemos usar a depuração remota do Chrome DevTools (link em inglês) para se conectar ao destino headless e inspecioná-lo.

  1. Acesse chrome://inspect e clique no botão Configure....
  2. Digite o endereço IP e o número da porta do URL do WebSocket.
    • No exemplo anterior, inseri 127.0.0.1:60926.
  3. Clique em Concluído. Um destino remoto vai aparecer com todas as guias e outros destinos listados.
  4. Clique em inspecionar para acessar o Chrome DevTools e inspecionar o destino sem comando remoto, incluindo uma imagem ao vivo da página.

O Chrome DevTools pode inspecionar uma página de destino headless remoto

Feedback

Queremos seu feedback sobre o novo modo headless. Se você encontrar algum problema, registre um bug.