Volver al blog
Guías
Raluca PenciucLast updated on Mar 31, 20268 min read

Extracción de datos con Cheerio: cómo recopilar datos fácilmente de páginas web

Extracción de datos con Cheerio: cómo recopilar datos fácilmente de páginas web

Atrás quedaron los días en los que había que recopilar y procesar manualmente los datos necesarios para poner en marcha los proyectos. Ya se trate de una página web de comercio electrónico o de un algoritmo de generación de clientes potenciales, una cosa es segura: el proceso de recopilación de datos resultaba tedioso y llevaba mucho tiempo.

En este artículo, descubrirás cómo Cheerio puede ayudarte con sus amplias funciones de análisis de lenguajes de marcado, primero con algunos ejemplos sencillos y luego con un caso de uso real.

Introducción a Cheerio

«Pero, ¿qué es Cheerio?», te preguntarás. Bueno, para intentar aclarar un error común, empezaré por lo que Cheerio no es: un navegador.

La confusión puede deberse al hecho de que Cheerio analiza documentos escritos en un lenguaje de marcado y luego ofrece una API para ayudarte a manipular la estructura de datos resultante. Pero, a diferencia de un navegador, Cheerio no renderiza visualmente el documento, no carga archivos CSS ni ejecuta JavaScript.

Así que, básicamente, lo que hace Cheerio es recibir una entrada HTML o XML, analizar la cadena y devolver la API. Esto lo hace increíblemente rápido y fácil de usar, de ahí su popularidad entre los desarrolladores de Node.js.

Configuración del entorno

Ahora, veamos algunos ejemplos prácticos de lo que Cheerio puede hacer. Lo primero es lo primero: debes asegurarte de que tu entorno está completamente configurado.

No hace falta decir que debes tener Node.js instalado en tu equipo. Si no es así, simplemente sigue las instrucciones de su página web oficial, según tu sistema operativo.

Asegúrate de descargar la versión de soporte a largo plazo (LTS) y no te olvides del gestor de paquetes de Node.js (NPM). Puedes ejecutar estos comandos para asegurarte de que la instalación se ha realizado correctamente:

node -v
npm -v

El resultado debería ser similar a este:

Ahora, en cuanto al debate sobre el IDE: para este tutorial, utilizaré Visual Studio Code, ya que es bastante flexible y fácil de usar, pero puedes utilizar cualquier IDE que prefieras.

Simplemente crea una carpeta donde guardar tu pequeño proyecto y abre un terminal. Ejecuta el siguiente comando para configurar un proyecto de Node.js:

npm init -y

Esto creará una versión predeterminada del archivo package.json, que se puede modificar en cualquier momento.

Siguiente paso: instalaré TypeScript junto con las definiciones de tipos para Node.js:

npm install typescript @types/node -save-dev

He elegido TypeScript en este tutorial por su tipado estático opcional para objetos JavaScript, lo que hace que el código sea más a prueba de fallos en lo que respecta a los errores de tipado. 

Esta es la misma ventaja que ha aumentado constantemente su popularidad entre la comunidad de JavaScript, según una encuesta reciente de CircleCI sobre los lenguajes de programación más populares.

Para verificar la correcta instalación del comando anterior, puedes ejecutar:

npx tsc --version

Ahora crearé el archivo de configuración tsconfig.json en la raíz del directorio del proyecto, que debería definir las opciones del compilador. Si quieres comprender mejor este archivo y sus propiedades, la documentación oficial de TypeScript te será de gran ayuda. 

Si no es así, simplemente copia y pega lo siguiente:

{
    "compilerOptions": {
        "module": "commonjs",
        "esModuleInterop": true,
        "target": "es6",
        "moduleResolution": "node",
        "sourceMap": true,
        "outDir": "dist"
    },
    "lib": ["es2015"]
}

¡Ya casi está! Ahora tienes que instalar Cheerio (obviamente):

npm install cheerio

Por último, pero no menos importante, crea el directorio src, que contendrá los archivos de código. Y hablando del archivo de código, crea y coloca el archivo index.ts en el directorio src.

¿Cómo funciona Cheerio?

¡Perfecto! Ahora ya puedes empezar.

Por el momento, ilustraré algunas características básicas de Cheerio utilizando un documento HTML estático. Simplemente copia y pega el contenido siguiente en un nuevo archivo static.html dentro de tu proyecto:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Page Name - Static HTML Example</title>
</head>
<body>
    <div class="page-heading">
        <h1>Page Heading</h1>
    </div>
    <div class="page-container">
        <div class="page-content">
            <ul>
                <li>
                    <a href="#">Item 1</a>
                      <p class="price">$100</p>
                      <p class="stock">12</p>
                </li>
                <li>
                    <a href="#">Item 2</a>
                    <p class="price">$200</p>
                    <p class="stock">422</p>
                </li>
                <li>
                    <a href="#">Item 3</a>
                    <p class="price">$150</p>
                    <p class="stock">5</p>
                </li>
            </ul>
        </div>
    </div>
    <footer class="page-footer">
        <p>Last Updated: Friday, September 23, 2022</p>
    </footer>
</body>
</html>

A continuación, tienes que enviar el archivo HTML como entrada a Cheerio, que a continuación devolverá la API resultante:

import fs from 'fs'
import * as cheerio from 'cheerio'

const staticHTML = fs.readFileSync('static.html')
const $ = cheerio.load(staticHTML)

Si recibes un error en este paso, asegúrate de que el archivo de entrada contenga un documento HTML válido, ya que a partir de la versión 1.0.0 de Cheerio también se verifica este criterio.

Ahora puedes empezar a experimentar con lo que Cheerio tiene para ofrecer. El paquete NPM es conocido por su sintaxis similar a jQuery y el uso de selectores CSS para extraer los nodos que buscas. Puedes consultar su documentación oficial para hacerte una idea más clara.

Supongamos que quieres extraer el título de la página:

const title = $('title').text()
console.log("Static HTML page title:", title)

Deberíamos probar esto, ¿no? Estás usando TypeScript, así que tienes que compilar el código, lo que creará el directorio dist, y luego ejecutar el archivo index.js asociado. Para simplificar, definiré el siguiente script en el archivo package.json:

"scripts": {
    "test": "npx tsc && node dist/index.js",
}

De esta forma, lo único que tengo que hacer es ejecutar:

npm run test

y el script se encargará de los dos pasos que acabo de describir.

De acuerdo, pero ¿qué pasa si el selector coincide con más de un elemento HTML? Intentemos extraer el nombre y el valor de cotización de los elementos que aparecen en la lista desordenada:

const itemStocks = {}
$('li').each((index, element) => {
    const name = $(element).find('a').text()
    const stock = $(element).find('p.stock').text()
    itemStocks[name] = stock
})
console.log("All items stock:", itemStocks)

Ahora ejecuta de nuevo el script de acceso directo y el resultado en tu terminal debería ser similar a este:

Static HTML page title: Page Name - Static HTML Example
All items stock: { 'Item 1': '12', 'Item 2': '422', 'Item 3': '5' }

Casos de uso de Cheerio

Bueno, eso era básicamente la punta del iceberg. Cheerio también es capaz de analizar documentos XML, extraer el estilo de los elementos HTML e incluso modificar los atributos de los nodos.

Pero, ¿cómo puede Cheerio ayudar en un caso de uso real?

Supongamos que queremos recopilar algunos datos para entrenar un modelo de aprendizaje automático para un proyecto futuro más grande. Normalmente, buscarías en Google algunos archivos de entrenamiento y los descargarías, o utilizarías la API del sitio web.

Pero, ¿qué haces cuando no encuentras archivos relevantes o el sitio web que estás consultando no ofrece una API, tiene una limitación de velocidad sobre los datos o no ofrece todos los datos que ves en una página?

Pues bien, aquí es donde el web scraping resulta útil. Si tienes curiosidad por conocer más casos de uso prácticos del web scraping, puedes echar un vistazo a este artículo tan bien escrito de nuestro blog.

Volviendo al tema que nos ocupa, por el bien del ejemplo, supongamos que nos encontramos precisamente en esta situación: queremos datos, y no hay forma de encontrarlos. Recuerda que Cheerio no se encarga de la extracción de HTML, ni de la carga de CSS, ni de la ejecución de JS.

Por lo tanto, en nuestro tutorial, voy a utilizar Puppeteer para navegar hasta el sitio web, capturar el HTML y guardarlo en un archivo. A continuación, repetiré el proceso de la sección anterior.

Para ser más específico, quiero recopilar algunas opiniones públicas de Reddit sobre un popular módulo de batería y centralizar los datos en un único archivo que se alimentará posteriormente a un posible modelo de entrenamiento de ML. Lo que suceda a continuación también puede variar: análisis de sentimiento, investigación de mercado y la lista podría seguir.

Solicitud del HTML

Veamos cómo se traducirá este caso de uso en código. En primer lugar, debes instalar el paquete NPM de Puppeteer:

npm install puppeteer

También crearé un nuevo archivo reddit.ts, para mantener el proyecto más organizado, y definiré un nuevo script en el archivo package.json:

"scripts": {
    "test": "npx tsc && node dist/index.js",
    "parse": "npx tsc && node dist/reddit.js"
},

Para obtener el documento HTML, definiré una función con este aspecto:

import fs from 'fs'
import puppeteer from 'puppeteer'
import * as cheerio from 'cheerio'

async function getContent(url: string): Promise<void> {

    // Open the browser and a new tab
    const browser = await puppeteer.launch()
    const page = await browser.newPage()

    // Navigate to the URL and write the content to file
    await page.goto(url)
    const pageContent = await page.content()
    fs.writeFileSync("reddit.html", pageContent)

    // Close the browser
    await browser.close()
    console.log("Got the HTML. Check the reddit.html file.")
}

Para probarlo rápidamente, añade un punto de entrada en tu código y llama a la función:

async function main() {


    const targetURL = 'https://old.reddit.com/r/Drumming/comments/r3tidc/yamaha_ead10/'
    await getContent(targetURL)
}

main()
    .then(() => {console.log("All done!")})
    .catch(e => {console.log("Unexpected error occurred:", e.message)})

El archivo reddit.html debería aparecer en el árbol de tu proyecto, y contendrá el documento HTML que queremos.

¿Dónde están mis nodos?

Ahora, una parte un poco más complicada: tienes que identificar los nodos que nos interesan para nuestro caso de uso. Vuelve a tu navegador (el de verdad) y navega hasta la URL de destino. Coloca el cursor del ratón sobre la sección de comentarios, haz clic con el botón derecho y luego elige la opción «Inspeccionar».

Se abrirá la pestaña Herramientas de desarrollador, que te mostrará exactamente el mismo documento HTML que guardaste anteriormente en tu equipo.

Para extraer solo los comentarios, tienes que identificar los selectores exclusivos de esta sección de la página. Puedes observar que toda la lista de comentarios se encuentra dentro de un contenedor div con una clase sitetable nestedlisting.

Profundizando más, cada comentario individual tiene un elemento «form» como padre, con la clase «usertext warn-on-unload». A continuación, en la parte inferior, puedes ver que el texto de cada comentario está dividido entre varios elementos «p».

Análisis y guardado de los datos

Veamos cómo funciona esto en el código:

function parseComments(): void {

    // Load the HTML document
    const staticHTML = fs.readFileSync('reddit.html')
    const $ = cheerio.load(staticHTML)

    // Get the comments section
    const commentsSection = $('div.sitetable.nestedlisting')

    // Iterate each comment
    const comments = []


    $(commentsSection).find('form.usertext.warn-on-unload').each((index, comment) => {
        let commentText = ""

          // Iterate each comment section and concatenate them
          $(comment).find('p').each((index, piece) => {
            commentText += $(piece).text() + '\n'
          })

          comments.push(commentText)
    })

    // Write the results to external file
    fs.writeFileSync("comments.json", JSON.stringify({comments}))
}

Muy bien, y ahora actualicemos el punto de entrada con la función recién definida y veamos cómo funciona este código en conjunto:

async function main() {

    const targetURL = 'https://old.reddit.com/r/Drumming/comments/r3tidc/yamaha_ead10/'
    await getContent(targetURL)
    parseComments()
}

main()
    .then(() => {console.log("All done. Check the comments.csv file.")})
    .catch(e => {console.log("Unexpected error occurred:", e.message)})

Ejecuta el código con el script definido anteriormente:

npm run parse

El navegador sin interfaz gráfica tardará entre 5 y 10 segundos en abrirse y navegar hasta nuestra URL de destino. Si tienes más curiosidad al respecto, puedes añadir marcas de tiempo al principio y al final de cada una de nuestras funciones para ver realmente lo rápido que es Cheerio.

El archivo comments.json debería ser nuestro resultado final:

Este caso de uso se puede ampliar fácilmente para analizar el número de votos positivos y negativos de cada comentario, o para obtener las respuestas anidadas de los comentarios. Las posibilidades son infinitas.

Conclusión

Gracias por llegar hasta el final de este tutorial. Espero que hayas comprendido lo indispensable que es Cheerio para el proceso de extracción de datos y cómo integrarlo rápidamente en tu próximo proyecto de scraping.

También utilizamos Cheerio en nuestro producto, WebScrapingAPI. Si alguna vez te ves envuelto en los numerosos retos que plantea el scraping web (bloqueos de IP, detección de bots, etc.), plantéate probarlo.

Acerca del autor
Raluca Penciuc, Desarrollador full-stack @ WebScrapingAPI
Raluca PenciucDesarrollador full-stack

Raluca Penciuc es desarrolladora full stack en WebScrapingAPI, donde se dedica a crear rastreadores, mejorar las técnicas de evasión y buscar formas fiables de reducir la detección en los sitios web de destino.

Empieza a crear

¿Estás listo para ampliar tu recopilación de datos?

Únete a más de 2000 empresas que utilizan WebScrapingAPI para extraer datos de la web a escala empresarial sin ningún gasto de infraestructura.