Convierte una WEB en API

Seguimos creando API's en esté serie donde quiero enseñarte todo lo que he aprendido dandome trastazos. En este articulo vamos a subir un poquitín el nivel y vamos a crear una api a una web cualquiera.

A veces necesitamos consumir datos de alguna web de interés pero que no tiene API ni servicio para compartir datos o simplemente la api es muy complicada pues la forma más fácil es crearte tu propia API.

La solución se basa en hacerle un scraping al HTML de la página para buscar los datos de interés y conformar un json de respuesta. El tema del scraping es todo un mundo, lo mejor que conozco es scrapy, una bestia tragando datos pero eso es otro nivel.

1. Preparación

Voy a tomar como base la función de la gaceta que utilizamos en cubablog.net y vamos a hacerle alguna variaciones. Lo primero es que necesitamos instalar par de librarias:

  • cheerio que es una implementación del core de jquery pero en el servidor, es decir, en nodejs. que nos permite seleccionar elementos de una página web usando selectores css o xpath.
  • got una librería para hacer request http en nodejs que es una belleza, también puedes probar con otras librerías como el popular axios , node-fetch o el propio http que forma parte del core de nodejs pero a mi me gusta got, cuestión de gustos.

Instalamos y etsamos listos:

npm i cheerio got

2. Back. API

Lo que haremos es:

  1. Abrir la página gacetaoficial.gob.cu
  2. En el código html buscamos los datos de la ultima gaceta publicada incluyendo la url de los detalles
  3. Abrimos la página de los detalles con la url que buscamos en el segundo paso
  4. Copiamos los detalles de la gaceta.

Este es el código de nuestra API para la gaceta. Lee los comentarios para los detalles.

var got = require('got')
var cheerio = require('cheerio')

module.exports = async (req, res) => {

  try {

    let url = 'https://www.gacetaoficial.gob.cu'
    let response, body, $

    // ABRIR LA PÁGINA DE LA GACETA DE CUBA Y GURADR EL HTML
    body = await got( url ).text();
    $ = cheerio.load( body );

    // BUSCAR LOS DATOS DE LA GACETA
    const link = url + $('.views-field-view-node a').attr('href')
    const file = $('.views-field-field-fichero-gaceta a').attr('href')
    const type = $('.views-field-field-tipo-edicion-gaceta .field-content').first().text()
    const number = $('.views-field-field-numero-de-gaceta .field-content').first().text()
    const date = new Date($('.views-field-field-fecha-gaceta .date-display-single').first().text())
    const title = `Gaceta ${type} No.${number}`

    // ABRIR LA PÁGINA DE LOS DETALLES DE LA GACETA
    body = await got( link ).text();
    $ = cheerio.load( body );

    // BUSCAR LOS ITEMS DE LA GACETA
    const items = $('.node-norma-juridica').map( ( i, el ) => ({
      title: $('.title',el).text(),
      content: $('.field-type-text-with-summary p',el).text(),
    })).get()

    res.json({ title, link, file, items });

  } catch (err) {

    console.log(err)
    res.status(500).json(err);

  }
}

Fíjense como buscamos los datos los datos que necesitamos por ejemplo el enlace de la gaceta utiliza un selector .views-field-view-node a

const link = url + $('.views-field-view-node a').attr('href')

Para buscar los selectores CSS tienes que abrir la página en el browser y abrir los DevTools con F12 y utilizar el selector de elementos.

selctor gaceta.jpg

Todo claro? Buscas selectores en la consola y codificando con cheerio y vas armando el muñeco, puedes seleccionar todos los datos que necesites para armar tu json. En nuestro caso { title, link, file, items }

En este punto ya puedes ejecutar vercel dev y podrás consultar la api en localhost:3000/api/gaceta.json y revisar el demo

3. Front. HTML / Alpine.js

Para el front mantenemos el mismo modelo de html con [alpine.js] (alpinejs.dev) que vimos en el articulo anterior.

En el index.html en la raiz del proyecto le agregamos el componente siguiente:

    <div 
        x-data="{ gaceta: false }"
        x-init="gaceta = await ( await fetch('/api/gaceta.json') ).json()"
    >
        <p>

        </p>
        <!-- X-SHOW muestra el elemento si se cumple la condición -->
        <article x-show="gaceta">
            <!-- :href con : indicamos que el valor del attributo es una variable-->
            <!-- x-text define el contenido del elemento -->
            <h4 >
                <a :href="gaceta.link" x-text="gaceta.title"></a>
            </h4>
            <p>
                <a :href="gaceta.file">Descargar &darr;</a>
            </p>
            <ul >
                <!-- x-for recorrer los items en gaceta. x-for Siempre hay definirlo con la etiqueta template -->
                <template x-for="item in gaceta.items">
                    <li >
                        <strong x-text="item.title"></strong>
                        <p x-text="item.content"></p>
                    </li>
                </template>
            </ul>
        </article>

    </div>

Vieron que sabrosura, me erizo, no voy ni a explicarlo. Solo fijense en el atributo x-init de alpine que es un código que se ejecuta en la fase inicialización. Entonces cuando la página se carga lo primero que hace nuestro componente es llamar a la api que hemos creado anteriormente y guardamos su valor en gaceta para luego mostrarlo.

Puedes ir a localhost:3000 y actualizar la página para ver los resultados.

#TIP: Apóyate en el DevTool para ver como funciona la llamada a la API

devtool networks.jpg

4. ...Y una cosa más

Incluí otra función/api para ofertas.cu puedes verla en corriendo en localhost:3000/api/ofertas.cu revísa el códgo y juega poco con ella. No es la mejor solución pero da una muestra de lo que se queremos y lo que se puede hacer

Conclusiones

Puedes revisar el código en github anímate y creale una api a una web y compártela en twitter conmigo estudia, prueba, equivocate y aprende.

Cualquier error de código, gramatical u ortográfico me lo comentas para corregirlo y asi ganamso todos.

En próximo artículo vamos a hacer una api que nos permita buscar en ofertas.cu y mostrar los resultados. Interesante!!