Web scraping con javascript

28-04-2024
web-scraping-con-javascript

¿Que es web scraping?

Es el proceso por el cual extraemos datos de paginas web utilizando scripts o bots que realizan requests y procesan los datos obtenidos

Contenido

Veamos juntos en este posteo la extracción de los datos de una pagina, el procesamiento de estos y grabar los mismos en 4 diferentes formatos de archivos:

  • Texto
  • CSV
  • JSON
  • Excel

Librerias

Vamos a necesitar tener instaladas las siguientes librerías:

  • fetch
  • cheerio - libreria para analizar documentos html y xml
  • xlsx

Para esto en el directorio que contendrá nuestro codigo abrimos una terminal y ejecutamos los siguientes comandos.

Los comandos los estoy realizando en una terminal de powershell


npm init -y

npm install node-fetch cheerio xlsx

📝 Para el uso de las librerias estoy utilizando modules no commonJS
por lo que se debe agregar al archivo package.json debajo de main lo siguiente: "type": "module",

Los archivos script deben tener la extensión mjs

Fuente de datos

Para el ejemplo estoy utilizando los datos de una aplicación que cree para que puedan descargar y hackear 😉.

Cómo correr la aplicación se encuentra en el readme, les dejo el link [app_for_web_scrapping]https://github.com/AlopexMM/app_for_testing/archive/refs/tags/v1.0.0.zip)

Codigo

Procesamiento de la página

La página de la cual vamos a obtener los datos contiene una tabla como esta:

Fecha Venta Compra
2023-01-02 346.0 342.0
2023-01-03 354.0 350.0
2023-01-04 354.0 350.0
2023-01-05 353.0 349.0

Primero obtengamos los datos de la página y procesamos los datos obtenidos con cheerio

import * as cheerio from "cheerio";
import fetch from "node-fetch";
import fs from "node:fs";
import * as XLSX from "xlsx/xlsx.mjs";

const url = "http://127.0.0.1:3001/dollar-history";

// Realizamos el request utilizando fetch
const res = await fetch(url);
// Obtenemos el codigo html del request
const html = await res.text();
// Con el codigo html obtenido lo procesamos utilizando la libreria cheerio
const $ = cheerio.load(html);

En esta parte del código utilizamos fetch para obtener el código HTML y se lo pasamos como parametro a cheerio.load()

Procesamiento de los datos

Los archivos donde se suelen guardar los datos son csv, excel o json.
Tambien podria ocurrir que los mismos se guarden en bases de datos

Veamos cómo resolvemos esto con Javascript utilizando selectores para obtener los datos de la tabla desde el objeto cheerio creado

CSV

/**
 * Procesamos los datos utilizando selectores para el
 * thead y el tbody para colocarlos en un
 * archivo CSV
 **/

// Procesamos el thead
let stringData = "";

$("body > section > div > table > thead > tr")
  .children()
  .each((i, el) => {
    if (i === 2) {
      stringData += `${$(el).text()}\n`;
    } else {
      stringData += `${$(el).text()};`;
    }
  });
// Procesamos el tbody
$("body > section > div > table > tbody ")
  .children()
  .each((i, tr) => {
    // Obtenemos los datos de cada linea
    $(tr)
      .find("td")
      .each((i, td) => {
        if (i === 2) {
          stringData += `${$(td).text().replace("$", "")}\n`;
        } else {
          stringData += `${$(td).text().replace("$", "")};`;
        }
      });
  });

// Grabamo los datos
fs.writeFile("data.csv", stringData, (err) => {
  console.error(err);
});

En esta porción de codigo utilizamos selectores css en este caso el combinador de hijo par encontrar y obtener los datos del HTML que se encuentra dentro de la variable $.

Cheerio ofrece una serie de metodos que nos permiten movernos dentro del HTML.

En este caso el primer metodo utilizado fue parentesis luego de $ y dentro un string conteniendo un selector css esto nos devuelve el elemento HTML que estamos buscando.

El segundo metodo fue children(), el mismo devuelve el elemento HTML que sigue a lo seleccionado con el selector en este caso fue el elemento tr, como este dentro tiene varios elementos th en el header y td en el body de la tabla para recorrer cada uno de ellos utilizamos el metodo each().

each() recibe un callback function que tiene el argumento i que contiene el numero del indice y el argumento el que representa el elemento seleccionado.

📝 En el código en lugar de usar el utilizo el nombre del elemento con el que se esta trabajando, por ejemplo: td

Dentro del callback function obtenemos el texto del elemento con el metodo text(), para que este funcione correctamente tenemos que acceder al elemento utilizando la sintaxis $(el), cada texto obtenido le agregamos ; al final o enter al final del texto y lo sumamos a la variable stringData

Una vez obtenido todo el texto utilizamos la libreria fs con el metodo writeFile() para escribir los datos dentro del archivo data.csv

JSON

/**
 * Procesamos los datos utilizando selectores para el
 * thead y el tbody para colocarlos en un
 * archivo JSON
 **/

// Va a contener todos los objetos que representan cada linea en la tabla
const jsonData = [];
// Contiene los nombres de las columnas
const header = [];

// Procesamos el contenido de header
$("body > section > div > table > thead > tr")
  .children()
  .each((i, td) => {
    header[i] = $(td).text();
  });
// Procesamos el contenido de jsonData
$("body > section > div > table > tbody ")
  .children()
  .each((i, tr) => {
    // Obtenemos los datos de cada linea
    const data = {};
    $(tr)
      .find("td")
      .each((i, td) => {
        data[header[i]] = $(td).text().replace("$ ", "");
      });
    jsonData.push(data);
  });

fs.writeFile("data.json", JSON.stringify(jsonData), (err) => {
  console.error(err);
});

En el caso de JSON a diferencia de CSV vamos a ver que se guarda la información en una lista y al momento de grabarlo en el archivo utilizamos JSON.stringify() para transformar la lista con los objetos JSON en un string

Si abrimos el archivo veremos algo como lo siguiente:

[
  {
    "Fecha": "2023-01-02",
    "Venta": "346.0",
    "Compra": "342.0"
  },
  {
    "Fecha": "2023-01-03",
    "Venta": "354.0",
    "Compra": "350.0"
  }
]

Excel

/**
 * Para excel utilizaremos los datos que se encuentran en
 * jsonData
 **/

// Creamos una hoja y le pasamos los datos recolectados
const worksheet = XLSX.utils.json_to_sheet(jsonData);
// Creamos el libro
const workbook = XLSX.utils.book_new();
// Agregamos la hoja al libre y le damos un nombre
XLSX.utils.book_append_sheet(workbook, worksheet, "Dolar 2023");
// Grabamos todo en un archivo
const buff = XLSX.write(workbook, { type: "buffer", bookType: "xlsx" });
fs.writeFileSync("data.xlsx", buff);

En este caso se reutilizo los datos recolectados en jsonData para crear la hoja.

Luego se agrego los datos al workbook y por ultimo se grabo el archivo.

Para esto fue necesario crear un buffer con XLSX.write() y luego utilizar fs.writeFileSync().

Conclusión

Vimos un breve procesamiento de los datos obtenidos de una página y como grabar los mismos en tres formatos distintos.

Recuerden revisar los distintos tags con el inspeccionador del explorador.