Cheerio vs. Puppeteer para raspagem da web

Um olhar sobre as diferenças entre o “Puppeteer” e o “Cheerio”, através da construção de um raspador da web com ambos.
12 min read
Cheerio vs. Puppeteer featured image

Cheerio e Puppeteer são duas bibliotecas Node.js que lhe permitem navegar programaticamente na Internet. Devido a isto, ambas são escolhas populares para aqueles que desejam construir um raspador da web node.js a partir do zero.  

A fim de comparar o Cheerio com o Puppeteer, vamos construir um simples raspador da web com o Cheerio e um raspador da web com o Puppeteer. Utilizaremos ambas as ferramentas para raspar todos os links do blogue de In Plain English, uma plataforma de programação popular.

Mas antes de começarmos, vamos ver o que vamos discutir neste artigo:

Diferenças entre Cheerio e Puppeteer

Há muitas diferenças entre essas 2 bibliotecas, e cada uma delas vem com as suas próprias características especiais que pode aproveitar para a raspagem da web.

Cheerio

  • Cheerio é um analisador DOM, capaz de analisar ficheiros HTML e XML.
  • É uma implementação rápida e simples do núcleo jQuery concebida especificamente para o servidor.
  • Se planeia usar isto para raspar um sítio web, terá de usar o Cheerio em conjunto com uma biblioteca de cliente http do Node.js como o https://axios-http.com/docs/introAxios.  
  • O Cheerio não mostra o sítio web como um navegador (não aplica CSS nem carrega recursos externos).
  • Devido a isto, terá dificuldade em tentar raspar SPAs construídas com tecnologias de vanguarda como a React.
  • O Cheerio não pode interagir com um sítio (por exemplo, não pode clicar em botões) nem aceder ao conteúdo de scripts.
  • Tem uma curva de aprendizagem fácil graças à sua sintaxe simples. Os usuários de jQuery se sentirão em casa aqui.
  • O Cheerio é rápido em comparação com o Puppeteer.

Puppeteer

  • O Puppeteer é uma ferramenta de automatização do navegador. Tem acesso a todo o motor do navegador (geralmente o Chromium).
  • Isto torna-o uma opção mais versátil, em comparação com o Cheerio.
  • Pode executar JavaScript, tornando-o capaz de raspar páginas dinâmicas como aplicações de página única (SPAs).
  • O Puppeteer pode interagir com sítios web, o que significa que pode ser usado para clicar em botões, digitar em formulários de registo, etc.
  • Tem uma curva de aprendizagem íngreme, uma vez que tem mais funcionalidades e requer frequentemente o uso de código assíncrono (ou seja, promessas/async await).
  • O Puppeteer é lento em comparação com o Cheerio.

Construir um Raspador da Web com o Cheerio

Primeiro, vamos criar uma pasta chamada ‘raspador’ para o nosso código. Dentro de ‘raspador’, correr npm init -y ou yarn init -y, dependendo de ter optado por usar npm ou yarn.  

Agora que temos a nossa pasta pronta e package.json inicializado, vamos instalar os nossos pacotes.  

Nota: Pode consultar o nosso guia principal de raspagem da web com node.js que inclui o uso de Cheerio e Axios para raspagem da web com mais detalhe.  

Passo 1 – Instalação de Cheerio

Para instalar o Cheerio, execute o seguinte comando no seu terminal:

// using npm
npm install cheerio

// or using yarn
yarn add cheerio

Passo 2 – Instalação de Axios

Axios é uma biblioteca popular para fazer pedidos HTTP no Node.js. Pode ser utilizado para fazer chamadas de API, obter dados de sítios web, e muito mais.

Para o instalar, execute o seguinte comando no seu terminal:

// using npm
npm install axios

// or using yarn
yarn add axios

Utilizamos Axios para fazer pedidos HTTP para o sítio web que pretendemos raspar. A resposta que obtemos do sítio web é sob a forma de HTML, que podemos então analisar e extrair a informação de que precisamos utilizando o Cheerio.

Passo 3 – Preparar o nosso raspador

Vamos entrar na nossa pasta ‘raspador’ e criar um ficheiro chamado cheerio.js.  

Aqui está a estrutura básica do código para começar com a raspagem da web usando Cheerio e Axios:

const axios = require('axios');
const cheerio = require('cheerio');

axios
 .get("https://plainenglish.io/blog")
 .then((response) => {
   // Initialize links array which we will push the links to later
   let links = [];

   // HTML Markup
   const body = response.data;

   // Load HTML data and initialize cheerio
   const $ = cheerio.load(body);

   // CSS selector for the target element
   const element = ".PostPreview_container__82q9E";

   // Loop through each matching element and extract the text content
   $(element).each(function () {
     // Loop through each matching element and get the href attribute of each element
     const _link = $(this).find("a").prop("href");

     // We check if the link is undefined because cheerio will return undefined if the element doesn't exist
     if (_link !== undefined) {
       // Add the link to the links array
       links.push(`https://plainenglish.io` + _link);
     }
   });

   return links;
 })
 .then((response) => {
   console.log(response);
 });

No código acima, precisamos primeiro as bibliotecas Axios e Cheerio.

Passo 5 – Processamento dos dados

A seguir, fazemos um pedido get() para “https://plainenglish.io/blog”. Porque o Axios é assíncrono, nós encadeamos a nossa função get() com then().  

Inicializamos uma matriz de links vazia para capturar os links que planeamos raspar.

Passamos então o response.data de Axios para Cheerio com:  

// HTML Markup
const body = response.data;

// Load HTML data and initialize cheerio
const $ = cheerio.load(body);
We choose which selector we plan to target, in our case:
// CSS selector for the target element
const element = ".PostPreview_container__82q9E";

Passo 5 – Processamento dos dados

Depois, fazemos um laço através de cada elemento correspondente, encontramos a etiqueta <a>, e agarramos o valor da propriedade href. Para cada partida, empurramo-la para a nossa matriz de links:  

// Loop through each matching element and extract the text content
$(element).each(function () {
 // Loop through each matching element and get the href attribute of each element
 const _link = $(this).find("a").prop("href");

 // We check if the link is undefined because cheerio will return undefined if the element doesn't exist
 if (_link !== undefined) {
   // Add the link to the links array
   links.push(`https://plainenglish.io` + _link);
 }
});

Depois os devolvemos, encadeamos outro then() e enviamos ao console.log a nossa resposta.    

Passo 6 – Resultados finais

Finalmente, se abrirmos um terminal de dentro da nossa pasta ‘raspador’, podemos correr o node.js cheerio.js. Isto irá executar todo o código do nosso ficheiro cheerio.js. Deverá ver os URLs da nossa matriz de links a serem enviados para a consola. Ficará algo parecido com isto:  

 'https://plainenglish.io/blog/how-to-implement-a-search-bar-in-react-js',
 'https://plainenglish.io/blog/how-to-build-data-driven-surveys-with-react-rest-api-surveyjs',
 'https://plainenglish.io/blog/handle-errors-in-angular-with-httpclient-and-rxjs',
 'https://plainenglish.io/blog/deploying-a-localhost-server-with-node-js-and-express-js',
 'https://plainenglish.io/blog/complete-guide-to-data-center-migration',
 'https://plainenglish.io/blog/build-a-stripe-app-with-typescript-and-node-js-part-2',
 ... 861 more items

E assim mesmo, conseguimos raspar o sítio In Plain English!

A partir daqui, podemos ir um passo mais longe e guardar os dados num ficheiro, em vez de simplesmente enviá-los para a consola.

O Cheerio e o Axios facilitam a execução de raspagem da web no Node.js. Com apenas algumas linhas de código, é possível extrair dados de sítios web e utilizá-los para vários fins.  

Construir um Raspador da Web com Puppeteer

Vamos para a nossa pasta ‘raspador’ e criemos um ficheiro chamado puppeteer.js. Já inicializámos o nosso package.json, mas se saltou para esta secção, vá em frente e inicialize agora esse ficheiro.  

Uma vez inicializado, vamos instalar o Puppeteer.

Passo 1 – Instalação do Puppeteer

Para instalar o Puppeteer, executar um dos seguintes comandos:

// using npm
npm install puppeteer

// or using yarn
yarn add puppeteer

Passo 2 – Preparar o nosso raspador

Vamos para a nossa pasta ‘raspador’ e criemos um ficheiro chamado puppeteer.js.  

Aqui está a estrutura básica do código para começar com a raspagem da web usando o Puppeteer:

const puppeteer = require("puppeteer");

// Because everything in Puppeteer is asynchronous,
// we wrap all of our code inside of an async IIFE
(async () => {
 // Initialize links array which we will push the links to later
 let links = [];

 // Launch Puppeteer
 const browser = await puppeteer.launch();

 // Create a new page
 const page = await browser.newPage();

 // Go to URL
 await page.goto("https://plainenglish.io/blog");

 // Set screen size
 await page.setViewport({ width: 1080, height: 1024 });

 // CSS selector for the target element
 const element = ".PostPreview_container__82q9E";

 // Get all matching elements
 const elements = await page.$$(element);

 // Wrapped with Promise.all to wait for all promises to resolve before continuing
 const _links = await Promise.all(
   // Get the href attribute of each element
   elements.map(async (el) => el.evaluate((el) => el.children[0].href))
 );

 if (_links.length) {
   // If there are any links
   _links.forEach((url) => {
     // Loop through each link
     links.push(url); // Add the link to the links array
   });
 }

 console.log(links);

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

No código acima, precisamos primeiro a biblioteca Puppeteer.

Passo 3 – Criação de uma IIFE

A seguir, criamos uma expressão de função imediatamente invocada (IIFE). Como tudo no Puppeteer é assíncrono, colocamos async no início. Em outras palavras, temos isto:  

(async () => {
// ...code goes here
}()

Dentro da nossa IIFE assimétrica, criamos uma matriz de links vazia, que utilizaremos para capturar os links do blogue que estamos a raspar.

// Initialize links array which we will push the links to later
let links = []

A seguir, executamos o Puppeteer, abrimos uma nova página, navegamos para um URL, e definimos o suporte de visualização da página (o tamanho da tela).

 // Launch Puppeteer
 const browser = await puppeteer.launch();

 // Create a new page
 const page = await browser.newPage();

 // Go to URL
 await page.goto("https://plainenglish.io/blog");

 // Set screen size
 await page.setViewport({ width: 1080, height: 1024 });

Por defeito, o Puppeteer corre em ‘modo sem cabeça’. Isto significa que não abre um navegador que se possa ver visualmente. No entanto, definimos um tamanho de visualização pois queremos que o Puppeteer navegue pelo sítio a uma certa largura e altura.

Nota: Se decidir que gostaria de ver o que o “Puppeteer” está a fazer em tempo real, pode passar a opção “headless: false” como parâmetro, assim:  

// Launch Puppeteer
const browser = await puppeteer.launch({ headless: false });

Passo 5 – Processamento dos dados

A partir daqui, escolhemos qual é o seletor que pretendemos visar, no nosso caso:

// CSS selector for the target element
const element = ".PostPreview_container__82q9E";

E executamos o que equivale à querySelectorAll() para o nosso elemento alvo:  

// Get all matching elements
const elements = await page.$$(element);

Nota: $$ não é o mesmo que querySelectorAll, por isso não espere ter acesso a todas as mesmas coisas.

Passo 5 – Processamento dos dados

Agora que temos os nossos elementos armazenados dentro de elementos, mapeamos cada elemento para retirar a propriedade href:  

// Wrapped with Promise.all to wait for all promises to resolve before continuing
const _links = await Promise.all(
 // Get the href attribute of each element
 elements.map(async (el) => el.evaluate((el) => el.children[0].href))
);

No nosso caso de uso específico, temos el.children[0], pois sei que o primeiro elemento secundário do nosso elemento alvo é uma etiqueta a, e é a etiqueta a cujo valor eu quero.  

Em seguida, fazemos um laço através de cada elemento mapeado e empurramos o valor para a nossa matriz de links, desta forma:

if (_links.length) {
 // If there are any links
 _links.forEach((url) => {
   // Loop through each link
   links.push(url); // Add the link to the links array
 });
}

Finalmente, enviamos os links ao console.log, e depois fechamos o navegador:  

console.log(links);

await browser.close();

Nota: Se não fechar o navegador, este permanecerá aberto e o seu terminal ficará suspenso.  

Passo 6 – Resultados finais

Agora, se abrirmos um terminal de dentro da nossa pasta ‘raspador’, podemos executar node.js puppeteer.js. Isto executará todo o código do nosso ficheiro puppeteer.js. Deverá ver os URLs da nossa matriz de links a serem enviados para a consola. Ficará algo parecido com isto:  

'https://plainenglish.io/blog/how-to-implement-a-search-bar-in-react-js',
 'https://plainenglish.io/blog/how-to-build-data-driven-surveys-with-react-rest-api-surveyjs',
 'https://plainenglish.io/blog/handle-errors-in-angular-with-httpclient-and-rxjs',
 'https://plainenglish.io/blog/deploying-a-localhost-server-with-node-js-and-express-js',
 'https://plainenglish.io/blog/complete-guide-to-data-center-migration',
 'https://plainenglish.io/blog/build-a-stripe-app-with-typescript-and-node-js-part-2',
 ... 861 more items

E assim mesmo, conseguimos raspar o sítio web com o Puppeteer!

Puppeteer é uma ferramenta poderosa para raspar e automatizar tarefas de navegação na web. Fornece uma API rica para tarefas de raspagem e automatização de navegação na web. Pode utilizá-la para extrair informação de sítios web, gerar capturas de ecrã e PDFs, e realizar muitas outras tarefas.

Se quiser usar o Puppeteer para raspar os principais sítios, deve considerar integrar o Puppeteer com um proxy, para evitar ficar bloqueado.  

Nota: Existem outras alternativas ao “Puppeteer”, tais como o Selenium, ou o IDE para Raspador da Web. Ou, se quiser poupar tempo, pode saltar todo o processo de raspagem da web completamente, procurando conjuntos de dados prontos.  

Conclusão

Se procura raspar páginas estáticas sem a necessidade de interações tais como cliques e envios de formulários (ou qualquer tipo de tratamento de eventos), o Cheerio é a escolha ideal.

No entanto, se o sítio web depende do JavaScript para a injeção de conteúdo, ou se precisa de tratar eventos, é necessário o Puppeteer.

Qualquer que seja a abordagem que decidir, vale a pena notar que este caso de uso específico foi bastante simples. Se tentar raspar algo mais complexo, como um sítio web dinâmico (YouTube, Twitter, ou Facebook, por exemplo), poderá encontrar-se em águas bastante profundas.  

Se procura raspar sítios web e não quer perder semanas a tentar decifrar uma solução, talvez seja melhor procurar uma solução pronta a usar, tal como o IDE para Raspador da Web.  

O IDE da Bright Data inclui funções de raspagem pré-fabricadas, infraestrutura proxy de desbloqueio sofisticada incorporada, scripts de navegador em JavaScript, depuração, e vários modelos de raspagem prontos a usar para sítios web populares.