Voltar ao blogue
Guias
Gabriel CiociLast updated on Mar 31, 20266 min read

Web scraping com o Puppeteer: Node.JS avançado

Web scraping com o Puppeteer: Node.JS avançado

Em vez de utilizar ferramentas comerciais, muitos programadores preferem criar os seus próprios web scrapers. Embora os produtos disponíveis tenham funcionalidades mais completas, não podemos negar os resultados que estes bots podem proporcionar, nem a satisfação de criar o seu próprio.

No artigo a seguir, irá descobrir os passos que deve seguir para criar o seu próprio web scraper utilizando o Node.js e o Puppeteer. Iremos programar uma aplicação que carrega um site, tira uma captura de ecrã, inicia sessão no site utilizando um navegador headless e extrai alguns dados em várias páginas. A sua aplicação irá tornar-se mais complexa à medida que avança.

Uma visão geral do Web Scraping com o Puppeteer

Uma visão geral do Web Scraping com o Puppeteer

A Google concebeu o Puppeteer para fornecer uma interface simples, mas poderosa, no Node.js para automatizar testes e várias tarefas utilizando o motor do navegador Chromium. Por predefinição, funciona sem interface gráfica, mas pode ser configurado para executar o Chrome ou o Chromium na íntegra.

A API criada pela equipa do Puppeteer utiliza o Protocolo DevTools para assumir o controlo de um navegador web, como o Chrome, e realizar diferentes tarefas, tais como:

  • Capturar imagens e gerar PDFs de páginas
  • Automatizar o envio de formulários
  • Testar a interface do utilizador (clicar em botões, introduzir dados via teclado, etc.)
  • Extrair dados de uma SPA e gerar conteúdo pré-renderizado (Renderização do Lado do Servidor)

A maioria das ações que pode realizar manualmente no navegador também pode ser feita utilizando o Puppeteer. Além disso, podem ser automatizadas para que possa poupar mais tempo e concentrar-se noutros assuntos.

O Puppeteer também foi concebido para ser intuitivo para os programadores. Quem estiver familiarizado com outras estruturas de teste populares, como o Mocha, sentir-se-á em casa com o Puppeteer e encontrará uma comunidade ativa que oferece apoio ao Puppeteer. Isto levou a um enorme crescimento da popularidade entre os programadores.

É claro que o Puppeteer não é adequado apenas para testes. Afinal, se ele consegue fazer tudo o que um navegador padrão consegue, então pode ser extremamente útil para web scrapers. Nomeadamente, pode ajudar na execução de código JavaScript para que o scraper consiga aceder ao HTML da página e imitar o comportamento normal de um utilizador, percorrendo a página ou clicando em secções aleatórias.

Estas funcionalidades tão necessárias tornam os navegadores headless um componente essencial para qualquer ferramenta comercial de extração de dados e para todos os web scrapers caseiros, exceto os mais simples.

Pré-requisitos

Pré-requisitos

Em primeiro lugar, certifique-se de que tem as versões atualizadas do Node.js e do Puppeteer instaladas no seu computador. Se não for esse o caso, pode seguir os passos abaixo para instalar todos os pré-requisitos.

Pode descarregar e instalar o Node.js a partir daqui. O gestor de pacotes padrão do Node, o npm, vem pré-instalado com o Node.js.

Para instalar a biblioteca Puppeteer, pode executar o seguinte comando no diretório raiz do seu projeto:

npm install puppeteer
# or "yarn add puppeteer"

Note que, ao instalar o Puppeteer, também é descarregada a versão mais recente do Chromium, que é garantidamente compatível com a API.

O Puppeteer em ação

O Puppeteer em ação

Há muitas coisas diferentes que pode fazer com a biblioteca. Uma vez que o nosso foco principal é a extração de dados da Web, falaremos sobre os casos de utilização que provavelmente mais lhe interessarão se pretender extrair dados da Web.

Fazer uma captura de ecrã

Fazer uma captura de ecrã

Vamos começar com um exemplo básico. Vamos escrever um script que irá capturar uma captura de ecrã de um site à nossa escolha.

Tenha em mente que o Puppeteer é uma biblioteca baseada em promessas (ele realiza chamadas assíncronas para a instância do Chrome sem interface gráfica nos bastidores). Por isso, vamos manter o código limpo usando async/await.

Primeiro, crie um novo ficheiro chamado index.js no diretório raiz do seu projeto.

Dentro desse ficheiro, precisamos de definir uma função assíncrona e envolvê-la em todo o código do Puppeteer.

const puppeteer = require('puppeteer')

async function snapScreenshot() {
	try {
		const URL = 'https://old.reddit.com/'
		const browser = await puppeteer.launch()
		const page = await browser.newPage()

		await page.goto(URL)
		await page.screenshot({ path: 'screenshot.png' })

		await browser.close()
	} catch (error) {
		console.error(error)
	}
}

snapScreenshot()

Primeiro, uma instância do navegador é iniciada usando o comando puppeteer.launch(). Em seguida, criamos uma nova página usando a instância do navegador. Para navegar até ao site desejado, podemos usar o método goto(), passando o URL como parâmetro. Para capturar uma imagem, usaremos o método screenshot(). Também precisamos de passar o local onde a imagem será guardada.

Note que o Puppeteer define um tamanho inicial de página de 800×600px, o que define o tamanho da captura de ecrã. Pode personalizar o tamanho da página utilizando o método setViewport().

Não se esqueça de fechar a instância do navegador. Depois, basta executar node index.js no terminal.

É mesmo assim tão simples! Deverá agora ver um novo ficheiro chamado screenshot.png na pasta do seu projeto.

Enviar um formulário

Enviar um formulário

Se, por algum motivo, o site que pretende rastrear não mostrar o conteúdo a menos que esteja conectado, pode automatizar o processo de login com o Puppeteer.

Primeiro, precisamos de inspecionar o site que estamos a extrair e encontrar os campos de login. Podemos fazer isso clicando com o botão direito do rato no elemento e escolhendo a opção Inspecionar.

No meu caso, os campos de entrada estão dentro de um formulário com a classe login-form. Podemos introduzir as credenciais de login utilizando o método type().

Além disso, se quiseres garantir que ele executa as ações corretas, podes adicionar o parâmetro headless e defini-lo como false ao lançares a instância do Puppeteer. Verás então como o Puppeteer realiza todo o processo por ti.

const puppeteer = require('puppeteer')

async function login() {
   try {
       const URL = 'https://old.reddit.com/'
       const browser = await puppeteer.launch({headless: false})
       const page = await browser.newPage()

       await page.goto(URL)

       await page.type('.login-form input[name="user"]', 'EMAIL@gmail.com')
       await page.type('.login-form input[name="passwd"]', 'PASSWORD')

       await Promise.all([
           page.click('.login-form .submit button'),
           page.waitForNavigation(),
       ]);
      
       await browser.close()

   } catch (error) {
       console.error(error)
   }
}

login()

Para simular um clique do rato, podemos usar o método click(). Depois de clicarmos no botão de login, devemos esperar que a página carregue. Podemos fazer isso com o método waitForNavigation().

Se introduzimos as credenciais corretas, já devemos estar a fazer login!

Extrair várias páginas

Extrair várias páginas

Vou usar o subreddit /r/learnprogramming para este artigo. Portanto, queremos navegar até ao site e recolher o título e o URL de cada publicação. Vamos usar o método evaluate() para isso.

O código deve ficar assim:

const puppeteer = require('puppeteer')

async function tutorial() {
   try {
       const URL = 'https://old.reddit.com/r/learnprogramming/'
       const browser = await puppeteer.launch()
       const page = await browser.newPage()

       await page.goto(URL)
       let data = await page.evaluate(() => {
           let results = []
           let items = document.querySelectorAll('.thing')
           items.forEach((item) => {
               results.push({
                   url: item.getAttribute('data-url'),
                   title: item.querySelector('.title').innerText,
               })
           })
           return results
       })

       console.log(data)
       await browser.close()

   } catch (error) {
       console.error(error)
   }
}

tutorial()

Usando o método Inspect apresentado anteriormente, podemos obter todas as publicações selecionando o seletor .thing. Iteramos por elas e, para cada uma, obtemos a URL e o título e colocamo-los numa matriz.

Depois de todo o processo estar concluído, pode ver o resultado na sua consola.

Ótimo, já extraímos a primeira página. Mas como é que extraímos várias páginas deste subreddit?

É mais simples do que pensas. Aqui está o código:

const puppeteer = require('puppeteer')

async function tutorial() {
   try {
       const URL = 'https://old.reddit.com/r/learnprogramming/'
       const browser = await puppeteer.launch({headless: false})
       const page = await browser.newPage()

       await page.goto(URL)
       let pagesToScrape = 5;
       let currentPage = 1;
       let data = []
       while (currentPage <= pagesToScrape) {
           let newResults = await page.evaluate(() => {
               let results = []
               let items = document.querySelectorAll('.thing')
               items.forEach((item) => {
                   results.push({
                       url: item.getAttribute('data-url'),
                       text: item.querySelector('.title').innerText,
                   })
               })
               return results
           })
           data = data.concat(newResults)
           if (currentPage < pagesToScrape) {
               await page.click('.next-button a')
               await page.waitForSelector('.thing')
               await page.waitForSelector('.next-button a')
           }
           currentPage++;
       }
       console.log(data)
       await browser.close()
   } catch (error) {
       console.error(error)
   }
}

tutorial()

Precisamos de uma variável para saber quantas páginas queremos extrair e outra variável para a página atual. Enquanto a página atual for menor ou igual ao número de páginas que queremos extrair, obtemos a URL e o título de cada publicação na página. Depois de cada página ser extraída, concatenamos os novos resultados com os já extraídos.

Depois, clicamos no botão da página seguinte e repetimos o processo de extração até atingirmos o número desejado de páginas extraídas. Também precisamos de incrementar a página atual após cada página.

Uma opção ainda mais fácil

Uma opção ainda mais fácil

Parabéns! Criou com sucesso o seu próprio scraper web com o Puppeteer. Espero que tenha gostado do tutorial!

Mesmo assim, o script que criámos neste guia não consegue realizar tarefas complexas. Faltam-lhe alguns aspetos essenciais que tornam o web scraping perfeito. A utilização de proxies móveis ou residenciais e a resolução de CAPTCHAs são apenas algumas das funcionalidades em falta.

Se procura uma forma mais profissional de extrair dados, veja o que a WebScrapingAPI consegue fazer e veja se é uma boa opção. Existe um pacote gratuito, pelo que tudo o que precisa de investir são 30 minutos do seu tempo.

Boa sorte com o web scraping!

Sobre o autor
Gabriel Cioci, Desenvolvedor Full-Stack @ WebScrapingAPI
Gabriel CiociDesenvolvedor Full-Stack

Gabriel Cioci é um programador Full Stack na WebScrapingAPI, responsável pela criação e manutenção dos sites, do painel do utilizador e das principais funcionalidades da plataforma destinadas aos utilizadores.

Comece a construir

Pronto para expandir a sua recolha de dados?

Junte-se a mais de 2.000 empresas que utilizam a WebScrapingAPI para extrair dados da Web à escala empresarial, sem quaisquer custos de infraestrutura.