Este tutorial adapta pedagógicamente el Día 51. Las secciones 📝 Ejercicio original del curso provienen de los archivos del curso (lecciones 374 a 378). Las soluciones oficiales (para X y para el clon Y) están en los gists enlazados en cada paso. Los retos 🟢🟡🟠🔴, la medición directa de la sección 3, el proyecto extra y el proyecto integrador final son creaciones de esta adaptación. Las correcciones al material original aparecen como 🛠️ Nota técnica.
Nota sobre las verificaciones: el 26 de septiembre de 2026, con Python 3.14, requests 2.34, Selenium 4.49 y Chrome 153, se comprobó: que speedtest.net carga y muestra el botón GO, que los selectores de las soluciones clásicas ya no existen tras el rediseño del sitio, y una medición de velocidad de descarga con
requests. No se crearon cuentas en X ni en Y, ni se publicó nada.
🏫 Para el aula: publicar en una red social exige cumplir la edad mínima de la plataforma y lo que se publica es público y permanente. En clase, el "reclamo" se enviará al clon de práctica Y (con la cuenta del docente) o, mejor aún, se redactará como una petición formal y se enviará al servidor de correo local (Día 47).
Tabla de contenidos
- Antes de empezar: reclamar con datos
- Paso 1: la cuenta y las velocidades prometidas
- Paso 2: organizar el bot en una clase
- Paso 3: medir la velocidad de Internet
- Paso 4: publicar el reclamo
- 🚀 Proyecto del día: el bot de reclamos por la velocidad de Internet
- 🌟 Proyecto extra: el observatorio de conectividad de la sala de informática
- 🏆 Proyecto integrador final del bloque de scraping y automatización (Días 45 a 51)
- 🧩 Comprueba lo aprendido
- 🤖 Programar con apoyo de IA
- 🛠️ Correcciones técnicas al material original
- 📋 Matriz de trazabilidad
- 📦 Evidencias de aprendizaje del Día 51
0. Antes de empezar: reclamar con datos
(Fuente: 374 - Day 51 Goals- what you will make by the end of the day.md)
A la profesora no le gusta llamar a servicio al cliente. Pero hay problemas por los que vale la pena reclamar. Un usuario de Comcast (Xfinity, en Estados Unidos) pagaba por cierta velocidad de Internet y, al medirla, recibía mucho menos. Creó un bot que medía su velocidad y, cuando era baja, publicaba automáticamente en Twitter (hoy X) un mensaje dirigido a Comcast: "¿Por qué mi Internet va a 23 de bajada y 5 de subida si pago 150 de bajada y 10 de subida?".
¿Por qué en una red social? Porque es público: las grandes empresas vigilan sus redes y muchas veces responden allí mejor que por teléfono. La profesora cita ejemplos de la aerolínea JetBlue, que compensó a un pasajero en pocos mensajes, y un artículo de la revista Time sobre la eficacia de reclamar en Twitter.
El plan del curso:
- Con Selenium, abrir
speedtest.net, presionar GO y leer la velocidad de bajada y de subida (la prueba tarda entre medio minuto y dos minutos). - Compararlas con las velocidades prometidas en el contrato.
- Publicar un mensaje al proveedor de Internet.
Lo nuevo hoy: organizar un bot de Selenium con varios pasos dentro de una clase (Días 16 a 20) y esperar procesos largos.
Prerequisitos: clases y objetos (Días 16 a 20), Selenium con esperas (Días 48 y 49).
📖 Contexto colombiano (adaptación; no es asesoría legal)
En Colombia, los usuarios de Internet tienen derechos definidos en el régimen de protección de usuarios de la Comisión de Regulación de Comunicaciones (CRC). El camino formal es presentar una PQR (petición, queja o reclamo) al operador; si la respuesta no te satisface, puedes acudir a la Superintendencia de Industria y Comercio (SIC). Un reclamo con datos medidos (fechas, horas y velocidades) es mucho más sólido que un "Internet está lento".
1. Paso 1: la cuenta y las velocidades prometidas
(Fuente: 375-step-1-setup-your-account-on-x-or-on-y.md)
📝 Contenido original del curso
- Cuenta: en X (antes Twitter) o en Y, un clon de práctica creado por el curso para no usar una red social real (
app.100daysofpython.dev). En X, se recomienda una cuenta nueva y no la personal. - Velocidades garantizadas: búscalas en tu contrato, o usa un ejemplo (150 Mbps de bajada y 10 Mbps de subida).
- Constantes al inicio del programa:
PROMISED_DOWN = 150
PROMISED_UP = 10
TWITTER_EMAIL = "..."
TWITTER_PASSWORD = "..."Con el servicio de práctica Y: PROMISED_DOWN = 1000, PROMISED_UP = 1000, más Y_EMAIL, Y_PASSWORD (se copia del panel de administración de Y) y Y_LOGIN_URL.
🛠️ Nota técnica: el ejemplo del curso escribe el correo y la contraseña como constantes en el código. Como en los Días 35 y 47, van en un archivo
.env. Solo las velocidades prometidas, que no son secretas, pueden quedar como constantes.
📖 Unidades: megabits no son megabytes
| Unidad | Qué mide | Ejemplo |
|---|---|---|
| Mbps (megabits por segundo) | Velocidad de conexión (así la venden los operadores) | "Plan de 100 Mbps" |
| MB/s (megabytes por segundo) | Velocidad de descarga que muestran muchos programas | 100 Mbps ≈ 12,5 MB/s |
1 byte = 8 bits. Por eso una descarga "de 12 MB/s" en un plan de 100 Mbps es normal.
🛠️ Nota técnica: la transcripción del video dice que el usuario de Comcast debía recibir "150 kilobits de bajada y 10 de subida". Son megabits (Mbps), como aparece en el paso 1 del curso. 150 kilobits por segundo sería una velocidad lentísima.
🔍 Antes de programar
- ¿Qué velocidad te prometió tu operador? ¿El contrato dice "hasta" o "mínimo garantizado"? ¿Qué diferencia hay?
- ¿Qué datos de este proyecto son secretos y cuáles no?
2. Paso 2: organizar el bot en una clase
(Fuente: 376-step-2-create-a-class.md)
🎯 Objetivo de aprendizaje
Estructurar un programa de varios pasos como una clase con atributos y métodos.
📝 Ejercicio original del curso: paso 2
"Como el bot tiene varios pasos, es más fácil organizar el código con una clase":
- Crear la clase
InternetSpeedTwitterBot. - En
__init__, crear eldriverde Selenium y dos atributos:downyup. - Crear dos métodos:
get_internet_speed()ytweet_at_provider(). - Fuera de la clase, crear el objeto y llamar los dos métodos en orden.
Pista del curso: repasa los Días 16 a 20 si no recuerdas cómo crear clases.
📖 Explicación conceptual
El esqueleto que describe el curso:
class InternetSpeedTwitterBot:
def __init__(self):
self.driver = ... # el navegador
self.down = 0 # se llenará al medir
self.up = 0
def get_internet_speed(self):
... # mide y guarda en self.down y self.up
def tweet_at_provider(self):
... # usa self.down y self.up para escribir el mensaje
bot = InternetSpeedTwitterBot()
bot.get_internet_speed()
bot.tweet_at_provider()¿Por qué una clase? El driver y las velocidades se comparten entre los dos métodos. Como atributos (self.driver, self.down), ambos métodos pueden usarlos sin pasarlos como parámetros. Es la misma idea que el Snake o el Scoreboard de los Días 20 a 24: un objeto que guarda su estado y sabe hacer ciertas acciones.
⚠️ Errores frecuentes
| Error | Qué ocurre | Solución |
|---|---|---|
Olvidar self en un método |
TypeError: ... takes 0 positional arguments but 1 was given |
Todo método recibe self como primer parámetro |
down = ... en lugar de self.down = ... dentro del método |
La variable desaparece al terminar el método | Guarda en self. lo que otros métodos necesiten |
Llamar tweet_at_provider() antes de medir |
Publica "0 de bajada, 0 de subida" | Respeta el orden, o haz que el método compruebe que hay mediciones |
Retos adicionales (creados para esta adaptación)
- 🟢 Reto 1: agrega un método
cerrar()que hagaself.driver.quit()y llámalo al final. - 🟡 Reto 2: pasa las velocidades prometidas como parámetros de
__init__(InternetSpeedTwitterBot(promised_down=150, promised_up=10)) en lugar de constantes globales. - 🟠 Reto 3: agrega un método
es_lenta()que devuelvaTruesi alguna velocidad medida está por debajo de un porcentaje de lo prometido (por ejemplo, el 80 %), y publica solo en ese caso. - 🔴 Reto 4 — Pensamiento computacional: separa la medición y la publicación en dos clases distintas (
MedidorVelocidadyReclamante). ¿Qué ganas? (Pista: podrías cambiar la forma de medir o el lugar donde reclamas sin tocar la otra clase.)
3. Paso 3: medir la velocidad de Internet
(Fuente: 377-step-3-get-internet-speeds.md)
📝 Ejercicio original del curso: paso 3
- Medir a mano en
speedtest.netla velocidad de bajada y de subida. - Con Selenium y Python, obtener el mismo resultado impreso en la consola.
Pistas del curso: abrir la página con Selenium y ver qué pasos debe seguir el bot; según tu velocidad, esperar 60 a 180 segundos a los resultados; usar XPath si no encuentras cómo apuntar a un elemento.
🛠️ Nota técnica (verificado el 26/09/2026): speedtest.net fue rediseñado. Los selectores de las soluciones clásicas (
.start-button apara el botón y.download-speed/.upload-speedpara los resultados) ya no existen en la página. El botón GO ahora es unbuttoncuyo texto está en unh1, con clases generadas automáticamente (por ejemplo,css-z9cxlb), que pueden cambiar con cada nueva versión del sitio. Además, el aviso de cookies aparece en el HTML pero no siempre se puede hacer clic en él. Lecciones:
- No uses clases con aspecto aleatorio como selector: son inestables.
- Prefiere lo que un humano reconoce, por ejemplo el texto del botón:
(By.XPATH, "//button[.//*[normalize-space(text())='GO']]").- Espera los resultados con
WebDriverWaity una condición (que el texto del resultado sea un número), no con unsleep(180)fijo.
📖 Medición directa sin automatizar un sitio ajeno (adaptación)
Selenium sobre speedtest.net depende del diseño de un sitio que no controlamos. Otra forma de medir es descargar un archivo de tamaño conocido y cronometrar cuánto tarda, algo que ya sabes hacer con requests:
import time
import requests
URL_PRUEBA = "https://speed.cloudflare.com/__down?bytes=10000000" # 10 000 000 bytes (10 MB)
inicio = time.perf_counter()
respuesta = requests.get(URL_PRUEBA, timeout=60)
segundos = time.perf_counter() - inicio
bytes_recibidos = len(respuesta.content)
mbps = bytes_recibidos * 8 / segundos / 1_000_000 # bytes -> bits -> megabits por segundo
print(f"Bajada aproximada: {mbps:.1f} Mbps")(Verificado: tres mediciones seguidas dieron 104,3, 102,4 y 96,1 Mbps.)
time.perf_counter()es un cronómetro de alta precisión.- La fórmula convierte bytes en bits (× 8) y bits por segundo en megabits por segundo (÷ 1 000 000).
- Es una medición aproximada (incluye el tiempo de conexión y usa una sola descarga), pero entiendes cada paso. Mide solo la bajada; la subida requeriría enviar datos.
📖 ¿Una medición es una prueba? (adaptación)
La velocidad cambia según la hora (en la noche hay más usuarios), la conexión (cable o wifi), la distancia al router y los demás dispositivos conectados. Un reclamo serio necesita varias mediciones, en distintas horas, preferiblemente por cable, registradas con fecha y hora. Un bot es ideal para eso: puede medir cada hora durante una semana sin cansarse.
⚠️ Errores frecuentes
| Error | Causa | Solución |
|---|---|---|
NoSuchElementException al buscar el botón |
Selectores de un tutorial viejo | Inspecciona el sitio hoy; usa el texto visible |
| Resultados vacíos o "—" | Se leyeron antes de que terminara la prueba | Espera con una condición |
ElementClickInterceptedException |
El aviso de cookies tapa el botón | Ciérralo primero o espera a que desaparezca |
| Confundir Mbps con MB/s | Te parece que la conexión es 8 veces más lenta | Revisa las unidades |
timeout muy corto en requests |
La descarga se interrumpe con conexiones lentas | Aumenta el tiempo o descarga menos bytes |
Retos adicionales (creados para esta adaptación)
- 🟢 Reto 1: repite la medición directa 5 veces e imprime el promedio, el mínimo y el máximo.
- 🟡 Reto 2: guarda cada medición con fecha y hora en
mediciones.csv. - 🟠 Reto 3: mide por wifi y por cable (si puedes) a la misma hora, y compara. Escribe una conclusión con los datos.
- 🔴 Reto 4: intenta el paso 3 con Selenium en speedtest.net usando selectores por texto. Documenta en un informe qué selectores funcionaron, cuáles no y por qué. (Hazlo pocas veces: cada prueba consume datos.)
4. Paso 4: publicar el reclamo
(Fuente: 378-step-4-building-an-x-bot-to-tweet-at-your-internet-provider.md)
📝 Ejercicio original del curso: paso 4
- Iniciar sesión y publicar a mano en X (o en Y) para estudiar qué selectores, ids, clases o XPath usar.
- Con Python y Selenium, repetir el proceso: iniciar sesión, redactar el mensaje con las velocidades medidas y las prometidas, y publicarlo.
- Si no quieres mencionar (@) a tu proveedor, basta un mensaje simple con las velocidades.
- Advertencia del curso: si inicias sesión en X muchas veces seguidas, te pedirá un reCAPTCHA o un código por correo para demostrar que eres humano. Hay que completarlo a mano para seguir probando.
📖 Un reclamo responsable (adaptación)
Un mensaje público sobre una empresa debe ser verdadero, respetuoso y verificable. Compara:
- ❌ "¡@Operador son unos estafadores, Internet nunca sirve!"
- ✅ "@Operador Entre el 1 y el 7 de octubre medí mi conexión 42 veces por cable: promedio 38 Mbps de bajada (plan contratado: 100 Mbps). Solicito revisión. Radicado PQR n.º …"
El segundo es más difícil de ignorar porque tiene datos. Además, antes de publicar, el camino formal es la PQR al operador (sección 0).
🏫 Alternativa para el aula
En lugar de publicar, el método reclamar() puede:
- Redactar una PQR formal con los datos del CSV de mediciones (fecha, promedio, mínimo, porcentaje respecto a lo contratado).
- Guardarla como
pqr_{fecha}.txty enviarla al servidor de correo local (Día 47), como si fuera el correo de atención al cliente. - Si el docente tiene una cuenta en Y, publicarla allí como demostración.
⚠️ Errores frecuentes
| Error | Causa | Solución |
|---|---|---|
El mensaje se publica con 0 Mbps |
Se llamó al método de publicar sin medir antes | Verifica que hay mediciones |
| Mensaje demasiado largo | Las redes limitan los caracteres | Calcula len(mensaje) antes de publicar |
| Bloqueo por iniciar sesión muchas veces | Defensa contra bots | Prueba con el clon Y o reutiliza el perfil (Día 49) |
| Números con muchos decimales ("38.4729 Mbps") | Formato por defecto | f"{valor:.1f}" |
5. 🚀 Proyecto del día: el bot de reclamos por la velocidad de Internet
(Integra los pasos 1 a 4 del curso: 375 a 378.)
Problema
Pagas por una velocidad de Internet y sospechas que recibes menos, pero no tienes pruebas.
Contexto
Una familia que estudia y trabaja desde casa necesita saber si el servicio cumple lo que paga, y reclamar de forma efectiva si no.
Objetivo
Un bot organizado en una clase que mida la velocidad, la compare con lo prometido y, si está por debajo, genere y envíe un reclamo.
Requisitos funcionales
- Clase con
__init__, un método para medir y un método para reclamar (estructura del curso). - Medición con Selenium en speedtest.net o con la medición directa de la sección 3.
- Comparación con las velocidades prometidas y un umbral (por ejemplo, 80 %).
- Registro de cada medición en
mediciones.csv. - Reclamo redactado con datos y enviado al destino elegido (Y, servidor local o archivo).
Restricciones técnicas
- Credenciales en
.env. - Esperas con condiciones, no
sleeplargos fijos. - Mensajes con números redondeados y unidades correctas.
Planificación
- Comprender. 2. Entradas: velocidades prometidas y umbral. 3. Procesos: medir → registrar → comparar → redactar → enviar. 4. Salidas: CSV y reclamo. 5. Dividir en métodos. 6. Diseñar la clase en papel (atributos y métodos). 7. Implementar. 8. Probar. 9. Corregir. 10. Mejorar.
Construcción paso a paso
- Versión 1: la clase vacía con los métodos que solo imprimen "midiendo…" y "reclamando…".
- Versión 2: medición real y registro en CSV.
- Versión 3: comparación con el umbral.
- Versión 4: redacción del reclamo con los datos.
- Versión final: envío al destino elegido y cierre ordenado del navegador.
Pruebas
| Caso | Datos | Resultado esperado |
|---|---|---|
| Velocidad suficiente | Medida 95, prometida 100, umbral 80 % | No hay reclamo; se registra la medición |
| Velocidad baja | Medida 40, prometida 100 | Se genera y envía el reclamo |
| Error de red al medir | Sin conexión | Mensaje claro; no se envía un reclamo con 0 Mbps |
| Mensaje | — | Menos de 280 caracteres, con unidades y un decimal |
(Para probar sin depender de tu conexión real, agrega un parámetro que permita inyectar una medición falsa: así pruebas la lógica de comparación en segundos.)
🧪 Prueba y depuración
class InternetSpeedTwitterBot:
def __init__(self):
driver = webdriver.Chrome()
self.up = 0
self.down = 0
def get_internet_speed():
self.driver.get("https://www.speedtest.net/")
time.sleep(180)
self.down = self.driver.find_element(By.CLASS_NAME, "download-speed").text
def tweet_at_provider(self):
mensaje = f"Mi Internet va a {self.down}down/{self.up}up cuando pago {PROMISED_DOWN}down/{PROMISED_UP}up"Encuentra cinco problemas (atributo sin self, método sin self, espera fija, selector que ya no existe, velocidad como texto que no se compara ni se valida).
⭐ Extensión del proyecto
- Ejecutar el bot cada hora durante una semana (Programador de tareas o PythonAnywhere) y generar un informe con pandas (promedio por franja horaria).
- Hacer una gráfica de las mediciones y adjuntarla al reclamo.
- Detectar la hora del día con peor servicio.
6. 🌟 Proyecto extra: el observatorio de conectividad de la sala de informática
(Proyecto creado para esta adaptación; no existe en el curso original.)
Situación
En la sala de informática, algunas clases "se caen" por Internet lento. La coordinación quiere datos antes de pedir una mejora a la Secretaría de Educación.
Requisitos
- Un programa (con autorización del docente o del área de sistemas) que mida la velocidad de descarga con el método directo cada 15 minutos durante una jornada, usando pocos datos (por ejemplo, 5 MB por medición).
- Un CSV con fecha, hora, velocidad y un campo "clase en curso" (según el horario de la sala).
- Un informe con: velocidad promedio por bloque de clase, el peor bloque y el porcentaje de mediciones por debajo de un mínimo definido (por ejemplo, 10 Mbps).
- Una carta (texto generado por el programa) dirigida a la coordinación con los resultados.
Decisiones que debes tomar (y justificar)
- ¿Cuánto dato consume el observatorio en un día? ¿Es aceptable para la red del colegio?
- ¿Cómo evitas que la medición misma empeore la conexión de las clases?
- ¿Qué mínimo de velocidad es razonable para una clase? Investiga y cita tu fuente.
Casos de prueba
- Simula una desconexión: el programa registra "sin conexión" y sigue funcionando.
- Verifica la fórmula con un caso conocido: 10 000 000 bytes en 2 segundos = 40 Mbps.
7. 🏆 Proyecto integrador final del bloque de scraping y automatización (Días 45 a 51)
(Proyecto creado para esta adaptación. Se construyó por etapas desde el Día 45.)
Situación problema
En el bloque web (Días 41 a 44) cada estudiante construyó el sitio de un grupo del colegio. Mantenerlo y usarlo implica tareas repetitivas: revisar que los enlaces e imágenes funcionen, generar reportes, vigilar datos que cambian (como los cupos) y procesar inscripciones. Hacerlo a mano consume tiempo y provoca errores.
Contexto
La automatización con Python (scraping, Selenium, correo, reintentos) puede ahorrar ese trabajo, pero un bot mal diseñado puede saturar un servicio, violar normas o manejar mal datos personales. El reto es construir un asistente útil, confiable y responsable.
Pregunta orientadora
¿Cómo puede un programa ahorrar tiempo a un grupo escolar recolectando y organizando información de la web de forma legal, ética y confiable?
Objetivo
Construir el Asistente del Grupo: un programa en Python, organizado en clases, que analice, vigile y automatice el sitio web del grupo y genere reportes.
Requerimientos
Funcionales
- Lector (Día 45): analiza con Beautiful Soup las páginas del sitio y extrae encabezados, enlaces e imágenes.
- Reporte (Día 46): genera
reporte.htmlcon el resumen y las alertas de accesibilidad. - Vigilante (Día 47): registra un dato que cambia (cupos) y envía alertas por correo al servidor local, una sola vez por cambio de estado.
- Probador e inscriptor (Día 48): con Selenium, verifica enlaces e imágenes y llena el formulario con aspirantes ficticios desde un CSV.
- Confiabilidad (Día 49):
WebDriverWait,retry, resumen con contadores y verificación con conjuntos. - Responsabilidad (Día 50): modo simulación, límite de ritmo y
ETICA.md. - Organización (Día 51): todo el asistente organizado en clases (por ejemplo,
Lector,Vigilante,Inscriptor,Notificador) y unmain.pycon un menú para elegir la tarea.
No funcionales
- Configuración en
config.json(rutas, umbrales, destinatarios) y secretos en.env. - Mensajes claros para el usuario; ningún error sin manejar en los casos de prueba.
- Código comentado donde haga falta y un
README.mdque explique cómo instalar y ejecutar.
Restricciones
- Solo se automatizan: el sitio propio, sitios creados para practicar y, si se justifica, páginas públicas sin datos personales, respetando su
robots.txt. - Solo datos ficticios de personas.
- Correos únicamente al servidor local.
- Pausa mínima de 1 segundo entre acciones sobre cualquier sitio.
Etapas de desarrollo
| Etapa | Día | Producto |
|---|---|---|
| 1. Leer el sitio | 45 | lector.py, reporte_sitio.txt |
| 2. De datos a página | 46 | generar_reporte.py, reporte.html |
| 3. Vigilar y avisar | 47 | vigilante.py, historial_cupos.csv |
| 4. Automatizar la inscripción | 48 | inscripcion.html, inscriptor.py |
| 5. Asistente confiable | 49 | Esperas, retry, resumen y verificación |
| 6. Asistente responsable | 50 | Modo simulación, límite de ritmo, ETICA.md |
| 7. Integración | 51 | Clases, main.py con menú, config.json, README.md |
Entregables
- Carpeta del proyecto: el sitio web, el código,
config.json,.env.ejemplo(sin datos reales),README.mdyETICA.md. reporte.htmlgenerado y capturas de las alertas en el servidor local.- Un informe de pruebas con la tabla de casos y sus resultados.
- Un cuaderno de errores del bloque (al menos 8 errores con causa y solución).
- Una demostración de 5 minutos: ejecutar cada opción del menú y explicar una decisión de diseño (por qué una clase, por qué un reintento, por qué un límite).
Casos de prueba mínimos
| # | Caso | Resultado esperado |
|---|---|---|
| 1 | Página con una imagen sin alt |
Aparece en las alertas del reporte |
| 2 | Enlace interno roto | El probador lo marca con ✗ |
| 3 | Cupos 12 → 4 → 4 → 0 | Dos alertas (pocos cupos y cerrado), no tres |
| 4 | CSV con 5 aspirantes y fallos simulados al 30 % | Los 5 inscritos; la verificación coincide |
| 5 | MODO_PRUEBA = True |
Ningún correo ni formulario enviado; se muestra lo que se haría |
| 6 | Falta una variable en .env |
Mensaje claro, sin KeyError |
| 7 | Salida redirigida a un archivo en Windows | Sin UnicodeEncodeError |
| 8 | Ejecutar dos veces seguidas | Sin duplicados ni ventanas de Chrome abiertas de más |
Criterios técnicos de funcionamiento
| Criterio | Nivel básico | Nivel alto |
|---|---|---|
| Extracción de datos | Obtiene los datos con selectores que funcionan | Selectores robustos; maneja None y listas vacías; explica por qué eligió cada selector |
| Automatización | El bot completa el recorrido | Esperas con condiciones, reintentos, verificación del resultado |
| Organización | Funciones separadas | Clases con responsabilidades claras; configuración fuera del código |
| Manejo de errores | Atrapa las excepciones más comunes | Mensajes claros, registro de errores, sin estados inconsistentes |
| Pruebas | Prueba a mano los casos principales | Tabla de casos completa; funciones puras probadas con assert |
| Responsabilidad | Usa solo sitios permitidos | Modo simulación, límite de ritmo, ETICA.md argumentado, datos ficticios |
| Documentación | Hay un README.md |
Cualquier compañero puede instalar y ejecutar el proyecto siguiendo el README |
Posibles extensiones
- Días 52 y 53 del curso: más automatización y un proyecto final de scraping con volcado a un formulario.
- Programar el vigilante para que se ejecute solo cada día.
- Con Flask (Día 54 en adelante): convertir el reporte en una página servida por Python que se actualiza sola.
- Agregar al lector un análisis del contraste de colores del CSS (Día 44).
8. 🧩 Comprueba lo aprendido
Conceptuales
- ¿Por qué el curso organiza el bot en una clase? ¿Qué atributos comparten los métodos?
- ¿Qué diferencia hay entre Mbps y MB/s?
- ¿Por qué una sola medición no es una buena prueba para un reclamo?
- ¿Por qué las clases con nombres como
css-z9cxlbson malos selectores? - ¿Cuál es el camino formal para reclamar a un operador de Internet en Colombia?
Análisis de código
mediciones = [38.2, 41.0, 12.5, 40.3, 39.9]
prometida = 100
bajas = [m for m in mediciones if m < prometida * 0.8]
print(f"{len(bajas)} de {len(mediciones)} mediciones bajo el 80 % ({sum(mediciones)/len(mediciones):.1f} Mbps promedio)")¿Qué imprime?
Predicción
Si descargas 5 000 000 de bytes en 0,8 segundos, ¿cuántos Mbps midió el programa? Calcúlalo a mano y luego con Python.
Depuración
bot = InternetSpeedTwitterBot
bot.get_internet_speed()¿Qué error aparece y por qué?
Modificación
Cambia el mensaje del reclamo para que incluya el porcentaje de la velocidad prometida que realmente se recibe.
Creación
Escribe una clase Medicion con atributos fecha, bajada y subida, y un método cumple(prometida_bajada, prometida_subida, umbral=0.8). Crea una lista de 5 mediciones y cuenta cuántas cumplen.
9. 🤖 Programar con apoyo de IA
Ciclo de trabajo: formula el problema → inténtalo tú → consulta a la IA → lee y entiende → prueba → compara con la documentación oficial → modifica → explica con tus palabras.
Actividad 1 — Selectores que caducan. Pide a una IA el código de Selenium para leer el resultado de speedtest.net. Es probable que use .download-speed. Ejecútalo (una sola vez) y, si falla, explica por qué con la nota técnica de la sección 3.
Actividad 2 — Redacción de un reclamo. Dale a una IA tus datos de medición y pídele que redacte una PQR formal para tu operador. Revisa: ¿los números coinciden con tus datos?, ¿inventó algún dato (un número de contrato, una norma)?, ¿el tono es respetuoso? Corrige lo necesario: tú firmas el reclamo.
Actividad 3 — Verificar la normativa. Pregúntale a una IA qué entidad regula los derechos de los usuarios de Internet en Colombia y a dónde escalar un reclamo. Compara con las páginas oficiales de la CRC y de la SIC.
10. 🛠️ Correcciones técnicas al material original
| Archivo | Afirmación o comportamiento del curso | Observación | Tratamiento |
|---|---|---|---|
377 |
Leer los resultados de speedtest.net con Selenium (soluciones con .start-button a, .download-speed, .upload-speed) |
El sitio fue rediseñado: esos selectores ya no existen; el botón GO usa clases generadas automáticamente (verificado el 26/09/2026) | Nota técnica de la sección 3; medición directa como alternativa |
377 |
Esperar 60 a 180 segundos | Una espera fija es lenta o insuficiente; mejor WebDriverWait con una condición |
Nota técnica de la sección 3 |
374 |
"150 kilobits de bajada y 10 de subida" (transcripción) | Son megabits por segundo, como indica el paso 1 | Nota técnica de la sección 1 |
375 |
Correo y contraseña como constantes en el código | Deben ir en .env (Días 35 y 47) |
Nota técnica de la sección 1 |
376 |
Métodos llamados init() en el texto |
El constructor de Python es __init__ (con dos guiones bajos a cada lado) |
Sección 2 |
374 |
Reclamar en X a la empresa | Aplica a Estados Unidos; en Colombia existe el camino formal de PQR y SIC. Además, publicar en redes exige la edad mínima de la plataforma | Contexto de la sección 0; sección 4 |
11. 📋 Matriz de trazabilidad
| Contenido original | Archivo de origen | Concepto desarrollado | Ejercicio original | Reto adicional | Proyecto relacionado |
|---|---|---|---|---|---|
| Reclamar a servicio al cliente, caso Comcast, reclamos públicos en Twitter, JetBlue, artículo de Time, speedtest.net, plan del bot | 374 |
Sección 0 | — | Contexto colombiano | Secciones 5 y 6 |
Cuenta en X o en el clon Y, velocidades garantizadas del contrato, constantes PROMISED_DOWN y PROMISED_UP, datos de acceso a Y |
375 |
Sección 1 | Paso 1 | Unidades Mbps y MB/s | Sección 5 |
Clase InternetSpeedTwitterBot, __init__ con driver, down y up, métodos get_internet_speed() y tweet_at_provider(), repaso de los Días 16 a 20 |
376 |
Sección 2 | Paso 2 | Retos 1–4 de la sección 2 | Secciones 5 y 7 |
| Medir a mano y con Selenium en speedtest.net, espera de 60 a 180 s, XPath | 377 |
Sección 3 | Paso 3 | Retos 1–4 de la sección 3 | Secciones 5 y 6 |
| Estudiar el proceso a mano, iniciar sesión, redactar y publicar, mensaje sin mención, reCAPTCHA | 378 |
Sección 4 | Paso 4 | Reclamo responsable; alternativa para el aula | Sección 5 |
| (No existe en el curso original) | — | Sección 3 | — | Medición directa con requests |
Secciones 5 y 6 |
| (No existe en el curso original) | — | — | — | Observatorio de conectividad | Sección 6 |
| (No existe en el curso original) | — | — | — | Asistente del Grupo: proyecto integrador final | Sección 7 |
12. 📦 Evidencias de aprendizaje del Día 51
- La clase
InternetSpeedTwitterBot(o tu versión en dos clases) con sus métodos. mediciones.csvcon al menos 5 mediciones y su resumen (promedio, mínimo, máximo).- El informe del Reto 4 de la sección 3 sobre los selectores de speedtest.net.
- El reclamo redactado con datos (PQR) y la captura de su envío al servidor local o a Y.
- El observatorio de conectividad (proyecto extra) con la carta a la coordinación.
- El proyecto integrador final con todos sus entregables.
- Cuaderno de errores: métodos sin
self, atributos sinself., esperas fijas, selectores generados automáticamente, Mbps contra MB/s, reclamos con 0 Mbps, clase sin instanciar.