Cheerio vs. Puppeteer para raspado de datos web

Una mirada a las diferencias entre Puppeteer y Cheerio al construir un raspador web con ambos.
12 min read
Cheerio vs. Puppeteer featured image

Cheerio y Puppeteer son dos librerías Node.js que te permiten navegar programáticamente por internet. Debido a esto, ambas son opciones populares para aquellos que desean construir un raspador web node.js desde cero.  

Para comparar Cheerio y Puppeteer, construiremos un raspador web simple con Cheerio y un raspador web con Puppeteer. Usaremos ambas herramientas para raspar datos de todos los enlaces de blogs de In Plain English, una popular plataforma de programación.

Pero antes de empezar, veamos de qué vamos a hablar en este artículo.

Diferencias entre Cheerio y Puppeteer

Hay muchas diferencias entre estas 2 bibliotecas, y cada una viene con sus propias características especiales que puede aprovechar para el raspado de datos web.

Cheerio

  • Cheerio es un analizador DOM, capaz de analizar archivos HTML y XML.
  • Es una implementación rápida y sencilla del núcleo de jQuery diseñada específicamente para el servidor.
  • Si planea usar esto para raspar datos en un sitio web, necesitará usar Cheerio en conjunto con una librería cliente http Node.js como Axios.  
  • Cheerio no renderiza el sitio web como un navegador (no aplica CSS ni carga recursos externos).
  • Debido a esto, le será difícil tratar de raspar datos en SPAs construidas con tecnologías frontales como React.
  • Cheerio no puede interactuar con un sitio (por ejemplo, no puede hacer clic en botones) ni acceder a contenidos detrás de scripts.
  • Tiene una curva de aprendizaje fácil gracias a su sintaxis simple. Los usuarios de jQuery se sentirán como en casa.
  • Cheerio es rápido en comparación con Puppeteer.

Puppeteer

  • Puppeteer es una herramienta de automatización del navegador. Tiene acceso a todo el motor del navegador (normalmente Chromium).
  • Esto la hace una opción más versátil, comparada con Cheerio.
  • Puede ejecutar JavaScript, por lo que es capaz de raspar páginas dinámicas como aplicaciones de una sola página (SPA).
  • Puppeteer puede interactuar con sitios web, lo que significa que se puede utilizar para hacer clic en botones, escribir en formularios de inicio de sesión, etc.
  • Tiene una curva de aprendizaje muy inclinada, ya que tiene más funcionalidades y a menudo requiere el uso de código asíncrono (es decir, promises/async await).
  • Puppeteer es lento en comparación con Cheerio.

Construir un raspador web con Cheerio

Primero, creemos una carpeta llamada scraper para nuestro código. Dentro de scraper, ejecute npm init -y o yarn init -y, según haya optado por usar npm o yarn.  

Ahora que tenemos nuestra carpeta lista y package.json inicializado, vamos a instalar nuestros paquetes.  

Nota: Puedes consultar nuestra guía principal de raspado de datos web en node.js que incluye el uso de Cheerio y Axios para raspado de datos web en más detalle.  

Paso 1 – Instalación de Cheerio

Para instalar Cheerio, ejecute el siguiente comando en su terminal:

// using npm
npm install cheerio

// or using yarn
yarn add cheerio

Paso 2 – Instalación de Axios

Axios es una popular biblioteca para hacer solicitudes HTTP en Node.js. Puede usarse para hacer llamadas API, obtener datos de sitios web, y más.

Para instalarla, ejecute el siguiente comando en su terminal:

// using npm
npm install axios

// or using yarn
yarn add axios

Usamos Axios para hacer peticiones HTTP al sitio web en el que queremos raspar datos. La respuesta que obtenemos del sitio web es en forma de HTML, que luego podemos analizar y extraer la información que necesitamos usando Cheerio.

Paso 3 – Preparando nuestro Scraper

Vayamos a la carpeta de nuestro scraper y creemos un archivo llamado cheerio.js.  

Esta es la estructura básica del código para comenzar con el raspado web usando Cheerio y 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);
 });

En el código anterior, primero necesitamos las bibliotecas Axios y Cheerio.

Paso 4 – Solicitar los datos

A continuación, realizamos una petición get() a “https://plainenglish.io/blog”. Dado que Axios es asíncrono, encadenamos nuestra función get() con then().  

Inicializamos una matriz de enlaces vacía para capturar los enlaces que planeamos raspar.

Luego pasamos el response.data de Axios a Cheerio con:  

// 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";

Paso 5 – Procesamiento de los datos

A continuación, recorremos cada elemento coincidente, buscamos la etiqueta <a> y obtenemos el valor de la propiedad href. Para cada coincidencia, lo empujamos a nuestra matriz de enlaces:  

// 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);
 }
});

Después, devolvemos los enlaces, encadenamos otro then() y console.log nuestra respuesta.  

Paso 6 – Resultados finales

Por último, si abrimos una terminal dentro de nuestra carpeta scraper, podemos ejecutar node.js cheerio.js. Esto ejecutará todo el código de nuestro archivo cheerio.js. Debería ver las URL de nuestra matriz de enlaces en la consola. Se verá algo como esto:  

 '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

Y así de fácil, ¡hemos conseguido raspar datos en el sitio web In Plain English!

A partir de aquí, podemos ir un paso más allá y guardar los datos en un archivo, en lugar de simplemente enviarlos a la consola.

Cheerio y Axios facilitan el raspado web en Node.js. Con sólo unas pocas líneas de código, puede extraer datos de sitios web y utilizarlos para diversos fines.  

Construyendo un Web Scraper con Puppeteer

Vayamos a nuestra carpeta scraper y creemos un archivo llamado puppeteer.js. Ya hemos inicializado nuestro package.json, pero si se ha saltado esta sección, siga adelante e inicialice ese archivo ahora.  

Una vez inicializado, vamos a instalar Puppeteer.

Paso 1 – Instalando Puppeteer

Para instalar Puppeteer, ejecute cualquiera de los siguientes comandos:

// using npm
npm install puppeteer

// or using yarn
yarn add puppeteer

Paso 2 – Preparando nuestro Scraper

Vayamos a nuestra carpeta scraper y creemos un archivo llamado puppeteer.js.  

Aquí está la estructura básica del código para empezar con el raspado de datos web usando 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();
})();

En el código anterior, primero requerimos la librería Puppeteer.

Paso 3 – Creación de una IIFE

A continuación, creamos una expresión de función inmediatamente invocada (IIFE). Como todo en Puppeteer es asíncrono, ponemos async al principio. En otras palabras, tenemos esto:  

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

Dentro de nuestra IIFE asíncrona, creamos un array de enlaces vacío, que usaremos para capturar los enlaces del blog que estamos raspando.

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

A continuación, lanzamos Puppeteer, abrimos una nueva página, navegamos a una URL, y establecemos el viewport de la página (el tamaño de la pantalla).

 // 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 defecto, Puppeteer se ejecuta en “modo headless” (sin interfaz gráfica). Esto significa que no abre un navegador que se pueda ver visualmente. Sin embargo, debemos establecer un tamaño de ventana ya que queremos que Puppeteer navegue el sitio a una cierta anchura y altura.

Nota: si decide que desea ver lo que Puppeteer está haciendo en tiempo real, puede pasar la opción headless: false como un parámetro, así:  

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

Paso 4 – Solicitar los datos

A partir de aquí, elegimos el selector al que queremos dirigirnos, en nuestro caso:

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

Y ejecutamos lo que equivale a querySelectorAll() para nuestro elemento objetivo:  

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

Nota: $$ no es lo mismo que querySelectorAll, así que no espere tener acceso a todas las mismas cosas.  

Paso 5 – Procesamiento de los datos

Ahora que tenemos nuestros elementos almacenados dentro de elementos, mapeamos cada elemento para extraer la propiedad 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))
);

En nuestro caso de uso específico, tenemos el.children[0], ya que sé que el primer elemento hijo de nuestro elemento de destino es una etiqueta a, y es la etiqueta a de la que requiero el valor.  

A continuación, hacemos un bucle a través de cada elemento mapeado y empujamos el valor en nuestra matriz de enlaces, así:

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
 });
}

Por último, console.log los enlaces, y luego cerrar el navegador:

console.log(links);

await browser.close();

Nota: si no cierra el navegador, permanecerá abierto y su terminal se colgará.  

Paso 6 – Resultados finales

Ahora, si abrimos un terminal desde dentro de nuestra carpeta scraper, podemos ejecutar node.js puppeteer.js. Esto ejecutará todo el código de nuestro archivo puppeteer.js. Usted debe ver las direcciones URL de nuestra matriz de enlaces de salida a la consola. Se verá algo como esto:  

'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

Y así de fácil, ¡hemos conseguido raspar datos en la web con Puppeteer!

Puppeteer es una potente herramienta para raspado de datos web y automatización de tareas del navegador. Proporciona una rica API para el raspado de datos web y la automatización de tareas del navegador. Puede utilizarlo para extraer información de sitios web, generar capturas de pantalla y archivos PDF, y realizar muchas otras tareas.

Si quiere usar Puppeteer para raspar datos en sitios web importantes, debería considerar integrar Puppeteer con un proxy, para evitar ser bloqueado.  

Nota: existen otras alternativas a Puppeteer, como Selenium, o el IDE Web Scraper. O si quiere ahorrar tiempo, puede saltarse todo el proceso de raspado de datos web buscando conjuntos de datos ya creados.  

Conclusión

Si lo que busca es raspar páginas estáticas sin necesidad de interacciones como clics y envíos de formularios (o cualquier tipo de manejo de eventos), Cheerio es la mejor opción.

Sin embargo, si el sitio web depende de JavaScript para la inyección de contenido, o si necesita manejar eventos, Puppeteer es necesario.

Cualquiera que sea el enfoque que decida, vale la pena señalar que este caso de uso específico era bastante simple. Si intenta hacer scraping de algo más complejo, como un sitio web dinámico (YouTube, Twitter o Facebook, por ejemplo), es posible que se encuentre en aguas bastante profundas.  

Si desea raspar sitios web y no quiere perder semanas intentando crear una solución, quizá le convenga más optar por una solución estándar como el IDE de Web Scraper de Bright Data.  

El IDE de Bright Data incluye funciones de raspado prediseñadas, una sofisticada infraestructura proxy de desbloqueo incorporada, secuencias de comandos de navegador en JavaScript, depuración y varias plantillas de raspado listas para usar para sitios web populares.