Aplicación de clima usando APIs
Introducción
Las APIs (Application Programming Interfaces) son interfaces que permiten a diferentes aplicaciones comunicarse entre sí. En el mundo del desarrollo, las APIs son fundamentales para acceder a servicios externos y obtener datos actualizados sin tener que crearlos nosotros mismos. Una aplicación de clima utilizando APIs es un proyecto práctico ideal para aprender cómo interactuar con servicios web externos desde Python. En este artículo, crearemos una aplicación que obtenga y muestre datos meteorológicos en tiempo real para cualquier ciudad del mundo.
Comprensión de las APIs de clima
Antes de comenzar a programar, es importante entender qué son las APIs de clima y cómo funcionaremos con ellas:
- Las APIs de clima proporcionan datos meteorológicos actualizados en formato estructurado (generalmente JSON)
- La mayoría requieren una clave API (API key) para autenticarse
- Suelen tener límites de peticiones (rate limits) según el tipo de suscripción
- Ofrecen diferentes endpoints para distintos tipos de datos (clima actual, pronóstico, histórico, etc.)
Para este proyecto utilizaremos la API de OpenWeatherMap, que ofrece un plan gratuito con un límite razonable de peticiones diarias.
Preparación del entorno
Para comenzar, necesitamos instalar la biblioteca requests
, que facilita el trabajo con peticiones HTTP:
# Instalar la biblioteca requests (ejecutar en terminal)
# pip install requests
Estructura básica del proyecto
Crearemos la estructura básica de nuestra aplicación:
import requests
import json
from datetime import datetime
# Clave API (regístrate en OpenWeatherMap para obtener tu propia clave)
API_KEY = "tu_clave_api_aqui"
BASE_URL = "https://api.openweathermap.org/data/2.5/weather"
def obtener_datos_clima(ciudad, pais=None):
"""
Obtiene los datos del clima para una ciudad específica.
Args:
ciudad (str): Nombre de la ciudad
pais (str, opcional): Código de país de dos letras (ISO 3166)
Returns:
dict: Datos del clima si la petición es exitosa, None en caso contrario
"""
# Construir la consulta de búsqueda
query = ciudad
if pais:
query = f"{ciudad},{pais}"
# Parámetros de la petición
params = {
"q": query,
"appid": API_KEY,
"units": "metric", # Usar unidades métricas (Celsius)
"lang": "es" # Respuestas en español
}
try:
# Realizar la petición GET
respuesta = requests.get(BASE_URL, params=params)
# Verificar si la petición fue exitosa (código 200)
if respuesta.status_code == 200:
return respuesta.json()
else:
print(f"Error: {respuesta.status_code} - {respuesta.json().get('message', 'Error desconocido')}")
return None
except requests.exceptions.RequestException as e:
print(f"Error de conexión: {e}")
return None
def main():
print("APLICACIÓN DE CLIMA")
print("==================")
ciudad = input("Introduce el nombre de la ciudad: ")
pais = input("Introduce el código del país (opcional, pulsa Enter para omitir): ")
if not pais:
pais = None
# Obtener datos del clima
datos_clima = obtener_datos_clima(ciudad, pais)
if datos_clima:
mostrar_datos_clima(datos_clima)
def mostrar_datos_clima(datos):
# Implementaremos esto más adelante
pass
if __name__ == "__main__":
main()
Procesamiento y visualización de datos
Ahora implementemos la función para mostrar los datos del clima de forma clara y ordenada:
def mostrar_datos_clima(datos):
"""
Muestra los datos del clima de forma legible.
Args:
datos (dict): Datos del clima obtenidos de la API
"""
# Extraer datos relevantes
nombre_ciudad = datos["name"]
pais = datos["sys"]["country"]
# Temperatura y sensación térmica
temp = datos["main"]["temp"]
temp_min = datos["main"]["temp_min"]
temp_max = datos["main"]["temp_max"]
sensacion = datos["main"]["feels_like"]
# Humedad y presión
humedad = datos["main"]["humidity"]
presion = datos["main"]["pressure"]
# Viento
velocidad_viento = datos["wind"]["speed"]
direccion_viento = datos["wind"]["deg"]
# Descripción del clima
descripcion = datos["weather"][0]["description"]
# Hora de la última actualización (convertir timestamp a formato legible)
timestamp = datos["dt"]
hora_actualizacion = datetime.fromtimestamp(timestamp).strftime("%H:%M:%S")
# Amanecer y atardecer
amanecer = datetime.fromtimestamp(datos["sys"]["sunrise"]).strftime("%H:%M:%S")
atardecer = datetime.fromtimestamp(datos["sys"]["sunset"]).strftime("%H:%M:%S")
# Mostrar la información
print("\nDATOS METEOROLÓGICOS")
print("===================")
print(f"Ciudad: {nombre_ciudad}, {pais}")
print(f"Condición: {descripcion.capitalize()}")
print(f"Temperatura: {temp}°C (mín: {temp_min}°C, máx: {temp_max}°C)")
print(f"Sensación térmica: {sensacion}°C")
print(f"Humedad: {humedad}%")
print(f"Presión atmosférica: {presion} hPa")
print(f"Viento: {velocidad_viento} m/s, dirección: {direccion_viento}°")
print(f"Amanecer: {amanecer}")
print(f"Atardecer: {atardecer}")
print(f"Última actualización: {hora_actualizacion}")
Añadiendo pronóstico a varios días
Vamos a mejorar nuestra aplicación añadiendo la capacidad de mostrar el pronóstico para los próximos días:
def obtener_pronostico(ciudad, pais=None, dias=5):
"""
Obtiene el pronóstico del tiempo para los próximos días.
Args:
ciudad (str): Nombre de la ciudad
pais (str, opcional): Código de país de dos letras
dias (int): Número de días para el pronóstico (máx. 5 para API gratuita)
Returns:
dict: Datos del pronóstico si la petición es exitosa, None en caso contrario
"""
# URL para el pronóstico
forecast_url = "https://api.openweathermap.org/data/2.5/forecast"
# Construir la consulta de búsqueda
query = ciudad
if pais:
query = f"{ciudad},{pais}"
# Parámetros de la petición
params = {
"q": query,
"appid": API_KEY,
"units": "metric",
"lang": "es",
"cnt": dias * 8 # 8 mediciones por día (cada 3 horas)
}
try:
respuesta = requests.get(forecast_url, params=params)
if respuesta.status_code == 200:
return respuesta.json()
else:
print(f"Error: {respuesta.status_code} - {respuesta.json().get('message', 'Error desconocido')}")
return None
except requests.exceptions.RequestException as e:
print(f"Error de conexión: {e}")
return None
def mostrar_pronostico(datos_pronostico):
"""
Muestra el pronóstico del tiempo para los próximos días.
Args:
datos_pronostico (dict): Datos del pronóstico obtenidos de la API
"""
print("\nPRONÓSTICO PARA LOS PRÓXIMOS DÍAS")
print("================================")
# Agrupar pronósticos por día
pronosticos_por_dia = {}
for pronostico in datos_pronostico["list"]:
# Obtener la fecha (sin la hora)
fecha = datetime.fromtimestamp(pronostico["dt"]).strftime("%Y-%m-%d")
# Añadir este pronóstico al día correspondiente
if fecha not in pronosticos_por_dia:
pronosticos_por_dia[fecha] = []
pronosticos_por_dia[fecha].append(pronostico)
# Mostrar pronóstico resumido por día
for fecha, pronosticos in pronosticos_por_dia.items():
# Convertir fecha a formato más legible
fecha_obj = datetime.strptime(fecha, "%Y-%m-%d")
fecha_legible = fecha_obj.strftime("%A, %d de %B").capitalize()
# Calcular promedios y encontrar valores min/max
temp_min = min(p["main"]["temp_min"] for p in pronosticos)
temp_max = max(p["main"]["temp_max"] for p in pronosticos)
# Obtener la descripción más común del día
descripciones = [p["weather"][0]["description"] for p in pronosticos]
descripcion_comun = max(set(descripciones), key=descripciones.count)
print(f"\n{fecha_legible}:")
print(f" Temperatura: {temp_min:.1f}°C a {temp_max:.1f}°C")
print(f" Condición predominante: {descripcion_comun.capitalize()}")
Manejo de errores y validación
Para hacer nuestra aplicación más robusta, añadamos un mejor manejo de errores:
def validar_ciudad(ciudad):
"""
Valida que la ciudad no esté vacía.
Args:
ciudad (str): Nombre de la ciudad
Returns:
bool: True si la ciudad es válida, False en caso contrario
"""
if not ciudad.strip():
print("Error: El nombre de la ciudad no puede estar vacío.")
return False
return True
def validar_respuesta_api(datos):
"""
Valida que la respuesta de la API contenga los datos esperados.
Args:
datos (dict): Datos recibidos de la API
Returns:
bool: True si los datos son válidos, False en caso contrario
"""
if not datos:
return False
campos_requeridos = ["name", "main", "weather", "wind", "sys"]
for campo in campos_requeridos:
if campo not in datos:
print(f"Error: Datos incompletos, falta el campo '{campo}'.")
return False
return True
Integración final
Ahora integremos todo en nuestro programa principal:
def main():
print("APLICACIÓN DE CLIMA CON API")
print("==========================")
while True:
ciudad = input("\nIntroduce el nombre de la ciudad: ")
if not validar_ciudad(ciudad):
continue
pais = input("Introduce el código del país (opcional, pulsa Enter para omitir): ")
if not pais:
pais = None
# Obtener datos del clima actual
print("\nObteniendo datos del clima actual...")
datos_clima = obtener_datos_clima(ciudad, pais)
if validar_respuesta_api(datos_clima):
mostrar_datos_clima(datos_clima)
# Preguntar si quiere ver el pronóstico
ver_pronostico = input("\n¿Desea ver el pronóstico para los próximos días? (s/n): ")
if ver_pronostico.lower() == 's':
print("\nObteniendo pronóstico...")
datos_pronostico = obtener_pronostico(ciudad, pais)
if datos_pronostico:
mostrar_pronostico(datos_pronostico)
# Preguntar si quiere consultar otra ciudad
otra_consulta = input("\n¿Desea consultar el clima de otra ciudad? (s/n): ")
if otra_consulta.lower() != 's':
print("\n¡Gracias por usar la aplicación de clima!")
break
if __name__ == "__main__":
main()
Ejemplo de ejecución
Veamos un ejemplo de cómo funcionaría nuestra aplicación:
APLICACIÓN DE CLIMA CON API
==========================
Introduce el nombre de la ciudad: Madrid
Introduce el código del país (opcional, pulsa Enter para omitir): ES
Obteniendo datos del clima actual...
DATOS METEOROLÓGICOS
===================
Ciudad: Madrid, ES
Condición: Cielo despejado
Temperatura: 22.5°C (mín: 19.8°C, máx: 24.6°C)
Sensación térmica: 22.1°C
Humedad: 45%
Presión atmosférica: 1018 hPa
Viento: 3.6 m/s, dirección: 230°
Amanecer: 07:23:44
Atardecer: 20:40:01
Última actualización: 15:30:24
¿Desea ver el pronóstico para los próximos días? (s/n): s
Obteniendo pronóstico...
PRONÓSTICO PARA LOS PRÓXIMOS DÍAS
================================
Miércoles, 23 de abril:
Temperatura: 19.2°C a 24.6°C
Condición predominante: Cielo despejado
Jueves, 24 de abril:
Temperatura: 16.8°C a 26.3°C
Condición predominante: Algunas nubes
Viernes, 25 de abril:
Temperatura: 17.5°C a 27.1°C
Condición predominante: Parcialmente nublado
Sábado, 26 de abril:
Temperatura: 18.3°C a 25.4°C
Condición predominante: Parcialmente nublado
Domingo, 27 de abril:
Temperatura: 17.9°C a 24.8°C
Condición predominante: Lluvia ligera
¿Desea consultar el clima de otra ciudad? (s/n): n
¡Gracias por usar la aplicación de clima!
Mejoras posibles
Nuestra aplicación de clima funciona correctamente, pero podríamos implementar varias mejoras:
- Interfaz gráfica: Convertir la aplicación a GUI usando bibliotecas como Tkinter o PyQt
- Almacenamiento de ciudades favoritas: Guardar las ciudades consultadas frecuentemente
- Visualización de datos: Incorporar gráficos para mostrar la evolución de temperaturas
- Alertas meteorológicas: Notificar sobre condiciones extremas o peligrosas
- Geolocalización: Detectar automáticamente la ubicación del usuario
- Exportación de datos: Permitir guardar los datos en formatos como CSV o PDF
Manejo seguro de claves API
Es importante manejar de forma segura las claves API:
# Alternativa más segura para manejar la clave API
import os
from dotenv import load_dotenv # Necesita: pip install python-dotenv
# Cargar variables de entorno desde archivo .env
load_dotenv()
# Obtener la clave API desde variable de entorno
API_KEY = os.getenv("OPENWEATHERMAP_API_KEY")
if not API_KEY:
print("Error: No se ha configurado la clave API.")
print("Crea un archivo .env con: OPENWEATHERMAP_API_KEY=tu_clave")
exit(1)
Resumen
En este artículo hemos desarrollado una aplicación de clima completa que utiliza APIs para obtener datos meteorológicos en tiempo real. Hemos aprendido a realizar peticiones HTTP con la biblioteca requests, procesar respuestas JSON, manejar errores y presentar información de forma legible para el usuario. Esta aplicación nos ha permitido aplicar conceptos importantes como la comunicación con servicios web externos, el manejo de datos estructurados y la interacción con el usuario a través de una interfaz por consola. Las APIs son herramientas fundamentales en el desarrollo moderno, y este proyecto nos proporciona una base sólida para seguir explorando su potencial en futuros desarrollos de aplicaciones más complejas.