Apariencia
📙 Clase 23 — requests: consumir APIs
Fase 4 · Archivos y el mundo exterior ⬅️ Volver al índice de clases
🎯 Qué aprendí
- Hacer peticiones HTTP con
requestsy leer respuestas JSON. - Códigos de estado (
200,404…),timeouty manejo de errores de red. - Traer datos de internet a tu app de escritorio (clima, tipo de cambio, lo que sea).
📖 PARTE TEÓRICA
🌐 1. ¿Qué es una API?
Una API web es un servicio al que le pides datos por una URL y te responde (casi siempre) JSON — el formato que ya dominas de la Clase 20.
tu app ──── GET https://api.servicio.com/usuarios/ana ────▶ servidor
tu app ◀─── {"nombre": "Ana", "repos": 8} (JSON) ───────── servidor📦 2. Instalar y usar
requests no viene con Python (se instala en tu venv — ya está en el del curso):
bash
pip install requestspython
import requests
r = requests.get("https://api.github.com/users/octocat", timeout=10)
print(r.status_code) # 200 ← todo OK
datos = r.json() # el JSON de la respuesta, YA convertido a dict
print(datos["login"]) # octocat
print(datos["public_repos"]) # 8📌 Verificado en tu
.venvconrequests 2.34.2contra la API real de GitHub: respuesta200,login='octocat',public_repos=8.
🚦 3. Códigos de estado
| Código | Significa | Qué haces |
|---|---|---|
200 | OK | usar r.json() |
404 | no existe | avisar "no encontrado" |
401 / 403 | sin permiso | revisar API key |
500+ | el servidor falló | reintentar más tarde |
python
r = requests.get("https://api.github.com/users/este-usuario-no-existe-xyz-999",
timeout=10)
print(r.status_code) # 404 ← la petición "funcionó", pero no hay tal usuario⚠️ Un
404no lanza excepción: la petición HTTP se completó. Compruebar.status_code(o llamar.raise_for_status()para convertir errores HTTP en excepción).
🛡️ 4. Lo que sí lanza excepciones: la red
Sin internet, DNS caído, servidor lento… eso sí explota. El patrón robusto completo:
python
def consultar_usuario(nombre):
try:
r = requests.get(f"https://api.github.com/users/{nombre}", timeout=10)
except requests.ConnectionError:
return None, "Sin conexión a internet"
except requests.Timeout:
return None, "El servidor tardó demasiado"
if r.status_code == 404:
return None, "Usuario no encontrado"
if r.status_code != 200:
return None, f"Error del servidor ({r.status_code})"
return r.json(), ""⚠️ Pon siempre
timeout=: sin él, una petición puede colgarse para siempre — y en una GUI eso significa ventana congelada.
🧪 Tip de entrevista: "¿Qué métodos HTTP conoces?" →
GET(leer),POST(crear),PUT/PATCH(modificar),DELETE(borrar). Conrequests:requests.post(url, json={...})etc.
🖥️ EN TU APP DE ESCRITORIO
Datos vivos en tu GUI — con la lógica separada (Clase 08) y el resultado en un label:
python
class App(ctk.CTk):
def _buscar(self):
usuario = self.entry.get().strip()
datos, error = consultar_usuario(usuario) # la función de arriba
if error:
self.resultado.configure(text=f"⚠️ {error}", text_color="orange")
return
self.resultado.configure(
text=f"{datos['login']} — {datos['public_repos']} repos públicos",
text_color="white",
)| Idea de app | API pública |
|---|---|
| Widget de clima | Open-Meteo (sin API key) |
| Conversor de monedas | exchangerate / frankfurter |
| Buscador de perfiles GitHub | api.github.com (la de esta clase) |
| Precio de cripto | CoinGecko |
⚠️ El gotcha grande: una petición lenta dentro de un callback congela la ventana (el
mainloopqueda esperando — es el problema de la Clase 03). Contimeout=10sobrevives por ahora; la solución profesional es threading, y es exactamente la Clase 33 de la Fase 6.
🗄️ CON BASE DE DATOS (caso de uso)
Patrón API + caché local: consulta una vez, guarda en tu BD/JSON, y sirve desde ahí (la app funciona incluso sin internet):
python
def obtener_tipo_cambio(con):
fila = con.execute("SELECT valor, fecha FROM cache WHERE clave='usd'").fetchone()
if fila and es_de_hoy(fila[1]):
return fila[0] # ① servir del caché
datos, error = consultar_api() # ② si no, pedir a la API
if datos:
con.execute("INSERT OR REPLACE INTO cache VALUES ('usd', ?, ?)",
(datos["valor"], hoy())) # ③ y guardar para la próxima
return datos["valor"] if datos else None🏋️ EJERCICIOS CON SOLUCIÓN
Ejercicio 1 — Tu perfil de GitHub
Consulta https://api.github.com/users/TU_USUARIO e imprime nombre y repos públicos.
Ver solución
python
import requests
r = requests.get("https://api.github.com/users/octocat", timeout=10)
if r.status_code == 200:
d = r.json()
print(d["login"], "-", d["public_repos"], "repos") # octocat - 8 reposEjercicio 2 — Manejo del 404
Escribe existe_usuario(nombre) que devuelva True/False según el status code.
Ver solución
python
import requests
def existe_usuario(nombre):
r = requests.get(f"https://api.github.com/users/{nombre}", timeout=10)
return r.status_code == 200
print(existe_usuario("octocat")) # True
print(existe_usuario("este-usuario-no-existe-xyz-999")) # FalseEjercicio 3 — A prueba de balas
Mejora existe_usuario para que devuelva None si no hay internet (atrapa requests.ConnectionError).
Ver solución
python
import requests
def existe_usuario(nombre):
try:
r = requests.get(f"https://api.github.com/users/{nombre}", timeout=10)
except requests.ConnectionError:
return None # sin red: "no sé"
return r.status_code == 200❓ Preguntas y respuestas (autoevaluación)
1. ¿Qué devuelve r.json()?
El cuerpo JSON de la respuesta convertido a estructuras Python (dict/list) — como
json.loads.
2. ¿Un 404 lanza excepción en requests?
No: la petición HTTP se completó. Se revisa
r.status_code(or.raise_for_status()).
3. ¿Por qué timeout= es obligatorio en la práctica?
Sin él la petición puede colgarse indefinidamente — en una GUI, ventana congelada.
4. ¿Qué excepciones de red conviene atrapar?
requests.ConnectionError(sin red/DNS) yrequests.Timeout(tardó de más).
📎 Apuntes relacionados
- El JSON que devuelven las APIs → Clase 20
- Separar la consulta de la GUI → Clase 08 · return
- La solución al congelamiento → Threading (Fase 6)
➡️ Siguiente
¡Fase 4 lista! 🎉 Fase 5: Clase 24 · lambda, map y filter.