Node.js: web scraping com Puppeteer

O Puppeteer é uma biblioteca Node que fornece uma API de alto nível para controlar o Chrome ou o Chromium sobre o protocolo DevTools. Esse controle pode ocorrer nos bastidores (segundo plano sem lançamento de janelas), ou se preferir, você pode visualmente acompanhar o que está ocorrendo ao optar por lançar uma instancia gráfica do navegador web.

Recursos do Puppeteer

Basicamente, tudo que você faz manualmente em seu navegador pode ser feito via Puppeteer, exemplo:

  • Navegar nas páginas, clicar em botões, preencher formulários e enviar;
  • Tirar captura (screenshots) de tela das páginas;
  • Salvar conteúdo em PDF;
  • Monitorar determinado valor ou condição da página para disparar uma ação secundaria;
  • Buscar dados dentro da página;
  • e muito mais…

As possibilidades são infinitas.

Onde utilizar o Puppeteer

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

  1. Necessidade de consultar dados de páginas web que envolvem uma 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, interar sobre os dados e sobre a página pripriamente dita na ausência de suporte a um modelo de API;
  3. Lidar com páginas web onde o JavaScript é responsável por dinamicamente renderizar os dados.

O tópico 3 foi o que me motivou a utilizar o Puppeteer. É algo muito simples acessar uma página web, consultar e submeter dados, porém, isso se torna mais complexo conforme elas passam a utilizar JavaScript para renderizar os dados e controlar eventos diversos.

Ferramentas e bibliotecas tradicionais buscam os dados na estrutura do “HTML”, ou seja, buscam na arvore DOM (Document Object Model). Mas o JavaScript tem a capacidade de modificar a DOM após sua renderização, es o problema, os meios tradicionais normalmente não processam/renderizam o JavaScript, logo, tudo que é exibido ou que ocorre após sua renderização não é enxergado por essas ferramentas tradicionais de raspagem, sendo necessário então utilizar alternativas como o Puppeteer.

Instalando

Antes de continuarmos, é necessário atender a alguns pré-requisitos, sendo eles, ter instalado em sua máquina o:

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

Passos

  • Primeiramente devemos:
Crie um diretório; e
Acesse este diretório
  • Para criar um novo projeto basta executar:
yarn init -y
  • init: essa instrução instrui o yarn a criar um arquivo Package.json, responsável por guardar algumas definições iniciais do projeto e a lista de dependências;
  • -y: ativa confirmação silenciosa, ou seja, preencher todas as perguntas com a resposta padrão, deste modo não seremos questionados quanto as escolhas durante o processo de criação, você é livre para remover o -yse assim preferir.

Agora iremos instalar nossa única dependência:

yarn add puppeteer

Primeiro uso (exemplo 1)

Vamos a um exemplo bem simples para entendermos o funcionamento do Puppeteer. Crie na raiz do seu projeto o arquivo exemplo1.js e adicione a ele esse código:

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: 'exemplo1.png'});await browser.close();
})();

Agora execute:

node exemplo1.js

Observe que no seu diretório foi criada uma imagem chamada exemplo1.png, ela é a captura de tela dá 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');

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

(async () => {

Criamos uma função anônimo assíncrona, isso é necessário pois nosso código irá acessar dados que não estarão imediatamente disponíveis, ou seja, dados assíncronos, que podem demorar para serem retornados, logo, se uma parte posterior do nosso código depender desses dados, teremos que esperar seu retorno para continuar. Para quem trabalha com Node isso não é novidade, dada a forma como a ferramenta processa os eventos:

const browser = await puppeteer.launch();

Lança uma instância do navegador. Por baixo dos panos o Chrome ou Chromium será chamado, vai depender do que você tem instalado ou das configurações adicionais que passou para este método.

const page = await browser.newPage();

Cria uma nova página (equivalente a uma aba).

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

Acessa a URL http://books.toscrape.com/. Este site foi projetado especialmente para desenvolvedores testarem técnicas de scraping (raspagem).

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

Tiramos uma captura de tela, ela será gravada no mesmo local em que o script foi disparado, neste caso, na raiz do projeto.

await browser.close();

Encerra a instância do navegador.

})();

Encerramento do corpo da função anônima seguido por uma chamada imediata.

Para visualizar a lista completa de opções e parâmetros disponíveis, consulte a documentação oficial da ferramenta disponível em: https://github.com/puppeteer/puppeteer/blob/v2.0.0/docs/api.md

Raspando dados (Exemplo 2)

No primeiro exemplo acessamos uma página e tiramos uma captura de tela, nosso objetivo era simples, entender a estrutura básica do Puppeteer. Agora veremos como extrair dados de uma página web. Crie um arquivo chamado exemplo2.js na raiz do projeto e adicione este código a ele:

const puppeteer = require('puppeteer')let scrape = async () => {
  const browser = await puppeteer.launch({headless: true})
  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)
})

Execute com:

node exemplo2.js

Resultado exibido no terminal no momento em que escrevo este post:

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