Node.js: web scraping com Puppeteer

Puppeteer é uma biblioteca Node.js que fornece uma API de alto nível para controle programático do Chrome ou Chromium por meio do protocolo DevTools. Esse controle pode ocorrer nos bastidores (segundo plano sem lançamento de janela), ou se preferir, é possível acompanhar toda a execução de forma visual ao solicitar o lançamento da interface gráfica do navegador.

O Chromium é um navegador web open source desenvolvido pela Google, seu código é utilizado inclusive no Chrome. Estes compartilham muitos recursos, porém, possuem licenças de uso distintas.

Recursos do Puppeteer

Basicamente, qualquer “coisa” feita via uso do Chrome ou Chromium pode ser automatizada via Puppeteer. Veja alguns exemplos:

  • Simular ações de usuários, tais como navegar nas páginas, clicar em links/botões, preencher formulários e enviá-los;
  • Disparar capturas de tela (screenshots) das páginas;
  • Salvar determinado conteúdo em PDF;
  • Monitorar determinado valor ou condição da página para disparar uma ação secundaria;
  • Buscar dados dentro das páginas;
  • e muito mais…

De fato as possibilidades são infinitas! O Puppeteer pode ser utilizando tanto para Web Scraping quanto para Web Crawling. Vejamos rapidamente o que cada termo significa:

  • Web Scraping: ato de baixar automaticamente os dados de uma ou mais páginas tendo como principal objetivo extrair informações muito especificas; Automatizar ações realizadas via uso da interface do navegador, tais como preencher e submeter formulários ou até mesmo simular a navegação de um usuário dentro do “site”.
  • Web Crawling: ato de baixar automaticamente os dados de uma página web, extrair os hiperlinks contidos nela e segui-los de forma recursiva. De modo grosseiro e simplório, imagine que essa é uma das técnicas utilizadas por buscadores como Google, Bing e outros.

📍 Onde utilizar?

Difícil falar onde não utilizar. Mas alguns cenários imagináveis são:

  1. Necessidade de consultar dados de páginas web que envolvem interação complexa por parte do usuário, como preencher formulários, lidar com cookies, interação frente a eventos disparados pela página e assim por diante;
  2. Raspar dados de páginas web, iterar sobre os dados e monitorar alterações;
  3. Lidar com páginas web onde o JavaScript é responsável pela renderização dos componentes.

O tópico 3 foi o que me motivou a utilizar o Puppeteer. É relativamente simples “acessar” uma página web de forma programática, consultar e submeter dados, porém, isso se torna gradualmente mais complexo conforme elas passam a utilizar JavaScript para renderizar seus componentes e reagir a eventos resultantes da interação do usuário.

Existem inúmeras abordagens, ferramentas e bibliotecas que permitem buscar dados em páginas web, submeter formulários e assim por diante, porém, muitas tentam fazer essa manipulação considerando somente a estrutura HTML das páginas, ou seja, atuam diretamente sobre a árvore DOM (Document Object Model). Atuar sobre a DOM é algo simples em razão do seu estado estático, porém, as coisas mudam quando temos páginas que utilizam JavaScript para fazer a renderização do lado do cliente (client side rendering), nestes casos é necessário que exista um motor capaz de executar/interpretar o JavaScript da página, posteriormente a essa ação a DOM será atualizada e seu estado mais recente estará disponível para nosso Web Scraping ou Web Crawling.

Puppeteer na prática

Segue abaixo a lista de pré-requisitos para construir uma solução de Web Scraping utilizando o Puppeteer. Veja:

Ao longo deste material irei utilizar o Yarn como gerenciador de dependências, porém, fique a vontade para utilizar o NPM se assim preferir. Já como editor de código utilizarei o VSCode, porém, você pode utilizar o sabor de editor que preferir. 😋

Instalando

Para os passos a seguir, sugiro que crie um diretório, acesse o mesmo e concentre todo o código dentro deste. Let’s get started! 🚀

  • Para criar um novo projeto auto gerado basta executar:
yarn init -y
  • init: instrui o yarn a criar um arquivo package.json, responsável por guardar algumas definições iniciais do projeto, bem como a lista de dependências;
  • -y: confirmação silenciosa de definições iniciais. Ao remover este parâmetro você poderá especificar o nome do projeto, descrição, autor etc.

Agora iremos instalar nossa única dependência:

yarn add puppeteer

Conforme a documentação oficial, o pacote puppeteer ocupa no:

  • MacOS: ~170MB
  • Linux: ~282MB
  • Windows: ~280MB

Ao instalar o módulo puppeteer, será feito download automático de uma versão standalone do Chromium, logo os valores apresentados acima representam o espaço que o binário ocupa em disco. Opcionalmente você poderia utilizar o pacote puppeteer-core, este por sua vez não realiza o download do binário, sendo assim é possível utilizar a versão já instalada na máquina. Pessoalmente eu prefiro instalar a versão que já trás o binário junto, assim mantenho as coisas separadas e evito que minhas customizações pessoais sejam um fator a mais de debug.

Primeiro uso (exemplo 1)

Este exemplo simplório tem por objetivo demonstrar o funcionamento do Puppeteer. Crie na raiz do seu projeto o arquivo example1.js e cole o código abaixo:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  await page.goto('http://books.toscrape.com/');
  await page.screenshot({path: 'example1.png'});

  await browser.close();
})();

Agora execute no terminal:

node example1.js

Observe que no seu diretório foi criado um arquivo de imagem chamado example1.png, este arquivo representa a captura de tela da página http://books.toscrape.com. Calma, ainda iremos muito além deste exemplo, porém, um passo de cada vez né!

Entendendo o código

O código apresentado anteriormente servirá de base para nosso pontapé inicial. Agora iremos entender cada uma das linhas, começando por:

const puppeteer = require('puppeteer');

Importe do módulo do Puppeteer, essa é a API de alto nível que falamos no inicio deste post.

(async () => {
 // ... código aqui
})();

Função anônima assíncrona, isso é necessário pois nosso código irá acessar dados que não estão imediatamente disponíveis, ou seja, recursos que podem demorar para serem retornados. Para saber mais a respeito deste comportamento leia meu post: Introdução ao Node.js (Single-Thread, Event-Loop e mercado).

const browser = await puppeteer.launch();

Por padrão irá lançar uma instância standalone do Chormium, porém, é possível passar o caminho para uma instalação diferente do Chromium ou Chrome, bem como informar altura e largura da janela, mesmo quando o lançamento ocorre em background.

const page = await browser.newPage();

Abre uma nova aba no navegador.

await page.goto('http://books.toscrape.com/');

Acessa a URL informada. O site escolhido para demonstração é um projeto aberto desenvolvido especialmente para ser alvo de testes de raspagem (web scraping ou até mesmo web crawling).

await page.screenshot({path: 'example1.png'});

Tira uma captura de tela e salva a imagem no disco.

await browser.close();

Encerra a instância do navegador.

Para mais detalhes consulte a documentação oficial: https://pptr.dev

Raspando dados (Exemplo 2)

No primeiro exemplo acessamos uma página web e disparamos uma captura de tela, nosso objetivo era simples, entender o funcionamento básico do Puppeteer. Agora veremos como extrair dados de uma página web. Crie um arquivo chamado example2.js na raiz do seu projeto e adicione o código abaixo a ele:

const puppeteer = require('puppeteer')
let scrape = async () => {
  const browser = await puppeteer.launch()
  const page = await browser.newPage()
  await page.goto('http://books.toscrape.com/')
  const result = await page.evaluate(() => {
    const books = []
    document.querySelectorAll('section > div > ol > li img')
            .forEach((book) => books.push(book.getAttribute('alt')))
    return books
  })
  browser.close()
  return result
}
scrape().then((value) => {
  console.log(value)
})

Agora execute no terminal:

node example2.js

Como resultado você verá algo mais ou menos igual a imagem abaixo:

Continue lendo em: https://medium.com/@fabiojanio/node-js-web-scraping-com-puppeteer-29dd974eb042