Created
November 4, 2025 02:23
-
-
Save juliancantillo/d25c9b841dba530e4ccdc26a5be4c77b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| # -*- coding: utf-8 -*- | |
| """ | |
| Script para geolocalizar direcciones IP y generar tabla LaTeX usando GeoIP2 | |
| Uso: python3 geolocalize_ips.py < ips.txt | |
| Requiere: pip install geoip2 | |
| Descarga la base de datos GeoLite2-City.mmdb de MaxMind | |
| """ | |
| import sys | |
| import geoip2.database | |
| import math | |
| import os | |
| from typing import List, Dict, Tuple | |
| class GeoLocalizador: | |
| """Clase para geolocalizar IPs y calcular distancias""" | |
| def __init__(self, db_path=None): | |
| self.ips_data = [] | |
| self.distancia_acumulada = 0 | |
| self.latitud_anterior = None | |
| self.longitud_anterior = None | |
| # Buscar base de datos GeoIP2 | |
| if db_path is None: | |
| # Buscar en ubicaciones comunes | |
| possible_paths = [ | |
| 'GeoLite2-City.mmdb', | |
| '/usr/share/GeoIP/GeoLite2-City.mmdb', | |
| '/usr/local/share/GeoIP/GeoLite2-City.mmdb', | |
| os.path.expanduser('~/GeoLite2-City.mmdb'), | |
| ] | |
| for path in possible_paths: | |
| if os.path.exists(path): | |
| db_path = path | |
| break | |
| if db_path is None: | |
| raise FileNotFoundError( | |
| "No se encontró GeoLite2-City.mmdb. " | |
| "Descárgalo de https://dev.maxmind.com/geoip/geolite2-free-geolocation-data" | |
| ) | |
| self.reader = geoip2.database.Reader(db_path) | |
| print(f"[*] Base de datos GeoIP2 cargada: {db_path}", file=sys.stderr) | |
| def __del__(self): | |
| """Cierra el lector de base de datos al destruir el objeto""" | |
| if hasattr(self, 'reader'): | |
| self.reader.close() | |
| def obtener_ubicacion(self, ip: str) -> Dict: | |
| """ | |
| Obtiene la ubicación geográfica de una IP usando GeoIP2 | |
| Args: | |
| ip (str): Dirección IP a geolocalizar | |
| Returns: | |
| dict: Diccionario con información de ubicación | |
| """ | |
| try: | |
| response = self.reader.city(ip) | |
| # Obtener datos de ubicación | |
| ciudad = response.city.name or 'Desconocida' | |
| pais = response.country.name or 'Desconocido' | |
| latitud = response.location.latitude or 0 | |
| longitud = response.location.longitude or 0 | |
| # GeoIP2 no proporciona ISP en la versión gratuita | |
| # Para obtener ISP necesitas GeoLite2-ASN o GeoIP2-ISP (de pago) | |
| isp = 'N/A' | |
| return { | |
| 'ip': ip, | |
| 'ciudad': ciudad, | |
| 'pais': pais, | |
| 'latitud': latitud, | |
| 'longitud': longitud, | |
| 'isp': isp, | |
| 'exito': True | |
| } | |
| except geoip2.errors.AddressNotFoundError: | |
| return { | |
| 'ip': ip, | |
| 'ciudad': 'No encontrada', | |
| 'pais': 'No encontrado', | |
| 'latitud': 0, | |
| 'longitud': 0, | |
| 'isp': 'N/A', | |
| 'exito': False | |
| } | |
| except Exception as e: | |
| return { | |
| 'ip': ip, | |
| 'ciudad': 'Error', | |
| 'pais': str(e), | |
| 'latitud': 0, | |
| 'longitud': 0, | |
| 'isp': 'N/A', | |
| 'exito': False | |
| } | |
| def calcular_distancia_haversine(self, lat1: float, lon1: float, | |
| lat2: float, lon2: float) -> float: | |
| """ | |
| Calcula la distancia entre dos puntos usando la fórmula de Haversine | |
| Args: | |
| lat1, lon1: Latitud y longitud del punto 1 | |
| lat2, lon2: Latitud y longitud del punto 2 | |
| Returns: | |
| float: Distancia en kilómetros | |
| """ | |
| R = 6371 # Radio de la Tierra en km | |
| lat1_rad = math.radians(lat1) | |
| lat2_rad = math.radians(lat2) | |
| delta_lat = math.radians(lat2 - lat1) | |
| delta_lon = math.radians(lon2 - lon1) | |
| a = math.sin(delta_lat/2)**2 + math.cos(lat1_rad) * math.cos(lat2_rad) * math.sin(delta_lon/2)**2 | |
| c = 2 * math.asin(math.sqrt(a)) | |
| distancia = R * c | |
| return distancia | |
| def procesar_ips(self, ips: List[str]) -> List[Dict]: | |
| """ | |
| Procesa una lista de IPs y obtiene su ubicación | |
| Args: | |
| ips (list): Lista de direcciones IP | |
| Returns: | |
| list: Lista de diccionarios con información de geolocalización | |
| """ | |
| self.distancia_acumulada = 0 | |
| self.latitud_anterior = None | |
| self.longitud_anterior = None | |
| for i, ip in enumerate(ips, 1): | |
| print(f"[*] Procesando IP {i}/{len(ips)}: {ip}", file=sys.stderr) | |
| ubicacion = self.obtener_ubicacion(ip) | |
| # Calcular distancia acumulada | |
| if ubicacion['exito'] and self.latitud_anterior is not None: | |
| dist = self.calcular_distancia_haversine( | |
| self.latitud_anterior, | |
| self.longitud_anterior, | |
| ubicacion['latitud'], | |
| ubicacion['longitud'] | |
| ) | |
| self.distancia_acumulada += dist | |
| elif ubicacion['exito']: | |
| self.distancia_acumulada = 0 | |
| ubicacion['distancia_acumulada'] = round(self.distancia_acumulada, 2) | |
| ubicacion['salto'] = i | |
| # Actualizar coordenadas anteriores | |
| if ubicacion['exito']: | |
| self.latitud_anterior = ubicacion['latitud'] | |
| self.longitud_anterior = ubicacion['longitud'] | |
| self.ips_data.append(ubicacion) | |
| return self.ips_data | |
| def generar_tabla_latex(self) -> str: | |
| """ | |
| Genera la tabla LaTeX con los resultados de geolocalización | |
| Returns: | |
| str: Tabla LaTeX formateada | |
| """ | |
| tabla = r"\begin{table}[H]" + "\n" | |
| tabla += r"\centering" + "\n" | |
| tabla += r"\caption{Geolocalizacion de Routers en la Ruta}" + "\n" | |
| tabla += r"\begin{tabular}{|c|c|c|c|c|c|c|}" + "\n" | |
| tabla += r"\hline" + "\n" | |
| tabla += r"\textbf{Salto} & \textbf{IP} & \textbf{Ciudad} & \textbf{Pais} & \textbf{Latitud} & \textbf{Longitud} & \textbf{Dist. Ac. (km)} \\" + "\n" | |
| tabla += r"\hline" + "\n" | |
| for dato in self.ips_data: | |
| salto = dato['salto'] | |
| ip = dato['ip'] | |
| ciudad = dato['ciudad'] | |
| pais = dato['pais'] | |
| latitud = round(dato['latitud'], 4) | |
| longitud = round(dato['longitud'], 4) | |
| dist_ac = dato['distancia_acumulada'] | |
| # Formatear para LaTeX (escapar caracteres especiales) | |
| ciudad = ciudad.replace('_', r'\_') | |
| pais = pais.replace('_', r'\_') | |
| tabla += f"{salto} & {ip} & {ciudad} & {pais} & {latitud} & {longitud} & {dist_ac} \\\\\n" | |
| tabla += r"\hline" + "\n" | |
| tabla += r"\end{tabular}" + "\n" | |
| tabla += r"\end{table}" + "\n" | |
| return tabla | |
| def main(): | |
| """Función principal""" | |
| print("[*] Geolocalizador de IPs v1.0", file=sys.stderr) | |
| print("[*] Leyendo IPs del stdin...", file=sys.stderr) | |
| # Leer IPs del stdin | |
| ips = [] | |
| try: | |
| for linea in sys.stdin: | |
| ip = linea.strip() | |
| if ip and not ip.startswith('#'): # Ignorar líneas vacías y comentarios | |
| ips.append(ip) | |
| except KeyboardInterrupt: | |
| print("\n[!] Interrupción del usuario", file=sys.stderr) | |
| sys.exit(1) | |
| if not ips: | |
| print("[!] No se encontraron IPs válidas", file=sys.stderr) | |
| sys.exit(1) | |
| print(f"[*] Se encontraron {len(ips)} IPs para procesar", file=sys.stderr) | |
| # Procesar IPs | |
| geolocalizador = GeoLocalizador() | |
| geolocalizador.procesar_ips(ips) | |
| # Generar tabla LaTeX | |
| tabla_latex = geolocalizador.generar_tabla_latex() | |
| # Mostrar resultados | |
| print("\n" + "="*80, file=sys.stderr) | |
| print("[✓] Tabla LaTeX generada exitosamente", file=sys.stderr) | |
| print("="*80 + "\n", file=sys.stderr) | |
| # Imprimir tabla en stdout | |
| print(tabla_latex) | |
| # Mostrar resumen | |
| print(f"\n% Resumen: {len(ips)} IPs procesadas", file=sys.stderr) | |
| print(f"% Distancia total acumulada: {geolocalizador.distancia_acumulada:.2f} km", file=sys.stderr) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment