He trabajado en un producto en el que cada día, reviso gráficos, registros y números de múltiples plataformas para obtener una comprensión completa de si todo está bien.

De media, este proceso me lleva unos 15 minutos al día, pero a veces puede alargarse hasta los 30 minutos. Después de realizar algunos cálculos básicos, me di cuenta de que en el transcurso de un año, equivale a más de 12 días laborables. Es una cantidad considerable de tiempo 12 días laborables al año que dedico a tareas repetitivas. En consecuencia, he decidido abordar esta cuestión y asignar este tiempo a tareas más productivas.

Puppete ¿Cómo agilizar el flujo de trabajo?

Requisitos


Ahora que hemos identificado el problema, vamos a esbozar los requisitos de la solución:

  • Un panel de control de una sola página que muestre toda la información crucial.
  • La capacidad de recopilar datos de múltiples plataformas.
  • Procesar los datos recopilados, mezclarlos si es necesario y extraer información concisa.
  • Visualización inmediata en el cuadro de mandos a modo de aviso y notificación si alguna acción requiere mi atención.
  • Fácil accesibilidad tanto para mí como para mis cofundadores en cualquier dispositivo y desde cualquier lugar para ver este cuadro de mando.

Acerca de mi solución


La solución debe merecer la pena. Por ejemplo, si tengo que invertir una cantidad significativa de tiempo en resolver este problema, podría ser mejor dejarlo como está ahora. Así pues, esforcémonos por encontrar una solución fácil de aplicar. Las soluciones se enumeran de la más fácil a la más difícil:

Notificaciones


En mi caso, la forma más fácil de recibir notificaciones es usar Telegram Bots. Puedo configurarlo rápidamente y empezar a usarlo inmediatamente.

Alojamiento


Para que el dashboard sea accesible en cualquier dispositivo, en cualquier momento y desde cualquier lugar, debería estar alojado en servicios como AWS o IONOS.

Recopilación de datos


Al principio, intenté encontrar servicios existentes que me permitieran recopilar datos fácilmente, pero no encontré nada que se adaptara a mis necesidades. Así que decidí explorar la posibilidad de crear una solución a medida.

Mi planteamiento consistía en recopilar datos de las plataformas que utilizamos a través de sus API, procesar esos datos y presentarlos en un cuadro de mandos. Sin embargo, me encontré con un problema, ya que muchas de estas plataformas no disponían de API para acceder a los datos. Esto me obligó a explorar métodos alternativos, y descubrí que Puppeteer era la opción más adecuada para mis requisitos específicos.

Puppeteer


Puppeteer es una libreria Node.js que proporciona una API de alto nivel para controlar Chrome/Chromium sobre el protocolo DevTools. La mayoría de las cosas que puedes hacer manualmente en el navegador se pueden hacer usando Puppeteer. He aquí algunos ejemplos:

Generar capturas de pantalla y PDFs de páginas.

  • Rastrear una SPA (Single-Page Application) y generar contenido pre-renderizado (i.e. "SSR" (Server-Side Rendering)).
  • Automatice el envío de formularios, las pruebas de interfaz de usuario, la introducción de datos mediante el teclado, etc.
  • Cree un entorno de pruebas automatizado utilizando las últimas funciones de JavaScript y del navegador.
  • Capture una traza cronológica de su sitio para ayudar a diagnosticar problemas de rendimiento.
  • Pruebe las extensiones de Chrome.

Aquí tienes un ejemplo sencillo de Puppeteer en acción, que realiza los siguientes pasos:

  1. Accede a Google.com.
  2. Ejecuta una búsqueda de "HackerNoon".
  3. Haz clic en el primer resultado de la búsqueda.
  4. Haz una captura de pantalla.
import puppeteer from 'puppeteer';

(async (searchValue) => {
  // Launch the browser and open a new blank page
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  // Search
  await page.goto('https://google.com');
  await page.locator('textarea').fill(searchValue);
  await page.$eval('form', form => form.submit());

  // Go to the first link
  await page.waitForNavigation();
  await page.click(`div[data-async-context^="query:"] a`);

  // Take a screenshot
  await page.waitForNavigation();
  await page.screenshot({path: './screenshot.png'});

  await browser.close();

})("HackerNoon");

Validación de la idea


Vamos a validar la idea con una implementación sencilla que se parezca mucho a un escenario real.

Dado que necesitamos recopilar datos de múltiples plataformas, vamos a elegir un escenario más sencillo que se asemeje a esta tarea, como la creación de una página que muestre las principales historias de HackerNoon y los puestos de trabajo en ingeniería de software.

El siguiente código recopila datos de HackerNoon Top Stories y HackerNoon Jobs cada hora, genera contenido HTML simple a partir de estos datos y, a continuación, sirve este contenido HTML cuando recibimos una solicitud HTTP. Es bastante sencillo.

index.js

import http from 'http';
import * as scraper from './scraper.js';

(async () => {
  let scrapedHtml = 'Try again later...';
  http.createServer((req, res) => {
    res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'})
    res.end(scrapedHtml);
  
  }).listen(8080);

  scrapedHtml = await scrapeAll();
  setInterval(async () => scrapedHtml = await scrapeAll(), 60*60*1000);
})();

async function scrapeAll() {
  const browser = await scraper.launchBrowser();
  const [stories, jobs] = await Promise.all([
    scraper.getTopStories(browser),
    scraper.getJobs('Software Engineer', browser)
  ]);
  await browser.close();
  return `
    <h2>Top Stories</h2>
    <ul>${stories.map(e => linkToHtml(e.title, e.url)).join('')}</ul>

    <h2>Jobs</h2>
    <ul>${jobs.map(e => linkToHtml(e.title, e.url)).join('')}</ul>
  `;
}

const linkToHtml = (title, url) => {
  return `<li>
    <a target="_blank" href="${url}">
      ${title}
    </a>
  </li>`;
}

scraper.js

import puppeteer, {Browser} from 'puppeteer';

/**
 * 
 * @returns {Browser}
 */
export async function launchBrowser() {
  return await puppeteer.launch();
}

/**
 * 
 * @param {Browser} browser
 * @returns {[{title: String, url: String}]}
 */
export async function getTopStories(browser) {
  const page = await browser.newPage();
  await page.goto('https://hackernoon.com/tagged/hackernoon-top-story');

  // Wait for articles
  await page.waitForSelector('main .story-card');

  // Get articles
  const res = [];
  const articles = await page.$$('main .story-card h2 a');
  for (const article of articles) {
    res.push(
      await article.evaluate(el => ({
        "title": el.textContent,
        "url": el.href,
      }))
    );
  }
  return res;
}

/**
 * 
 * @param {String} keyword
 * @param {Browser} browser
 * @returns {[{title: String, url: String}]}
 */
export async function getJobs(keyword, browser) {
  const page = await browser.newPage();
  await page.goto('https://jobs.hackernoon.com');

  // Search
  await page.locator('#search-jobkeyword input').fill(keyword);
  await page.click('button[type=submit]');

  // Wait for result
  await page.waitForSelector('.job-list-item');

  // Get jobs
  const res = [];
  const items = await page.$$('.job-list-item');
  for (const item of items) {
    res.push(
      await item.evaluate(el => ({
        "title": [
          el.querySelector('.job-name'),
          ...el.querySelectorAll('.desktop-view span')
        ].map(e => e.textContent).join(', '),
        "url": el.href,
      }))
    );
  }
  return res;
}

El resultado es algo parecido a esto:

Ventajas


Desde mi punto de vista, esta solución ofrece las siguientes ventajas:

  • Aplicación relativamente rápida
  • Un único método de extracción de datos
  • Facilidad de ampliación si necesito añadir una plataforma adicional

Desventajas

Desde mi punto de vista, las desventajas de esta solución son las siguientes

  • Requiere ajustes si cambia la interfaz de usuario de alguna plataforma
  • Puede resultar difícil o imposible extraer datos de determinadas plataformas, en particular de aquellas con medidas de seguridad reforzadas.

Conclusión


Esta solución resultó eficaz para mi situación y me permitió resolver mi problema rápidamente. Si crees que hay un enfoque mejor para resolverlo, me encantaría que compartieras tus ideas.

Puedes acceder al código fuente de este ejemplo en GitHub.

Fuente

Plataforma de cursos gratis sobre programación