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.

jueves, 6 de junio de 2019

Volviendo a escena


Pasó mucho desde el último Post en el Blog.

Muchas experiencias nuevas, muchos cambios.

Entre ellas, dejé un poco de lado el desarrollo de Videojuegos. No significa que no lo vaya a retomar sino que por ahora estoy tratando de probar otras cosas.
En parte por eso llamé al Blog Diario de un Indie DEV y no un Game Dev o algo similar.
Tengo algunas cosas que fui haciendo para uso personal pero que podría compartir con Internet. Entre ellas, una plantilla de Latex para realizar recetas de cocina o también un Framework de Testing hecho en C.

Son cosas pequeñas pero son algo y como muchas cosas hechas en el Blog, son cosas que no encontré cuando las necesitaba (o no me gustaba las opciones que habían) así que decidí hacerlas por mi cuenta.

Así que esperen cambios y nuevas cosas en el Post.

Saludos!

-L

sábado, 2 de febrero de 2019

¿Qué aprendí de la Global Game Jam 2019?

Lecciones de la GGJ 2019




Hace una semana participé en la Global Game Jam 2019. Para los que no la conocen, es una Game Jam que sucede al mismo tiempo en todo el mundo. El tema fue "What home means to you" (Qué significa hogar para ti).

Pueden descargar el juego desde la página de la Global Game Jam (https://globalgamejam.org/2019/games/come-home-0) o pueden mirar el código desde GitHub (https://github.com/GonzaloHirsch/GGJ-2019).

Fue una muy linda experiencia en donde aprendí mucho tanto de desarrollo de juegos, de Unity y de trabajar en equipo. 

Quería compartirles un par de lecciones que me llevo de este evento y espero poder aplicar en próximas Game Jams a las que vaya. Espero que les guste y les sirvan por si participan de algún evento similar.

Empecemos.

Una vez dado el tema lo primero que hicimos el Viernes con el equipo fue empezar a tirar ideas sobre cómo podíamos abordarlo. Había de todo tipo de ideas, pero nos dimos cuenta que todas recaían sobre una misma idea central, el hecho de que un personaje recorra el mapa y vaya aprendiendo o recordando cosas de su pasado. Para esto creímos que lo mejor era hacer un Walking Simulator (en 3D).

Aprendizaje #1: El Tamaño del juego


En el equipo éramos 2 programadores (acá estaba yo), 2 artistas 3D, 1 músico y 1 narradora/escritora. Por la composición del equipo, un Walking Simulator 3D nos iba a dar la facilidad de no tener que programar muchas mecánicas (solo el movimiento del personaje y algunas interacciones con objetos) y dedicarle la mayor parte a el modelado 3D del ambiente y a la historia.

El juego iba a ser enteramente dentro de una casa y para completarlo puede que no tardes más de 10 minutos. Hacer el juego así de corto nos llevó a que en ningún momento tengamos que correr para solucionar problemas o estar apurados para llegar a tiempo, en este aspecto creo que acertamos al elegir la temática y el tamaño del proyecto.


Habrán notado que nos "faltó" el rol de Game Designer en el equipo. Si bien nadie ocupó este rol al 100%, nos permitió que todo el tiempo cualquier integrante del equipo pueda aportar ideas sobre un nivel o  de agregar algo extra al juego y entre todos la analizábamos en vez de responsabilizar todo el diseño en una sola persona.

Aprendizaje #2: Que todos participen de la idea del juego


Al comenzar el evento, empezamos a desarrollar las cosas que si o si íbamos a necesitar (el movimiento del jugador por ejemplo) dejando algunas cosas "para el futuro" como por ejemplo, cómo va a ser la historia del juego o cómo iba a ser contada o qué iba a pasar al interactuar con los objetos.

Luego de un rato de desarrollo, se nos sumó el músico que conocimos en el evento y al contarle la idea que teníamos ya planeada para el juego, nos dio la idea que nos terminó cerrando el gameplay central del juego. Como la casa (el "mundo" del juego) iba a estar divida en habitaciones, cada habitación cuenta la historia de un recuerdo que tuvo el personaje en esa habitación.

Con esta última idea definimos el juego: Walking Simulator en la que el personaje va reviviendo sus recuerdos al entrar en las habitaciones de su antigua casa.

Sin el aporte del músico no hubiéramos podido cerrar la idea tan fácilmente, por esto creo que siempre todos deben participar de la idea del juego.


De ahí en adelante fue todo sin mayores complicaciones. Pudimos dividirnos bien las tareas y cada uno pudo avanzar por su lado con sus tareas asignadas. Aunque no faltó mucho para que tengamos el primer problema como grupo, la falta de comunicación clara.


Aprendizaje #3: Resolver lo antes posible los problemas de grupo


Nuestra situación fue la siguiente, fuimos con un grupo ya armado (excepto el músico y la escritora) y ya para el Sábado a la mañana teníamos una idea de qué queríamos hacer. Esto dejó poco lugar para que se pudiera integrar cómodamente un integrante al equipo y agregar ideas pero aún así integramos a la narradora/escritora el sábado por el mediodía que conocimos en el evento.

A medida que trabajábamos tocó el turno de definir la historia para ajustarla al gameplay del juego.

Tuvimos un gran problema de comunicación con la escritora entre explicarle lo que necesitábamos para que el juego funcione y lo que ella quería crear. Si bien su historia no era que estaba fuera de lugar, no era el nivel de profundidad de historia que necesitábamos para el juego que estábamos desarrollando.

Esto nos llevó a que al final el resto del equipo tuvo que realizar su parte del trabajo y ella enojándose con nosotros. Una pena que no hayamos podido comunicarnos efectivamente. Para la próxima, lo mejor creo que sería resolver este tipo de problemas lo antes posible de forma clara para que no escale.


Aunque mirando para atrás este fue el único problema que tuvimos como equipo, los demás problemas fueron técnicos y ninguno grave por lo que pudimos repararlo rápidamente. Un problema recurrente que tuvimos fue especialmente técnico de Unity, el hecho de que varias personas trabajen sobre la misma escena, lo que lleva a la otra lección.


Aprendizaje #4: De ser posible, que cada uno trabaje en una escena aparte (en Unity)


Una limitación muy marcada que tuvimos fue que no podíamos trabajar varias personas en una misma escena de Unity al mismo tiempo. Si esto pasaba, después no podíamos integrar ambas escenas con GitHub (o no supimos cómo). Esto nos atrasó mucho el trabajo porque tal vez teníamos que esperar que alguien termine de arreglar algún detalle o modelo en la escena para que otras dos personas puedan agregar algo a la escena.

Esto se pudo haber solucionado si cada uno trabajaba en "zonas" de la casa distintas (escenas distintas). 

Este problema no fue grave porque el tamaño del juego era chico y ya teníamos las mecánicas desarrolladas. Lo único que faltaba eran cuestiones de diseño.


Quitando lo técnico, hubieron unos momentos donde se nos ocurrieron algunas ideas locas para agregar al juego, algunos easter eggs que ayudaron a que nos pasemos la madrugada riendo,  integrando memes o ideas locas al juego. De ahí la siguiente lección:

Aprendizaje #5: Arriésgate

Muchas de estas "ideas locas" fueron los "¿Que tal si..?" que fueron bien recibidos por todo el equipo. Agregar una habitación secreta con una parodia a un meme o elementos dentro del juego que desentonen con la temática del juego. Estos elementos trajeron una gran distensión al grupo sin alejarnos de nuestro objetivo principal.

Desde el lado técnico, si bien el tiempo no sobra en las Game Jams y en eventos similares, cuando me encargaba de la programación de las "misiones", me arriesgué a desarrollar un "Sistema de misiones". Si bien esto me llevó más tiempo que hacer las cosas directamente, a largo plazo nos hizo mucho más fácil agregar más "misiones". El riesgo lo tomé porque ya tenía un poco de experiencia realizando este tipo de sistemas y porque había otro programador en el equipo que finalmente me ayudó a integrar este sistema dentro del juego.


Por último y no menos importante para mi:

Aprendizaje #6: Duerme

Este evento iba a ser mi 4° evento competitivo (5° contando la Game Jam que organicé) en el que el tiempo es el mayor recurso. Esto para algunos suele inducir a que hay que quedarse toda la noche programando o a lo sumo dormir 1 o 2 horas.

En los eventos pasados llevaba esta idea de no dormir y aguantar lo más posible y siempre quedaba exhausto y de malhumor para el día siguiente. Esta vez hice algo distinto, primero consulté a mis compañeros si no les molestaba que en ese momento vaya a dormir (parece tonto pero tal vez alguien espera que todos sus compañeros se queden sin dormir para trabajar). Viendo que no había problema, dormí mínimo 4 horas y ahí estuvo el cambio.

Cuando me levanté no tuve ni dolor de cabeza ni mal humor y tuve energía suficiente para el resto del evento. Si bien puede que no estábamos al 100% de nuestra energía, fue suficiente para que resolvamos los problemas que surgían y pudiéramos terminar el juego.

Esta Game Jam fue un evento muy distinto para mi. No solo porque el evento era de videojuegos sino por cómo lo abordamos. Me gustó mucho el resultado final que tuvimos y como al elegir un juego de pequeño tamaño, no tuvimos que quedarnos todos hasta último momento programando para terminar.

Espero poder asistir ( y organizar)  más Game Jams este año y espero que estas lecciones les puedan servir a alguien.

Hasta la próxima.

-L