HTTP y la librería requests
1.1 · El lenguaje de la web
HTTP habla con verbos: GET (pedir datos), POST (enviar y crear),
PUT/PATCH (actualizar) y DELETE (borrar). Toda web que consumes es una
API. La respuesta trae un código de estado: 200 OK, 201 creado, 404 no
encontrado, 500 error interno.
1.2 · Consumir una API con requests
$ pip install requests
import requests
r = requests.get("https://api.github.com/users/octocat")
print(r.status_code) # 200
datos = r.json() # respuesta en dict
print(datos["login"]) # octocat
r.status_code. Si es 200, procesa; si es 404 no hay datos.
Para errores de red usa try/except requests.RequestException.payload = {"nombre": "Ana", "email": "ana@x.es"}
r = requests.post("https://api.ejemplo.com/usuarios", json=payload)
print(r.status_code) # 201 si se crea bien
Flask: tu primer servidor
Flask es el micro-framework web más popular de Python: en 3 líneas tienes una web funcionando.
$ pip install flask
from flask import Flask
app = Flask(__name__)
@app.route("/")
def inicio():
return "¡Hola, mundo!"
if __name__ == "__main__":
app.run(debug=True)
Ejecuta python app.py y abre http://127.0.0.1:5000. El decorador
@app.route("/") mapea una URL a una función.
debug=True recarga el servidor solo al guardar y muestra errores detallados.
¡Son solo para desarrollo!Rutas dinámicas, formularios y redirecciones
3.1 · Rutas con parámetros
from flask import Flask
app = Flask(__name__)
@app.route("/usuario/<nombre>")
def ver_usuario(nombre):
return f"Perfil de {nombre}"
@app.route("/numero/<int:n>")
def cuadrado(n):
return f"El cuadrado es {n ** 2}"
3.2 · Leer datos enviados por el cliente
En Flask, request.args lee parámetros de la URL ?clave=valor y
request.form los campos de un formulario POST:
from flask import Flask, request, redirect
app = Flask(__name__)
@app.route("/buscar")
def buscar():
texto = request.args.get("q", "")
return f"Buscando: {texto}"
@app.route("/enviar", methods=["POST"])
def enviar():
nombre = request.form["nombre"]
return redirect(f"/usuario/{nombre}")
redirect(...) envía al navegador a otra ruta. Es el patrón típico tras guardar un formulario:
POST → procesar → redirigir.
Tu propia API REST con JSON
jsonify convierte un dict en una respuesta JSON con el tipo de contenido correcto. Así montas
una API REST: URLs por recurso y verbos HTTP por acción.
from flask import Flask, jsonify, request
app = Flask(__name__)
productos = [
{"id": 1, "nombre": "ratón", "precio": 12},
{"id": 2, "nombre": "teclado", "precio": 45},
]
@app.route("/api/productos")
def listar():
return jsonify(productos)
@app.route("/api/productos", methods=["POST"])
def crear():
datos = request.get_json()
nuevo = {"id": len(productos) + 1, **datos}
productos.append(nuevo)
return jsonify(nuevo), 201
return jsonify(nuevo), 201. Guía: GET→200, creación→201, no existe→404, error del cliente→400.Conectar Flask con tu frontend
Flask puede servir páginas completas con plantillas (más adelante, con «templates» de Jinja) o únicamente
responder JSON. La conexión más natural con lo que ya sabes de JavaScript es: tu página llama a tu API con
fetch() y pinta el resultado.
from flask import Flask, jsonify, request
app = Flask(__name__)
tarefas = ["estudiar Python", "hacer ejercicios"]
@app.route("/api/tareas")
def listar_tareas():
return jsonify(tarefas)
@app.route("/api/tareas", methods=["POST"])
def anadir_tarea():
cuerpo = request.get_json()
tarefas.append(cuerpo["texto"])
return jsonify(tarefas), 201
<script>
fetch('http://127.0.0.1:5000/api/tareas')
.then(r => r.json())
.then(tareas => console.log(tareas));
</script>
file:// y el servidor está en otra URL, el navegador
bloqueará la petición (CORS). Lo más simple: sirve también tu HTML desde Flask o usa
flask-cors en desarrollo.✍️ Los 15 ejercicios del bloque
Crea la carpeta python-b7/. Los primeros ejercicios son scripts de cliente; a partir del 8 son
aplicaciones Flask que ejecutas con python app.py.
Sección A · Consumiendo APIs (requests)
1 Mi primera petición
Con requests.get consulta https://api.github.com/users/octocat, muestra el estado
y los campos login y bio.
Ver solución
import requests
r = requests.get("https://api.github.com/users/octocat")
print(r.status_code)
datos = r.json()
print(datos["login"])
print(datos["bio"])
2 Estado y manejo de errores
Consulta una URL inexistente (https://api.github.com/users/este-usuario-no-existe-123) y
muestra, según el código, «OK», «No encontrado» o «Error». Envuelve la petición en try/except.
Ver solución
import requests
try:
r = requests.get("https://api.github.com/users/este-usuario-no-existe-123")
if r.status_code == 200:
print("OK")
elif r.status_code == 404:
print("No encontrado")
else:
print(f"Error {r.status_code}")
except requests.RequestException as e:
print("Error de red", e)
3 Serie de peticiones
Petición a la lista de repos de https://api.github.com/users/octocat/repos y muestra el nombre
y el lenguaje de los 3 primeros repos.
Ver solución
import requests
r = requests.get("https://api.github.com/users/octocat/repos")
repos = r.json()
for repo in repos[:3]:
print(repo["name"], repo.get("language"))
Sección B · Flask básico
4 Hola desde Flask
Crea app.py con una ruta / que devuelva «¡Hola, Flask!». Ejecútalo y compruébalo
en el navegador.
Ver solución
from flask import Flask
app = Flask(__name__)
@app.route("/")
def inicio():
return "¡Hola, Flask!"
if __name__ == "__main__":
app.run(debug=True)
5 Dos rutas
Añade a app.py una ruta /sobre que devuelva un breve texto sobre ti, y
/contacto con tu email falso.
Ver solución
from flask import Flask
app = Flask(__name__)
@app.route("/")
def inicio():
return "Bienvenido"
@app.route("/sobre")
def sobre():
return "Estudiante de Python en TécnicoWeb"
@app.route("/contacto")
def contacto():
return "hola@ejemplo.es"
if __name__ == "__main__":
app.run(debug=True)
6 Ruta dinámica con nombre
Añade /usuario/<nombre> que devuelva "Hola, {nombre}". Pruébalo con tu
nombre en el navegador.
Ver solución
from flask import Flask
app = Flask(__name__)
@app.route("/usuario/<nombre>")
def saludo(nombre):
return f"Hola, {nombre}"
if __name__ == "__main__":
app.run(debug=True)
7 Doble dinámica con int
Añade /numero/<int:n> que muestre el número y su doble. Si escribes letras, Flask debe
responder 404 (comportamiento esperado).
Ver solución
from flask import Flask
app = Flask(__name__)
@app.route("/numero/<int:n>")
def numero(n):
return f"{n} por 2 es {n * 2}"
if __name__ == "__main__":
app.run(debug=True)
8 Query string
Rende /buscar que lea request.args.get("q") y devuelva «Buscando: …» (con
valor por defecto vacío). Prueba /buscar?q=python.
Ver solución
from flask import Flask, request
app = Flask(__name__)
@app.route("/buscar")
def buscar():
q = request.args.get("q", "")
return f"Buscando: {q}"
if __name__ == "__main__":
app.run(debug=True)
9 POST y redirect
Ruta /guardar que acepte POST, lea request.form["nombre"] y redirija a
/usuario/<nombre>. Pruébalo con curl o un formulario simple en HTML.
Ver solución
from flask import Flask, request, redirect
app = Flask(__name__)
@app.route("/guardar", methods=["POST"])
def guardar():
nombre = request.form["nombre"]
return redirect(f"/usuario/{nombre}")
@app.route("/usuario/<nombre>")
def ver(nombre):
return f"Hola, {nombre}"
if __name__ == "__main__":
app.run(debug=True)
Sección C · Tu propia API
10 jsonify básico
Ruta /api/saludo que con jsonify devuelva
{"mensaje": "hola", "ok": true}. Compruébalo en el navegador (debe verse JSON).
Ver solución
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/api/saludo")
def saludo():
return jsonify({"mensaje": "hola", "ok": True})
11 API de tareas: listar
Con una lista global tareas = ["estudiar", "reposar"], crea /api/tareas (GET)
que devuelva la lista con jsonify.
Ver solución
from flask import Flask, jsonify
app = Flask(__name__)
tareas = ["estudiar", "reposar"]
@app.route("/api/tareas")
def listar():
return jsonify(tareas)
12 API de tareas: crear
Amplía la anterior con POST /api/tareas que lea el JSON con request.get_json()
(campo texto), lo añada a la lista y devuelva 201. Pruébalo con la extensión «REST Client» de
VS Code o con requests desde otro script.
Ver solución
from flask import Flask, jsonify, request
app = Flask(__name__)
tareas = ["estudiar", "reposar"]
@app.route("/api/tareas")
def listar():
return jsonify(tareas)
@app.route("/api/tareas", methods=["POST"])
def crear():
cuerpo = request.get_json()
tareas.append(cuerpo["texto"])
return jsonify(tareas), 201
13 Cliente de tu propia API
Con Flask corriendo, crea cliente.py que con requests liste las tareas y luego
añada una «hacer el examen» con POST. Muestra el resultado.
Ver solución
import requests
base = "http://127.0.0.1:5000/api/tareas"
print(requests.get(base).json())
r = requests.post(base, json={"texto": "hacer el examen"})
print(r.status_code, r.json())
14 Búsqueda en la API
Añade al servidor de tareas una ruta /api/tareas/buscar que lea ?texto= y
devuelva con un filtro (list comprehension) solo las tareas que lo contengan.
Ver solución
from flask import Flask, jsonify, request
app = Flask(__name__)
tareas = ["estudiar python", "reposar", "hacer el examen"]
@app.route("/api/tareas/buscar")
def buscar():
texto = request.args.get("texto", "").lower()
coinciden = [t for t in tareas if texto in t.lower()]
return jsonify(coinciden)
15 🏆 Proyecto integrador: «API REST de productos»
Crea api_productos.py: una API REST completa con Flask. Requisitos:
- Lista global de productos:
{"id", "nombre", "precio", "categoria"}(popúlala con 3+). GET /api/productos→ lista completa.POST /api/productos→ crea conrequest.get_json()y devuelve 201 y el producto creado.GET /api/productos/<int:pid>→ uno concreto o 404 conjsonify({"error": "no encontrado"}), 404.- Filtro opcional
GET /api/productos?categoria=…. DELETE /api/productos/<int:pid>→ borra y responde 200.- Cliente
cliente.pycon requests que liste, cree y borre un producto. - Docstring en las funciones y comentarios de la estructura.
Ver una solución posible
from flask import Flask, jsonify, request
app = Flask(__name__)
productos = [
{"id": 1, "nombre": "ratón", "precio": 12, "categoria": "periférico"},
{"id": 2, "nombre": "monitor", "precio": 150, "categoria": "pantalla"},
{"id": 3, "nombre": "teclado", "precio": 45, "categoria": "periférico"},
]
def siguiente_id():
"""Devuelve el id siguiente al último existente."""
return max(p["id"] for p in productos) + 1
@app.route("/api/productos")
def listar():
"""Lista productos (opcional: filtro por ?categoria=...)."""
categoria = request.args.get("categoria")
if categoria:
return jsonify([p for p in productos if p["categoria"] == categoria])
return jsonify(productos)
@app.route("/api/productos", methods=["POST"])
def crear():
"""Crea un producto desde el JSON enviado."""
datos = request.get_json()
nuevo = {"id": siguiente_id(), **datos}
productos.append(nuevo)
return jsonify(nuevo), 201
@app.route("/api/productos/<int:pid>")
def detalle(pid):
"""Devuelve un producto por id o 404."""
for p in productos:
if p["id"] == pid:
return jsonify(p)
return jsonify({"error": "no encontrado"}), 404
@app.route("/api/productos/<int:pid>", methods=["DELETE"])
def borrar(pid):
"""Borra un producto por id."""
global productos
productos = [p for p in productos if p["id"] != pid]
return jsonify({"ok": True})
if __name__ == "__main__":
app.run(debug=True)
📝 Examen del Bloque 7
Dos partes: un test teórico autoevaluable (mínimo 70 %) y un reto práctico de autocorrección con laboratorio.
Parte B · Reto «mini API»
Crea mini_api.py con Flask: una API mínima pero completa. Valida tú mismo/a estos criterios.
Mínimo para aprobar: 6 de 8 casillas.
Cumplidos: 0 / 8