Mostrando las entradas con la etiqueta Misc. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Misc. Mostrar todas las entradas

domingo, 18 de agosto de 2019

Crear un scraper de productos de Supermercado con Python

Scrapper de Supermercado
Hola!

Hoy traigo algo un poco distinto a lo que venía subiendo que tuve que hacer como tarea previa para un Trabajo Practico de la Universidad (que sigue en desarrollo).

El trabajo consiste en realizar una aplicación web y la nuestra necesita tener los datos de los precios de los productos de los supermercados. Idealmente en el futuro, los supermercados podrán ofrecer una vía directa para que la aplicación consulte los precios o la misma aplicación les dejará subir o actualizar sus precios. Por ahora como la aplicación es nada más una prueba de concepto, basta con tomar los datos de un supermercado y a partir de ellos crear nuevos precios para simular que tenemos los datos de varios supermercados.

La primera idea que se nos ocurrió para obtener los precios de todos los productos fue utilizar algún Framework como Selenium que permite la automatización de un navegador web y agarrar y copiar los valores de los atributos html de la página.

Por ejemplo si un supermercado lista sus productos con una lista de html tendría algo así:

<li id="producto1" ..> 
     <div>
        <p class="nombreprod">Nombre producto</p>
        <p class="precioprod">Precio</p>
        <p class="catprod">Categoría</p>
       ...
     </div>
</li>
<li id="producto2"..>
...
Entonces nosotros podríamos agarrar uno por uno cada elemento de la lista y guardarlo en algún tipo de diccionario de python para luego transformarlo en un archivo .json.
Esto sin embargo lleva mucho tiempo ya que (siguiendo el ejemplo) si no se tiene la clase "nombreprod", no habría una "forma directa" de acceder al nombre del producto entonces tendríamos que buscar otra forma de acceder a ese atributo que podría ser utilizando el path absoluto "body/div/div/div/div/table/..." que con páginas grandes puede llevar mucho tiempo calcular cada path.

Por eso tratamos de evitar esta opción y nos preguntamos si la página para recibir los productos podría utilizar un archivo .json en donde estén todos los datos guardados y arme el html en base a eso.

Por suerte este es el caso de Jumbo y de Dia. Decidimos enfocarnos en Jumbo ya que fue el primero que encontramos.

Si accedemos por ejemplo a este link podemos ver que se está haciendo algún tipo de búsqueda entre los primeros 50 elementos y se retorna el json de cada elemento. En cada item se encuentra el precio, la descripción, url de la imágen, categorías y mucha información más.

Nosotros lo único que necesitábamos por el momento es el precio, nombre, descripción e imagen, tal vez algún atributo más pero el archivo original contiene demasiada información de más que no nos servía.

Entonces el siguiente paso era filtrar solo la información que queríamos y automatizar para que lo haga con todos los productos.

Para eso decidí crear un script en python que se encarga de hacer lo siguiente:
  1. Descargar e interpretar el archivo json de a 50 items (cambiando los valores from y to del url).
  2. Agarrar la información que necesitábamos y colocarla en un diccionario nuevo.
  3. Repetir los pasos 1 y 2 hasta que no hayan más productos disponibles (que el url nos indique un error).
  4. Volcar toda esta información en nuestro propio archivo .json.
 Así de simple en menos de 50 líneas (con comentarios) pude crear el scraper:



Después descubrimos que el supermercado Día también utiliza archivos json para listar sus productos así que se podría adaptar este programa para scrapear también los precios de Día pero ahí nos encontraríamos en un problema: ¿Cómo podemos relacionar 2 productos iguales de distintos supermercados? No tenemos garantía de que un supermercado tenga por convención "NombreProducto-Marca" y otro "Marca-NombreProducto". 

Después descubrimos que por producto existe un código único llamado "ean" para igualarlos así que existe la posibilidad de relacionar dos productos iguales de distintos supermercados pero eso lo dejamos para el futuro.

Esto puede abrir la puerta a cuestiones más interesantes como ir rastreando la variación de precios a lo largo del mes o del año (ejecutando el mismo script y recopilando la información) que con información de varios supermercados podría servir como indicador de inflación y otras cosas.

Pero bueno, por ahora nos conformamos con tener solo esta información para nuestro trabajo, tal vez en el futuro explote un poco más este tema.

Hasta la próxima.

-L

domingo, 16 de junio de 2019

Tasteful - C Testing Framework

Otro Framework de Testing..


Testing.

Algo tan dejado de lado en la comunidad informática. Pocas empresas hacen un correcto Testing y son aún menos los que lo hacen de la "forma correcta".

Cómo dijo un profesor mío, si bien una empresa puede hacer los Tests antes o después de programar, ¿Cómo se aseguran si esos Tests están bien hechos?

Ese es todo un tema aparte..

Para sobre-simplificar todo, podríamos decir que tenemos "niveles" de Testing:

  • Hacer Tests.
  • Análisis estático y dinámico de Código.
  • Análisis de Cobertura de los Tests.
  • Mutación de Tests.
  • ??? (mi conocimiento del tema llega hasta acá).
Como dije antes, pocos programadores testean su código. Lo que podría ser que hagan antes o después de los Tests es correr algunas herramientas de análisis de código estático que se encargan de verificar si hay algún "error conocido" a simple vista del código y cuando algo no falla, recién ahí suelen utilizar el análisis dinámico.

El análisis estático o dinámico pueden reconocer posibles problemas como "si este valor llega a ser NULL, va a generar una falla por acá".

Luego, de la gente que hace Tests de su código, algunos (me incluyo) dan como listo el código (que puede seguir teniendo fallas). Podríamos utilizar un análisis de cobertura que se encarga de fijarse si cada línea del código (o la gran mayoría) está implicada en algún Test, de esta forma podemos saber si hay algún camino de ejecución que no tuvimos cuenta en los Tests.

Y por último, el último nivel que conozco (hasta ahora). Sabiendo que un programa se transforma en "código de máquina" (Assembler), podemos tener que por ejemplo "a + b" se transforme en 3 pasos de assembler. Ahora, teniendo esta idea recordemos que un Test Unitario debería Testear una ÚNICA cosa.

Por lo tanto, si logramos crear un programita similar a "a + b" que cambie en alguna de esas 3 instrucciones de Assembler (mutación del programa), el Test debería dar.. ¿positivo?.. ¿negativo?..

Por cosas como estas es que luego de hacer los Tests podemos preguntarnos: ¿Están bien hechos?

Luego de este breve paseo por el Testing, les presento mi simple Framework.

El Framework es "Tasteful" y está disponible Open-Source en mi cuenta de Github:


Este Framework lo hice por dos motivos principalmente, el primero es que tenía que usar un Framework para hacer Tests y quería algo simple, que no requiera muchos pasos de instalación o configuración o escribir todo de cierta manera para que funcione (que en parte fue lo que me terminó pasando igual). La otra motivación fue practicar la programación en Linux en C.

Este Framework después lo utilicé para hacer los Tests de  mis Trabajos Prácticos y me sirvieron bastante, le fui agregando cosas a medida que las necesitaba y seguiré trabajando en él.

Algunas cosas que quiero agregarles son:
  • No tener que hacer add_test(funcion) para cada Test sino que lo reconozca solo.
  • Buscar otra forma más eficiente de poder seleccionar los archivos de Tests.
  • Rehacer el Makefile.
  • Ver si puedo simplificarlo y portarlo para que pueda ser usado por el sistema BareBones.
Al final estoy bastante contento con el resultado obtenido. A fin de cuentas, pude utilizarlo como quería.

Espero que les haya servido este Post (y que les sirva el Framework).

Hasta la próxima.