Reforzando mis conocimientos en Python con 3 mini proyectos prácticos
Usando requests, csv, re, Counter y matplotlib para analizar endpoints, logs y métricas.

Ingeniero en Telecomunicaciones con enfoque en redes, conectividad y automatización. Actualmente estudiando AWS, DevOps y CI/CD. Comparto mi camino con proyectos prácticos y artículos técnicos desde Chile 🇨🇱.
https://robertoespana.hashnode.dev/portafolio
Reforzando mis conocimientos de Python
Para reforzar mis conocimientos en Python me propuse crear 4 mini proyectos con el fin de utilizar las librerías requests, csv, re, Counter y matplotlib.
Proyecto 1: Verificador de endpoints HTTP
Para el primer proyecto debo hacer un verificador de endpoints HTTP, cuyo objetivo es aprender a hacer solicitudes HTTP y manejar errores.
Aprendizajes: requests, bucles, manejo de excepciones, listas.
Debía realizar lo siguiente:
Crear un script que consulte URLs de tus pods (
/metricso/test) y diga si están accesibles.Mostrar resultados en consola y, opcionalmente, guardar en un CSV.
Para esto utilicé la librería requests para las consultas HTTP y csv para almacenar los datos dentro de un archivo CSV.
El código queda de la siguiente manera:
Quizás no era necesario definirlo dentro de una función. Primero creé una lista donde almacenaría los datos.
Hice tres consultas para cada path: /, /test y /metrics.
Asigné una variable result1, donde con el método requests.get() hago la consulta al path correspondiente, y utilizo el método .append() para almacenar la ruta (que defino directamente, aunque el código también la obtiene de la consulta).
Así lo hice para las tres consultas. Al final retorno resultados.
Luego creo un menú con un input() que consulta si deseo almacenar los resultados dentro de un archivo CSV.
Solo creé un archivo llamado resultados.csv utilizando la librería csv. Además, almaceno dentro de una variable la función con las consultas (que serían los resultados) y utilizo el writer para escribir los datos dentro del archivo y guardarlo.
import requests
import csv
def test():
resultados = []
result1 = requests.get('http://easy-app.local/')
resultados.append({"ruta": "/home", "codigo": result1.status_code})
result2 = requests.get('http://easy-app.local/test')
resultados.append({"ruta": "/test", "codigo": result2.status_code})
result3 = requests.get('http://easy-app.local/metrics')
resultados.append({"ruta": "/metrics", "codigo": result3.status_code})
return resultados
resp = input(str("¿Almacenar resultados en archivo CSV? (y/n): "))
if resp == "y":
data = test()
with open("resulatados.csv", "w", newline="") as csvfile:
campos = ["ruta", "codigo"]
writer = csv.DictWriter(csvfile, fieldnames=campos)
writer.writeheader()
writer.writerows(data)
print("Archivo CSV creado correctamente.")
else:
data=test()
print(data)
Al ejecutar el script, nos pide “y/n”. Si coloco “no”, devuelve los datos directamente en la consola (en este caso, las consultas fueron exitosas con código 200).
Si coloco “yes” y hago un cat, podemos ver que se creó el archivo CSV y los datos quedaron almacenados correctamente.

Proyecto 2: Analizador simple de logs
El objetivo de este proyecto era aprender a procesar archivos y extraer métricas.
Aprendizajes: manejo de archivos (open, read, write), expresiones regulares (re).
Solo debía tomar un log de mi aplicación (en este caso Flask), contar la cantidad de requests por endpoint (path) y la cantidad de errores (HTTP 4xx y 5xx), y mostrar un resumen en consola.
Primero fui a mi aplicación (que está corriendo en Kubernetes) y extraje un log con las consultas HTTP de mi aplicación.

Con este archivo trabajé para cumplir con los objetivos.
En este proyecto tuve que utilizar la librería re de expresiones regulares. Al principio las encontré un poco complejas.
Lo primero que hice fue abrir el archivo con un with open y aplicar un bucle for para trabajar de manera más ordenada.
Primero definí el patrón. Este patrón utiliza 3 grupos.
Es importante visualizar la estructura del log: el mío es sencillo, y lo que quiero extraer son la consulta (método), el path y el código.
La consulta en el log comienza con un ", seguido del método (por ejemplo GET).
Con eso en mente, definí el primer grupo utilizando \w+.
El \w significa “cualquier carácter alfanumérico”, y el + indica que continúe hasta que no encaje (en este caso, hasta un espacio).
Ojo: \w no lee el carácter /.
El segundo grupo es para extraer el path de la consulta. Para eso utilicé \S+, que captura todos los caracteres que no sean espacios, incluyendo /, hasta encontrar un espacio.
Después definí HTTP/1\.1", pero no lo capturo; solo sirve para mantener el orden.
Finalmente, el tercer grupo extrae el código HTTP: utilicé \d{3} para definir que sean exactamente 3 dígitos (por ejemplo: 200, 404, 500).
Completado el patrón, utilicé el método re.findall para encontrar las coincidencias dentro del log.
Le paso el patrón y las líneas del archivo, y el resultado lo guardo dentro de matches.
Luego ejecuté un bucle for metodo, path, codigo in matches:.
Como el resultado está ordenado, trabajo directamente con esas tres variables.
Primero quiero extraer los paths que existen dentro del log.
Para eso hago paths.append(path) y luego, fuera del bucle, ejecuto un for path in paths para almacenar en la lista vistos los valores únicos (sin repetir) hasta recorrer todos los datos del log.
Después, dentro del mismo bucle, utilizo un if para obtener solo los GET con código 200 y los almaceno dentro de log.
En caso contrario (else), almaceno el resto (los errores) dentro de log1.
Para saber la cantidad de cada uno, simplemente uso un bucle for y voy contando con un contador.
import re
log=[]
log1=[]
paths=[]
vistos=[]
with open('flask_app_pod1.log', 'r') as archivo:
lineas = archivo.readlines()
for l in lineas:
patron = r'"(\w+) (\S+) HTTP/1\.1" (\d{3})'
# El primer " le dice a regex que debe buscar un " y despues de este capturar con el primer grupo de captura.
# Primer grupo: cualquier carecter, "+" sigue capturando todos lo que cumplan la condicion finalizando en el espacio. no captura "/"
# Segundo grupo: cualquier caracter que no tenga un espacio en blanco. si captura "/" ademas tiene un "+" que siga capturando hasta que no se cumpla la condicion "termina cuando llega al espacio".
# Defino el HTTP/1.1 pero no lo capturo le digo a regex que continua. despues del espacio sigue el 3er grupo.
# Tercer grupos: cualquier numero del 0-9 pero con {3} que indica el max de caracteres que deben ser.
# El signo "+" es que siga capturando caracteres que cumplan con la condicion.
matches = re.findall(patron, l)
# Dentro de una variable uso el re.findall para encontrar coincidencias, le cargo el patron y los logs.
for metodo, path, codigo in matches:
paths.append(path)
for metodo, path, codigo in matches: #for in defino 3 variables: metodo, path y codigo y estan se iteran en matches
if metodo == "GET" and codigo == "200": # matches almacena el resultado en listas por linea.
resultado=f"{metodo}, {path}, {codigo}"#defino un if donde si el metodo es igual a GET y tambien el codigo es 200 lo imprimo.
log.append(resultado)
else:
resultado1=f"{metodo}, {path}, {codigo}"
log1.append(resultado1)
contador200=0
for n in log:
contador200 +=1
contador400500=0
for m in log1:
contador400500 +=1
print("\nConsultas GET encontradas:\n")
for path in paths:
if path not in vistos:
print(path)
vistos.append(path)
print("\nTotal 200:",contador200)
print("Total errores (4XX o 5XX):",contador400500)
Finalmente, al ejecutar el código obtengo primero los paths encontrados dentro del log, que serían /main, /test y /metrics (sin repetir).
Después obtengo la cantidad de códigos 200 y el total de errores dentro del log.

Proyecto 3: Dashboard básico de métricas
En el proyecto número 3 hice algunas mejoras, como usar len() para contar los datos dentro de una variable y mejorar la lógica dentro del for.
Este proyecto consiste en crear un dashboard básico de métricas.
Aprendizajes: matplotlib o plotly, manejo de listas/diccionarios, exportar gráficos.
Debía usar los datos del proyecto anterior, graficar un histograma de requests por endpoint y un gráfico circular (pie) con los códigos HTTP, y guardar los gráficos como imágenes.
Para completarlo, debía extraer todos los GET y dividirlos por código para poder graficarlos.
Primero extraje solo los códigos del log y usé Counter para contar la cantidad de códigos existentes (200, 4XX y 5XX).
Luego utilicé matplotlib para graficar.
Con plt.subplots pude mostrar los dos gráficos lado a lado (pie y barra).
import re
from collections import Counter
import matplotlib.pyplot as plt
log=[]
log1=[]
paths=[]
vistos=[]
codigos=[]
resultadocodes=[]
with open('flask_app_pod1.log', 'r') as archivo:
lineas = archivo.readlines()
for l in lineas:
patron = r'"(\w+) (\S+) HTTP/1\.1" (\d{3})'
matches = re.findall(patron, l)
for metodo, path, codigo in matches:
paths.append(path)
codigos.append(codigo)
if metodo == "GET":
resultadocodes.append(codigo)
if codigo == "200":
resultado = f"{metodo}, {path}, {codigo}"
log.append(resultado)
else:
resultado1 = f"{metodo}, {path}, {codigo}"
log1.append(resultado1)
contador200=len(log)
contador400500=len(log1)
x=int(input("1. Ver consultas analizadas\n2. Ver gráficos\nIngrese una opción: "))
if x == 1:
print("\nConsultas GET encontradas:\n")
for path in paths:
if path not in vistos:
print(path)
vistos.append(path)
print("\nTotal 200:",contador200)
print("Total errores (4XX o 5XX):",contador400500)
else:
contador = Counter(resultadocodes)
etiquetas = list(contador.keys())
valores = list(contador.values())
colores_pie = ["#2E8B57", "#88D18A", "#F0E68C", "#DDA15E", "#BC6C25"]
colores_bar = ["#2E8B57", "#88D18A", "#F0E68C"]
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12,6))
ax1.pie(valores, labels=etiquetas, autopct="%0.1f %%", colors=colores_pie[:len(valores)])
ax1.set_title("Distribución de códigos HTTP")
ax1.axis("equal")
ax2.bar(x=etiquetas, height=valores, color=colores_bar[:len(valores)])
ax2.set_title("Cantidad de respuestas por código HTTP")
ax2.set_ylabel("Cantidad de respuestas")
plt.tight_layout()
plt.show()
La salida sería la siguiente: se genera un dashboard según la elección del usuario.
En la primera opción muestra las consultas directamente en consola, y en la segunda aparece una nueva ventana con los gráficos de pastel y barra, mostrando la distribución de códigos HTTP según mi log (en porcentaje y cantidad de requests).


Con este breve desarrollo de proyectos pude refrescar mi memoria en Python y reforzar varias librerías que me servirán en el análisis de logs y métricas.
En la próxima publicación subiré un script orientado a conectarse a la API de Prometheus y configurar un cron para extraer un archivo con los datos.




