Cree una API extraída de la web con Express y Cheerio

C

El crecimiento de la World Wide Web durante las últimas dos décadas ha llevado a que se recopile y pegue una enorme cantidad de datos en las páginas web de Internet. Un corolario de esta producción y distribución hiperbólicas de contenido en la web es la conservación de una gran cantidad de información que se puede utilizar de formas enumerables si se puede extraer y agregar de manera eficaz.

Las formas más comunes de recopilar y agregar datos disponibles en la web son (1) solicitarlo desde una API con tecnologías como DESCANSO o JABÓN y (2) escribir un programa para analizarlo o extraerlo de datos poco estructurados, como HTML. El primero es, con mucho, el método preferible para el programador que consume la información, pero a menudo esto no es una posibilidad debido al tiempo de desarrollo y los recursos necesarios por parte del productor. Por lo tanto, la mayoría de las veces, el único medio disponible para obtener datos valiosos es rasparlos.

Este artículo presentará una técnica para crear una aplicación independiente Node.js que recopila (raspa) datos de impuestos para el estado de Nebraska y presenta esa información al usuario o calcula los impuestos según una ciudad y la cantidad suministrada.

Las tecnologías a utilizar son:

  • Node.js: un tiempo de ejecución de JavaScript basado en el motor V8 de Chrome
  • Express: un marco web de Node.js
  • Cheerio: una biblioteca de análisis HTML que refleja la API familiar de la biblioteca jQuery

El código fuente se puede encontrar en GitHub. aquí.

Configuración del proyecto base

Este tutorial utilizará Node Package Manager (npm) para inicializar el proyecto, instalar bibliotecas y administrar dependencias. Antes de comenzar, asegúrese de haber configurado npm para su entorno.

Inicialice el proyecto aceptando las opciones básicas predeterminadas:

Instalar dependencias:

Estructura básica del proyecto:

Rápido

Nosotros usaremos Rápido para construir nuestra API RESTful para la aplicación de cálculo de impuestos. Express es un marco de trabajo de aplicación web para aplicaciones Node que es flexible en el sentido de que impone pocas restricciones en la forma de desarrollar sus aplicaciones, pero muy poderoso porque proporciona varias características útiles que se utilizan en una multitud de aplicaciones web.

Configuración de Express

En server.js incluiremos un código de configuración Express repetitivo que creará la aplicación Express, luego registraremos el módulo de rutas que haremos en la siguiente subsección. Al final del archivo, le indicaremos a la aplicación Express que escuche el puerto proporcionado o el 3500, que es un puerto codificado.

En server.js copie y pegue el siguiente código:

'use strict';

const express = require('express');
const app = express();
const port = process.env.PORT || 3500;

const routes = require('./api/routes');
routes(app);

app.listen(port);

console.log("Node application running on port " + port);

Rutas

Configuraremos el enrutamiento en nuestra aplicación para responder a las solicitudes realizadas a rutas URI específicas y significativas. ¿A qué me refiero con significativo que pueda estar preguntando? Bueno, en el paradigma REST, las rutas de ruta están diseñadas para exponer recursos dentro de la aplicación de manera autodescriptiva.

En el archivo routes / index.js, copie y pegue el siguiente código:

'use strict';

const taxCtrl = require('../controllers');

module.exports = (app) => {
    app.use(['/calculate/:stateName/:cityName/:amount', '/taxrate/:stateName/:cityName'], (req, res, next) => {
        const state = req.params.stateName;
        if (!taxCtrl.stateUrls.hasOwnProperty(state.toLowerCase())) {
            res.status(404)
                    .send({message: `No state info found for ${state}`});
        } else {
            next();
        }
    });

    app.route('/taxrate/:stateName/:cityName')
        .get(taxCtrl.getTaxRate);

    app.route('/calculate/:stateName/:cityName/:amount')
        .get(taxCtrl.calculateTaxes);
  
    app.use((req, res) => {
        res.status(404)
            .send({url: `sorry friend, but url ${req.originalUrl} is not found`});
    });
}

Las dos rutas que se definen en este módulo son / taxrate /: stateName /: cityName y / calculate /: stateName /: cityName /: amount. Están registrados en el app objeto que se pasó al módulo desde el script server.js descrito anteriormente llamando al método de ruta en el app. Dentro del método de ruta se especifica la ruta y luego el get El método es llamado, o encadenado, en el resultado de llamar a route. Dentro del método get encadenado hay una función de devolución de llamada que analizaremos con más detalle en la sección sobre controladores. Este método de definir rutas se conoce como “encadenamiento de rutas”.

La primera ruta describe un punto final que mostrará las tasas de impuestos estatales y municipales en respuesta a una solicitud GET correspondiente a :stateName y :cityName, respectivamente. En Express, especifica lo que se conoce como “parámetros de ruta” precediendo una sección de una ruta delimitada entre barras diagonales con dos puntos para indicar un marcador de posición para un parámetro de ruta significativo. La segunda ruta / calcular /: stateName /: cityName /: amount describe un punto final que calculará los montos de impuestos de la ciudad y del estado, así como el monto total en función del parámetro de monto de la ruta.

Las otras dos invocaciones del app objeto están especificando middleware. El middleware Express.js es una característica increíblemente útil que tiene muchas aplicaciones que podrían justificar fácilmente su propia serie de artículos, por lo que no voy a profundizar mucho aquí. Solo sepa que el middleware son funciones que pueden conectarse, acceder y modificar la solicitud, la respuesta, el error y los siguientes objetos de un ciclo de solicitud-respuesta Express.

Registra una función de middleware llamando al use método en el app objeto y pasando combinaciones únicas de rutas y funciones de devolución de llamada. El primer middleware declarado en el objeto de la aplicación especifica nuestras dos URL dentro de una matriz y una función de devolución de llamada que verifica si el estado que se pasa para solicitar información fiscal está disponible.

Esta aplicación de demostración solo se desarrollará para responder a solicitudes de ciudades en Nebraska, pero alguien podría extenderla fácilmente con otros estados dado que tienen una página web estática disponible públicamente con información similar. El segundo middleware sirve como captura para todas las rutas de URL solicitadas que no se especifican.

Controladores

Los controladores son la parte de una aplicación Express que maneja las solicitudes reales realizadas a las rutas definidas y devuelve una respuesta. Estrictamente hablando, los controladores no son un requisito para desarrollar aplicaciones Express. Se puede usar una función de devolución de llamada, anónima o de otro tipo, pero el uso de controladores conduce a un código mejor organizado y a una separación de preocupaciones. Dicho esto, usaremos controladores, ya que siempre es una buena idea seguir las mejores prácticas.

En su archivo controllers / index.js, copie y pegue el siguiente código.

'use strict';

const svc = require('../services');

const getTaxRate = (req, res) => {
    const state = req.params.stateName;
    svc.scrapeTaxRates(state, stateUrls[state.toLowerCase()], (rates) => {
        const rate = rates.find(rate => {
            return rate.city.toLowerCase() === req.params.cityName.toLowerCase();
        });
        res.send(rate);
    });
}

const calculateTaxes = (req, res) => {
    const state = req.params.stateName;
    svc.scrapeTaxRates(state, stateUrls[state.toLowerCase()], (rates) => {
        const rate = rates.find(rate => {
            return rate.city.toLowerCase() === req.params.cityName.toLowerCase();
        });
        res.send(rate.calculateTax(parseFloat(req.params.amount)));
    });
}


const stateUrls = {
    nebraska: 'http://www.revenue.nebraska.gov/question/sales.html';
};

module.exports = {
    getTaxRate,
    calculateTaxes,
    stateUrls
};

Lo primero que ve que se importa y declara en el módulo de controladores es una constante llamada svc que es la abreviatura de “servicio”. Este objeto de servicio sirve como una pieza reutilizable de funcionalidad para solicitar una página web y analizar el HTML resultante. Profundizaré más en la sección sobre Cheerio y servicios sobre lo que está sucediendo detrás de escena con este objeto de servicio, pero por ahora solo sepa que analiza el HTML para los bits significativos que nos interesan (es decir, tasas de impuestos).

Las dos funciones que más nos interesan son getTaxRate y calculateTaxes. Ambas funciones se pasan en solicitud y respuesta (req y res) objetos a través del route.get(...) métodos en el módulo de rutas. los getTaxRate función accede al stateName parámetro de ruta del objeto params del objeto de solicitud.

El nombre del estado y su URL de destino correspondiente (en este caso, solo Nebraska y su página web gubernamental que muestra información imponible) se pasan al método del objeto de servicio scrapeTaxRates. Se pasa una función de devolución de llamada como tercer parámetro para filtrar y responder con la información de la ciudad correspondiente al cityName parámetro encontrado en la ruta de la ruta.

La segunda función del controlador, calculateTaxes, nuevamente usa el método de servicio scrapeTaxRates para solicitar y analizar el HTML, pero esta vez calcula los impuestos a través de un método dentro del TaxRate class, que discutiremos a continuación en la sección de modelos.

Modelos

Al igual que los controladores, los modelos no son algo estrictamente necesario para una aplicación Express. Sin embargo, los modelos son bastante útiles cuando queremos encapsular datos (estado) y comportamiento (acciones) dentro de nuestras aplicaciones de manera organizada.

En su archivo modelos / index.js, copie y pegue el siguiente código:

'use strict'

class TaxRate {
    constructor(state, city, localRate, stateRate) {
        this.state = state;
        this.city = city;
        this.localRate = localRate;
        this.stateRate = stateRate;
    }

    calculateTax (subTotal) {
        const localTax = this.localRate * subTotal;
        const stateTax = this.stateRate * subTotal;
        const total = subTotal + localTax + stateTax;
        return {
            localTax,
            stateTax,
            total
        };
    }
}

module.exports = TaxRate;

El único modelo (o más correctamente dicho: clase) que definiremos en nuestra aplicación es TaxRate. TaxRate contiene campos de miembros para almacenar datos sobre el estado, la ciudad, la tasa impositiva local y la tasa impositiva estatal. Estos son los campos de clase que componen el estado del objeto. Solo hay un método de clase, calculateTax(...), que toma el parámetro que representa un monto subtotal pasado a la ruta / calcular /: stateName /: cityName /: amount path y devolverá un objeto que representa las cantidades de impuestos calculadas y el monto total final.

Cheerio

Cheerio es una biblioteca de JavaScript liviana que implementa el núcleo de jQuery para acceder, seleccionar y consultar HTML en aplicaciones del lado del servidor. En nuestro caso, utilizaremos Cheerio para analizar el HTML en la página web estática que solicitamos al sitio web del gobierno de Nebraska que muestra información fiscal.

Servicios

En nuestra pequeña aplicación, usaremos un módulo de servicios personalizados para implementar la solicitud de la página HTML del sitio web del gobierno de Nebraska, así como el análisis del HTML resultante para extraer los datos que deseamos.

En su archivo services / index.js, copie y pegue el siguiente código:

'use strict';

const http = require('http');
const cheerio = require('cheerio');
const TaxRate = require('../models');

const scrapeTaxRates = (state, url, cb) => {
    http.get(url, (res) => {
        let html="";
  
        res.on('data', chunk => {
            html += chunk;
        });
  
        res.on('end', () => {
            const parser = new Parser(state);
            const rates = parser.parse(html);
            cb(rates);
        });
    });
};

class Parser {
    constructor(state) {
        this.state = state;
    }

    parse(html) {
        switch(this.state.toLowerCase()) {
            case 'nebraska':
                return this.parseNebraska(html);
            default:
                return null;
        }
    }

    parseNebraska(html) {
        const $ = cheerio.load(html);
        let rates = [];
        $('tr').each((idx, el) => {
            const cells = $(el).children('td');
            if (cells.length === 5 && !$(el).attr('bgcolor')) {
                const rawData = {
                    city: $(cells[0]).first().text(),
                    cityRate: $(cells[1]).first().text(),
                    totalRate: $(cells[2]).first().text()
                };
                rawData.cityRate = parseFloat(rawData.cityRate.replace('%', ''))/100;
                rawData.totalRate = parseFloat(rawData.totalRate.substr(0, rawData.totalRate.indexOf('%')))/100;
                rawData.stateRate = rawData.totalRate - rawData.cityRate;
                rates.push(new TaxRate('Nebraska', rawData.city, rawData.cityRate, rawData.stateRate));
            }
        });
        return rates;
    }
}

module.exports = {
    scrapeTaxRates;
};

Las primeras tres líneas se están importando (a través de require()) algunos objetos de nivel de módulo http, cheerioy TaxRate. TaxRate se describió en la sección anterior sobre módulos para no vencer al proverbial caballo muerto y repasar su uso con demasiado detalle, por lo que basta con decir que se usa para almacenar datos de tasas impositivas y calcular impuestos.

los http El objeto es un módulo de node que se utiliza para realizar solicitudes desde el servidor a otro recurso en red, que en nuestro caso es la página web de tasa impositiva del gobierno de Nebraska. El restante es Cheerio, que se utiliza para analizar el HTML utilizando la conocida API jQuery.

El módulo de servicios solo expone una función disponible públicamente llamada scrapeTaxRates, que toma una cadena de nombre de estado, una cadena de URL (para la página del estado que muestra las tasas de impuestos) y una función de devolución de llamada para procesar las tasas de impuestos de formas únicas especificadas por el código del cliente que llama.

Dentro del cuerpo del scrapeTaxRates funciona el get método para el http se llama al objeto para solicitar la página web en la URL especificada. La función de devolución de llamada pasó al http.get(...) El método maneja el procesamiento de la respuesta. En el procesamiento de la respuesta, se crea una cadena de HTML y se almacena en una variable llamada html. Esto se hace de forma incremental a medida que data Se activa el evento y la respuesta devuelve una parte de los datos almacenados en búfer.

Tras el disparo del end evento se invoca una función de devolución de llamada final. Dentro de esta devolución de llamada, Parser se crea una instancia de la clase y se llama al método parse para analizar el HTML y extraer la información específica de la estructura y el diseño de la página web de Nebraska. Los datos analizados se cargan en una serie de TaxRate objetos almacenados en una matriz y pasados ​​a la función de devolución de llamada para ejecutar la lógica especificada en el código del cliente que llama (en nuestro caso, en las funciones del controlador descritas anteriormente). Es en este último paso que los datos se serializan y envían como respuesta a la persona que llama de la API REST.

Conclusión

En este breve artículo, investigamos cómo diseñar una aplicación Node.js simple y ligera que extraiga información útil sobre la tasa impositiva de un sitio web del gobierno, lo que podría ser útil para aplicaciones de comercio electrónico. Los dos propósitos principales de la aplicación son recaudar tasas de impuestos y mostrar esa información para una ciudad determinada o calcular los impuestos en función de un estado, una ciudad y un subtotal.

Por ejemplo, a continuación encontrará capturas de pantalla de la aplicación que muestran los impuestos de la ciudad de Omaha y calculan los impuestos para un subtotal de $ 1000. Para probar esta aplicación cd en el directorio raíz y escriba $ node server.js en la consola. Verá un mensaje que dice “Aplicación de node ejecutándose en el puerto 3500”.

Espero que este artículo lo inspire a seguir investigando el mundo del raspado de datos para que pueda crear aplicaciones útiles y productos de datos significativos. Como siempre, doy la bienvenida a todos y cada uno de los comentarios a continuación.

 

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias para su correcto funcionamiento. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad