Saltar al contenido
Inicio 100 Días de Código en Python — Día 51: El bot que reclama por la velocidad de Internet

100 Días de Código en Python — Día 51: El bot que reclama por la velocidad de Internet

Nivel: Grado décimo Área: Tecnología e Informática Modalidad: Tutorial práctico Fuente: Curso "100 Days of Code: The Complete Python Pro Bootcamp" (Angela Yu, App Brewery). Contenido del Día 51 (nivel intermedio+).

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

  1. Antes de empezar: reclamar con datos
  2. Paso 1: la cuenta y las velocidades prometidas
  3. Paso 2: organizar el bot en una clase
  4. Paso 3: medir la velocidad de Internet
  5. Paso 4: publicar el reclamo
  6. 🚀 Proyecto del día: el bot de reclamos por la velocidad de Internet
  7. 🌟 Proyecto extra: el observatorio de conectividad de la sala de informática
  8. 🏆 Proyecto integrador final del bloque de scraping y automatización (Días 45 a 51)
  9. 🧩 Comprueba lo aprendido
  10. 🤖 Programar con apoyo de IA
  11. 🛠️ Correcciones técnicas al material original
  12. 📋 Matriz de trazabilidad
  13. 📦 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:

  1. 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).
  2. Compararlas con las velocidades prometidas en el contrato.
  3. 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).

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

  1. 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.
  2. Velocidades garantizadas: búscalas en tu contrato, o usa un ejemplo (150 Mbps de bajada y 10 Mbps de subida).
  3. Constantes al inicio del programa:
Python
   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":

  1. Crear la clase InternetSpeedTwitterBot.
  2. En __init__, crear el driver de Selenium y dos atributos: down y up.
  3. Crear dos métodos: get_internet_speed() y tweet_at_provider().
  4. 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:

Python
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 haga self.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 devuelva True si 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 (MedidorVelocidad y Reclamante). ¿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

  1. Medir a mano en speedtest.net la velocidad de bajada y de subida.
  2. 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 a para el botón y .download-speed/.upload-speed para los resultados) ya no existen en la página. El botón GO ahora es un button cuyo texto está en un h1, 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:

  1. No uses clases con aspecto aleatorio como selector: son inestables.
  2. Prefiere lo que un humano reconoce, por ejemplo el texto del botón: (By.XPATH, "//button[.//*[normalize-space(text())='GO']]").
  3. Espera los resultados con WebDriverWait y una condición (que el texto del resultado sea un número), no con un sleep(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:

Python
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

  1. Iniciar sesión y publicar a mano en X (o en Y) para estudiar qué selectores, ids, clases o XPath usar.
  2. Con Python y Selenium, repetir el proceso: iniciar sesión, redactar el mensaje con las velocidades medidas y las prometidas, y publicarlo.
  3. Si no quieres mencionar (@) a tu proveedor, basta un mensaje simple con las velocidades.
  4. 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:

  1. Redactar una PQR formal con los datos del CSV de mediciones (fecha, promedio, mínimo, porcentaje respecto a lo contratado).
  2. Guardarla como pqr_{fecha}.txt y enviarla al servidor de correo local (Día 47), como si fuera el correo de atención al cliente.
  3. 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

  1. Clase con __init__, un método para medir y un método para reclamar (estructura del curso).
  2. Medición con Selenium en speedtest.net o con la medición directa de la sección 3.
  3. Comparación con las velocidades prometidas y un umbral (por ejemplo, 80 %).
  4. Registro de cada medición en mediciones.csv.
  5. Reclamo redactado con datos y enviado al destino elegido (Y, servidor local o archivo).

Restricciones técnicas

  • Credenciales en .env.
  • Esperas con condiciones, no sleep largos fijos.
  • Mensajes con números redondeados y unidades correctas.

Planificación

  1. 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

Python
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

  1. 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).
  2. Un CSV con fecha, hora, velocidad y un campo "clase en curso" (según el horario de la sala).
  3. 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).
  4. 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

  1. Lector (Día 45): analiza con Beautiful Soup las páginas del sitio y extrae encabezados, enlaces e imágenes.
  2. Reporte (Día 46): genera reporte.html con el resumen y las alertas de accesibilidad.
  3. 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.
  4. Probador e inscriptor (Día 48): con Selenium, verifica enlaces e imágenes y llena el formulario con aspirantes ficticios desde un CSV.
  5. Confiabilidad (Día 49): WebDriverWait, retry, resumen con contadores y verificación con conjuntos.
  6. Responsabilidad (Día 50): modo simulación, límite de ritmo y ETICA.md.
  7. Organización (Día 51): todo el asistente organizado en clases (por ejemplo, Lector, Vigilante, Inscriptor, Notificador) y un main.py con 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.md que 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

  1. Carpeta del proyecto: el sitio web, el código, config.json, .env.ejemplo (sin datos reales), README.md y ETICA.md.
  2. reporte.html generado y capturas de las alertas en el servidor local.
  3. Un informe de pruebas con la tabla de casos y sus resultados.
  4. Un cuaderno de errores del bloque (al menos 8 errores con causa y solución).
  5. 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

  1. ¿Por qué el curso organiza el bot en una clase? ¿Qué atributos comparten los métodos?
  2. ¿Qué diferencia hay entre Mbps y MB/s?
  3. ¿Por qué una sola medición no es una buena prueba para un reclamo?
  4. ¿Por qué las clases con nombres como css-z9cxlb son malos selectores?
  5. ¿Cuál es el camino formal para reclamar a un operador de Internet en Colombia?

Análisis de código

Python
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

Python
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.csv con 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 sin self., esperas fijas, selectores generados automáticamente, Mbps contra MB/s, reclamos con 0 Mbps, clase sin instanciar.