Saltar al contenido
Inicio 100 Días de Código en Python — Día 48: Selenium WebDriver y un bot que juega

100 Días de Código en Python — Día 48: Selenium WebDriver y un bot que juega

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 48 (nivel intermedio+).

Este tutorial adapta pedagógicamente el Día 48. Las secciones 📝 Ejercicio original del curso provienen de los archivos del curso (lecciones 350 a 357). Los retos 🟢🟡🟠🔴, el proyecto extra y el proyecto integrador 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 se ejecutaron los ejemplos con Python 3.14, Selenium 4.49 y Google Chrome 153 en modo headless (sin ventana), contra python.org, Wikipedia y la versión de Cookie Clicker que indica el curso. No se probó Amazon ni el formulario de práctica de la lección 355, cuya URL está en los recursos de Udemy y no en la carpeta.


Tabla de contenidos

  1. Antes de empezar: un robot que usa el navegador
  2. Instalar y configurar Selenium
  3. Encontrar elementos con Selenium
  4. Retos de extracción: eventos de Python y el contador de Wikipedia
  5. Interactuar: hacer clic, escribir y llenar formularios
  6. 🚀 Proyecto del día: el bot de Cookie Clicker
  7. 🌟 Proyecto extra: el probador automático de tu sitio web
  8. 🏆 Proyecto integrador del bloque (Días 45 a 51) — Etapa 4
  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 48

0. Antes de empezar: un robot que usa el navegador

(Fuente: 350 - Day 48 Goals- what you will make by the end of the day.md)

Con Beautiful Soup leemos páginas, pero no podemos usarlas: no podemos escribir en un campo, hacer clic en un botón ni encadenar acciones. Selenium WebDriver es una herramienta gratuita que controla un navegador real desde un programa: escribe, hace clic, se desplaza… "cualquier cosa que un humano pueda hacer en un sitio web". La profesora lo compara con un robot al que le das instrucciones, y Selenium es lo que le permite comunicarse con el navegador.

Es una de las herramientas más conocidas para probar sitios web automáticamente, y también sirve para automatizar tareas repetitivas, como pasar datos de una hoja de cálculo a un formulario en línea. El proyecto del día: un bot que juega Cookie Clicker por ti.

Beautiful Soup Selenium
Qué hace Analiza HTML que ya descargaste Controla un navegador de verdad
Ejecuta JavaScript No Sí
Puede hacer clic y escribir No Sí
Velocidad Muy rápido Más lento (abre un navegador)
Úsalo cuando… La información está en el HTML que llega del servidor La página se construye con JavaScript o necesitas interactuar

Prerequisitos: selectores CSS (Día 43), DevTools (Días 41 y 44), Beautiful Soup (Día 45), diccionarios (Día 9), time y ciclos while (Días 5 y 20).


1. Instalar y configurar Selenium

(Fuente: 351 - How to Install & Set Up Selenium.md)

🎯 Objetivo de aprendizaje

Instalar Selenium, abrir una página en Chrome controlado por Python, mantener el navegador abierto y cerrarlo.

📖 Explicación conceptual

Piezas del sistema:

  1. Chrome: el navegador que se va a controlar (el curso lo usa por sus herramientas para desarrolladores).
  2. El paquete selenium: código para hablar con muchos navegadores (Chrome, Firefox, Safari…).
  3. El driver (en este caso, ChromeDriver): el puente que traduce las órdenes de Selenium al navegador concreto. Cada navegador tiene su propio driver.
Python
from selenium import webdriver

chrome_options = webdriver.ChromeOptions()
chrome_options.add_experimental_option("detach", True)   # que Chrome NO se cierre al terminar el programa

driver = webdriver.Chrome(options=chrome_options)       # Chrome con C mayúscula: se crea un objeto
driver.get("https://www.python.org")

# driver.close()   # cierra solo la pestaña activa
# driver.quit()    # cierra todo el navegador (la profesora prefiere este)

Al ejecutarlo, Chrome muestra el aviso "Chrome está siendo controlado por software de prueba automatizado": ese es Selenium.

🛠️ Nota técnica: el video explica que en Mac aparece el mensaje "chromedriver no se puede abrir porque el desarrollador no puede ser verificado" y hay que permitirlo en Seguridad y Privacidad. Desde la versión 4.6, Selenium incluye Selenium Manager, que descarga automáticamente el driver correcto para tu versión de Chrome. En la verificación de esta guía (Selenium 4.49, Windows) no hubo que instalar ni configurar ChromeDriver a mano. Si aparece un error con el driver, primero actualiza Selenium (pip install -U selenium).

⚠️ Errores frecuentes

Error Causa Solución
El navegador se cierra apenas termina el programa Es el comportamiento por defecto Opción detach en True, o no llames quit() mientras pruebas
Muchas ventanas de Chrome abiertas Con detach, cada ejecución deja una ventana Usa driver.quit() cuando ya no la necesites
webdriver.chrome() TypeError o AttributeError: la clase es Chrome Mayúscula inicial
SessionNotCreatedException por versión Driver y navegador de versiones distintas Actualiza Selenium; Selenium Manager se encarga del resto

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: abre la página del colegio (o https://www.python.org), imprime driver.title y cierra el navegador.
  • 🟡 Reto 2: abre dos páginas en dos pestañas (investiga driver.switch_to.new_window("tab")) y usa close() en una. Verifica que la otra sigue abierta.
  • 🟠 Reto 3: investiga la opción --headless=new de ChromeOptions. ¿Qué cambia? ¿Para qué serviría un navegador sin ventana?
  • 🔴 Reto 4: escribe una función crear_driver(visible=True) que devuelva un driver configurado (visible o sin ventana) y úsala en todos los programas del día para no repetir código.

2. Encontrar elementos con Selenium

(Fuente: 352 - How to Find and Select Elements on a Website with Selenium.md)

🎯 Objetivo de aprendizaje

Localizar elementos con las estrategias de By y obtener su texto, atributos y otras propiedades.

📖 Explicación conceptual

Python
from selenium.webdriver.common.by import By

elemento = driver.find_element(By.ID, "submit")          # el primero que coincide
lista = driver.find_elements(By.CSS_SELECTOR, "li a")    # todos, en una lista (plural)
Estrategia Ejemplo Cuándo usarla
By.ID (By.ID, "submit") El elemento tiene un id (debería ser único)
By.NAME (By.NAME, "q") Campos de formulario: casi siempre tienen name
By.CLASS_NAME (By.CLASS_NAME, "a-price-whole") Una sola clase
By.CSS_SELECTOR (By.CSS_SELECTOR, ".documentation-widget a") El elemento no tiene nada único, pero su ubicación sí
By.XPATH (By.XPATH, '//*[@id="container"]/li[3]/a') Último recurso: una "ruta" desde la raíz del documento
By.LINK_TEXT (By.LINK_TEXT, "Content portals") Enlaces, por su texto visible

Del elemento a los datos:

Expresión Da
elemento.text El texto visible (es una propiedad, sin paréntesis)
elemento.get_attribute("placeholder") El valor de un atributo
elemento.tag_name El nombre de la etiqueta
elemento.size Alto y ancho en píxeles

Si imprimes el elemento directamente, no verás HTML sino algo como <selenium.webdriver.remote.webelement.WebElement ...>.

XPath, en palabras del curso, es como la ruta de un archivo (Usuarios/angela/desarrollo), pero para llegar a un elemento bajando por el árbol HTML. No hace falta escribirlo a mano: en DevTools, clic derecho sobre el elemento → Copy → Copy XPath. Como el XPath copiado trae comillas dobles, el texto de Python debe ir entre comillas simples.

¿Por qué Selenium "gana" en Amazon? La profesora obtiene el precio de Amazon en tres líneas: como Selenium maneja un navegador, envía todos los encabezados de un usuario real y ejecuta el JavaScript de la página. Beautiful Soup "se atasca" cuando el contenido se genera con JavaScript (React, Angular…) o tarda en cargar.

🔎 Ejemplo explicado (verificado en python.org, 26/09/2026)

Python
driver.get("https://www.python.org")

search_bar = driver.find_element(By.NAME, "q")
print(search_bar.tag_name)                          # input
print(search_bar.get_attribute("placeholder"))      # Search

button = driver.find_element(By.ID, "submit")
print(button.size)                                  # {'height': 40, 'width': 46}

documentation_link = driver.find_element(By.CSS_SELECTOR, ".documentation-widget a")
print(documentation_link.text)                      # docs.python.org

Los resultados coinciden con los del video.

⚠️ Errores frecuentes

Error Causa Solución
AttributeError: 'WebDriver' object has no attribute 'find_element_by_name' Tutoriales antiguos usan find_element_by_..., que se eliminó en Selenium 4 (verificado) find_element(By.NAME, ...)
NoSuchElementException El selector no coincide o el elemento aún no ha cargado Revisa en DevTools; espera a que cargue (Día 49: WebDriverWait)
elemento.text() TypeError: text no es un método elemento.text
Comillas del XPath en conflicto SyntaxError Envuelve el XPath en comillas simples
Iterar sobre find_element Es un solo elemento, no una lista Usa find_elements (en plural)

📝 Ejercicio original del curso: explorar python.org y Amazon

La lección muestra, en orden: el precio de la Instant Pot en Amazon con dos By.CLASS_NAME (a-price-whole y a-price-fraction, que "pueden cambiar en cualquier momento"); la barra de búsqueda de python.org por NAME; el botón por ID y su tamaño; el enlace a la documentación por CSS_SELECTOR; el enlace "Submit Website Bug" por XPATH; y la versión en plural find_elements.

🔍 Antes de programar

  • Para cada elemento, ¿qué atributo lo hace único? Si no hay ninguno, ¿qué contenedor sí lo es?
  • ¿Por qué el XPath copiado de DevTools es "frágil"? ¿Qué pasa si el sitio agrega un elemento antes?

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: en python.org, obtén el texto de todos los enlaces del menú principal con find_elements.
  • 🟡 Reto 2: localiza el mismo elemento de tres formas distintas (id, CSS y XPath) y verifica que las tres dan el mismo texto.
  • 🟠 Reto 3: compara un XPath "absoluto" copiado de DevTools con uno "relativo" que escribas tú (por ejemplo, //a[text()="Docs"]). Modifica el HTML en vivo con DevTools agregando un elemento arriba. ¿Cuál sigue funcionando?
  • 🔴 Reto 4 — Pensamiento computacional: escribe una tabla de decisión que ayude a un compañero a elegir la estrategia By adecuada según lo que tenga el elemento (id, name, clase, texto o nada). Pruébala con 5 elementos de una página.

3. Retos de extracción: eventos de Python y el contador de Wikipedia

(Fuentes: 353 - Challenge- Use Selenium to Scrape Website Data.md, 354 - Challenge- Use Selenium in a Blank Project & Scrape a Different Piece of Data.md)

📝 Ejercicio original del curso 1: los próximos eventos de python.org

  • Enunciado: extraer las 5 fechas y los 5 nombres de la sección Upcoming Events de python.org y construir un diccionario con esta forma:
Python
  {
      0: {"time": "2026-10-03", "name": "PyBay 2026"},
      1: {"time": "...", "name": "..."},
      ...
  }
  • Razonamiento de la profesora: la fecha está en un elemento time dentro de un li dentro de un ul, pero esa estructura se repite en la sección de noticias. Lo único que distingue la sección de eventos es un div con clase event-widget. Por eso: .event-widget time.
  • La trampa de los nombres: .event-widget a también atrapa el enlace "More" del encabezado de la sección. Solución: .event-widget li a, porque "More" no está dentro de un li.
  • Construcción del diccionario: un for n in range(len(event_times)) que agrega events[n] = {"time": ..., "name": ...} (también se puede con comprensión de diccionarios).

Verificado el 26/09/2026: ambos selectores devuelven 5 elementos (por ejemplo, 2026-10-03 y PyBay 2026). Tus eventos serán otros: dependen de la fecha.

📝 Ejercicio original del curso 2: el contador de artículos de Wikipedia

  • Enunciado: en un archivo nuevo, interaction.py, abrir la portada de Wikipedia en inglés e imprimir el número de artículos en inglés.
  • Solución del curso: el número está en un a sin id ni clase, dentro de un div con id="articlecount": driver.find_element(By.CSS_SELECTOR, "#articlecount a"). Como hay dos enlaces en ese div, find_element toma el primero.

🛠️ Nota técnica (verificado el 26/09/2026): Wikipedia cambió ese texto. Hoy dice "267,685 active editors · 7,245,031 articles in English": el primer enlace es el número de editores activos y el segundo, el de artículos. El selector del curso ya no da el dato que pide el reto. Es un ejemplo perfecto de por qué un programa debe verificar que el dato tiene sentido: "267 685 artículos" es demasiado poco para Wikipedia en inglés.

🔍 Antes de programar

  • ¿Qué tienen de único los elementos que buscas? ¿Dónde se repite la misma estructura en la página?
  • ¿Cómo compruebas que el número que extrajiste es el correcto y no otro número cercano?

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: corrige el reto de Wikipedia para que imprima el número de artículos (pista: find_elements y el índice correcto, o un selector más preciso).
  • 🟡 Reto 2: convierte el número "7,245,031" en un entero y calcula cuántos artículos habría que leer al día para leerlos todos en 80 años.
  • 🟠 Reto 3: abre la portada de Wikipedia en español (es.wikipedia.org), inspecciónala y extrae su número de artículos. ¿El selector es el mismo?
  • 🔴 Reto 4: en el diccionario de eventos, agrega la ciudad o el lugar de cada evento. ¿Está en la portada o tienes que hacer clic en cada evento? Diseña el recorrido antes de programarlo.

4. Interactuar: hacer clic, escribir y llenar formularios

(Fuente: 355 - How to Automate Filling Out Forms and Clicking Buttons with Selenium.md)

🎯 Objetivo de aprendizaje

Hacer clic en elementos, escribir texto, enviar teclas especiales y completar un formulario automáticamente.

📖 Explicación conceptual

Acción Código
Hacer clic elemento.click()
Encontrar un enlace por su texto driver.find_element(By.LINK_TEXT, "Content portals")
Escribir campo.send_keys("Python")
Presionar una tecla especial campo.send_keys(Keys.ENTER)

Las teclas especiales (Enter, Shift, Tab, Control…) están como constantes en la clase Keys:

Python
from selenium.webdriver.common.keys import Keys

🔎 Ejemplo explicado (verificado en Wikipedia, 26/09/2026)

Python
driver.get("https://en.wikipedia.org/wiki/Main_Page")

all_portals = driver.find_element(By.LINK_TEXT, "Content portals")
# all_portals.click()          # llevaría a la página de portales

search = driver.find_element(By.NAME, "search")
search.send_keys("Python", Keys.ENTER)   # escribe y presiona Enter
print(driver.title)                      # Python - Wikipedia

⚠️ Hallazgo de la verificación: la página de Wikipedia tiene dos campos con name="search" (uno para pantallas grandes y otro para pantallas pequeñas). Con una ventana de 1400 píxeles de ancho, find_element encuentra el visible y todo funciona. Con una ventana angosta (500 píxeles), encuentra el oculto y falla con ElementNotInteractableException (verificado). Es decir, ¡el mismo código puede funcionar en tu computador y fallar en el de un compañero! Solución: maximizar la ventana (driver.maximize_window()) o elegir entre find_elements(...) el que cumpla is_displayed().

⚠️ Errores frecuentes

Error Causa Solución
sendKeys, findElementByName Son nombres de Java o de versiones viejas; en Python se escribe con guiones bajos send_keys, find_element(By.NAME, ...)
NameError: name 'Keys' is not defined Falta la importación from selenium.webdriver.common.keys import Keys
ElementNotInteractableException El elemento existe pero está oculto o tapado Ventana más grande, esperar, o buscar el visible
ElementClickInterceptedException Otro elemento (una ventana emergente, un aviso de cookies) está encima Cerrar primero ese elemento
Probar un formulario en un sitio real Envías datos basura a un servicio de verdad Practica en sitios hechos para eso (el curso da uno) o en tu propio sitio

📝 Ejercicio original del curso: llenar un formulario de registro

  • Enunciado: en la página de práctica del curso (enlace en los recursos de Udemy), escribir automáticamente nombre, apellido y correo, y hacer clic en el botón de registro, hasta llegar a la página de "éxito".
  • Pistas de la solución: según la transcripción, los campos tienen los nombres (name) fname, lname y email (compruébalo en DevTools: las mayúsculas deben coincidir exactamente); el botón no tiene id ni nombre, pero es el único button dentro del form, así que sirve el selector CSS form button.
  • Por qué un sitio de práctica: la profesora no quiere que se envíen datos de prueba a un sitio real, que "recibiría mucho tráfico basura". Tampoco hace falta usar tu correo real.

🔍 Antes de programar

  • ¿En qué orden deben ocurrir las acciones?
  • ¿Cómo sabrá tu programa que el registro funcionó?
  • ¿Qué datos ficticios usarás? (Por ejemplo, un dominio .test, que nunca pertenece a nadie.)

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: en Wikipedia, busca tres términos seguidos (de una lista) e imprime el título de cada página resultante.
  • 🟡 Reto 2: después de buscar "Python", haz clic en el primer enlace del contenido del artículo e imprime el título de la nueva página.
  • 🟠 Reto 3 — El juego de Wikipedia: partiendo de un artículo al azar (Special:Random), haz clic siempre en el primer enlace del texto y registra el recorrido. ¿En cuántos pasos llegas al artículo "Philosophy"? Limita el recorrido a 20 pasos y espera 1 segundo entre clics.
  • 🔴 Reto 4: crea tu propio formulario HTML (Días 41 a 44) con action="gracias.html" y method="get", y un programa de Selenium que lo llene con los datos de un archivo CSV (5 filas), uno por uno, verificando cada vez que la URL de gracias.html contiene los datos enviados.

(Fuentes: 356 - The Cookie Clicker Project.md, 357-challenge-create-an-automated-game-playing-bot.md)

Problema

Cookie Clicker es un juego muy simple y, según muchos usuarios, muy adictivo: haces clic en una galleta gigante para "hornear" galletas y, con ellas, compras mejoras (un cursor que hace clic por ti, una abuela que hornea…). La profesora propone "jugar haciendo trampa": que un bot haga los clics y compre las mejoras.

Contexto

Antes de programar, juega unos cinco minutos como humano (recomendación del curso) para entender las reglas. Automatizar un juego para un solo jugador en tu computador no perjudica a nadie; en cambio, usar bots en juegos en línea con otras personas suele violar sus reglas y es injusto con los demás jugadores. Esa diferencia vale la pena discutirla.

Objetivo

Maximizar las galletas por segundo al cabo de 5 minutos de juego automático.

Requisitos funcionales (originales del curso)

  1. Abrir el juego (el curso indica la versión de github.com/ozh/cookieclicker; se juega en ozh.github.io/cookieclicker/).
  2. Cerrar automáticamente la selección de idioma del inicio.
  3. Hacer clic en la galleta lo más rápido posible.
  4. Cada 5 segundos, revisar las mejoras del panel derecho y comprar la más cara que se pueda pagar (por ejemplo, con 105 galletas se pueden pagar el cursor y la abuela, pero se compra la abuela, que es más cara).
  5. A los 5 minutos, detener el bot e imprimir las galletas por segundo.
  6. Opcional: cambiar el algoritmo (revisar cada 1 o cada 10 segundos, comprar todo lo que se pueda…) y comparar resultados con tus compañeros.

La profesora cree que lo más difícil será elegir la mejora más cara cada 5 segundos, porque las mejoras aparecen poco a poco. Pistas del curso: split(), strip(), replace() y cómo detener un ciclo while después de cierto tiempo.

Datos verificados del juego (26/09/2026)

  • Botones de idioma: langSelect-EN (inglés), langSelect-ES (español) y otros diez idiomas con el mismo patrón langSelect-....
  • La galleta: id="bigCookie".
  • El contador: id="cookies", con un texto como "28 cookies\nper second: 0".
  • Cada edificio: id="product0", product1…; su precio en productPrice0…; cuando se puede comprar, su atributo class incluye enabled.

Compruébalos tú en DevTools: los juegos también se actualizan.

Restricciones técnicas

  • Esperar a que la página cargue antes de buscar la galleta (WebDriverWait, se profundiza en el Día 49).
  • El tiempo se controla con time.time(), no contando clics.
  • Los precios pueden tener comas ("1,100"): hay que limpiarlos antes de convertirlos.

Planificación

  1. Comprender el juego jugándolo. 2. Entradas: la página. 3. Procesos: clic → cada 5 s leer galletas y precios → decidir → comprar. 4. Salida: galletas por segundo. 5. Dividir en funciones: leer_galletas(), leer_precios(), elegir_compra(), comprar(). 6. Diseñar el ciclo con dos relojes (5 segundos y 5 minutos). 7. Implementar. 8. Probar. 9. Corregir. 10. Mejorar el algoritmo.

Construcción paso a paso

  • Versión 1: abrir el juego, elegir el idioma y hacer 100 clics en la galleta.
  • Versión 2: un ciclo que hace clic durante 30 segundos (time.time()).
  • Versión 3: una función que devuelve las galletas actuales como int.
  • Versión 4: una función que devuelve un diccionario {precio: elemento} de los edificios que se pueden pagar. Pruébala por separado, imprimiendo el diccionario.
  • Versión final: compra cada 5 segundos, detente a los 5 minutos e imprime las galletas por segundo.

Pista: dos relojes en un solo ciclo

Python
import time

inicio = time.time()
siguiente_revision = time.time() + 5
while time.time() - inicio < 5 * 60:
    # hacer clic en la galleta
    if time.time() >= siguiente_revision:
        # revisar y comprar
        siguiente_revision = time.time() + 5

Pruebas

Prueba Resultado esperado
Convertir "1,100" en número 1100
Con 105 galletas y precios 15 (cursor) y 100 (abuela) Se elige la abuela
Con 10 galletas No se compra nada y no hay error
Duración total Aproximadamente 5 minutos
Sin la selección de idioma El bot la cierra solo

🧪 Prueba y depuración

Python
cookies = driver.find_element(By.ID, "cookies").text
if cookies > 100:
    driver.find_element(By.ID, "product1").click
precios = [int(p.text) for p in driver.find_elements(By.CSS_SELECTOR, "[id^=productPrice]")]

Encuentra cuatro problemas (tipo de dato del contador, texto con varias líneas, click sin paréntesis, precios vacíos o con comas).

⭐ Extensión del proyecto

  • Compara tres estrategias (la del curso, "comprar todo lo posible" y "comprar lo que más produce por galleta invertida") y presenta una tabla con los resultados.
  • Haz clic en las mejoras (upgrades) además de los edificios.
  • Registra en un CSV las galletas por segundo cada 30 segundos y grafica la evolución.

6. 🌟 Proyecto extra: el probador automático de tu sitio web

(Proyecto creado para esta adaptación; no existe en el curso original.)

Situación

Selenium nació para probar sitios web. Antes de publicar el sitio de tu grupo (bloque web), quieres comprobar automáticamente que funciona.

Requisitos

  1. Abrir con Selenium tu index.html local (driver.get("file:///C:/ruta/a/index.html")).
  2. Verificar que el title no sea "Document" y que haya exactamente un h1.
  3. Hacer clic en cada enlace interno y comprobar que la página destino carga (su título no está vacío), y volver (driver.back()).
  4. Verificar que todas las imágenes cargaron (pista: el atributo naturalWidth de una imagen rota es 0).
  5. Probar el sitio con dos tamaños de ventana (1400 y 400 píxeles de ancho) y tomar una captura de cada uno (driver.save_screenshot(...)).
  6. Imprimir un reporte de pruebas: ✓ o ✗ por cada verificación.

Decisiones que debes tomar (y justificar)

  • ¿Qué cuenta como "fallo"? ¿Una imagen sin alt es un fallo o una advertencia?
  • ¿Cómo evitas volver a probar el mismo enlace dos veces?

Casos de prueba

  • Rompe a propósito un enlace y una imagen: el reporte debe detectarlos.

7. 🏆 Proyecto integrador del bloque (Días 45 a 51) — Etapa 4

(Proyecto creado para esta adaptación. La situación problema está en el Día 45; la versión final, en el Día 51.)

Etapa 4 (hoy): automatizar la inscripción

  1. Agrega a tu sitio una página inscripcion.html con un formulario HTML (nombre, curso, correo, interés) con action="gracias.html" y method="get", y crea gracias.html.
  2. Escribe inscriptor.py, que lea aspirantes.csv (datos ficticios, al menos 5 filas) y, con Selenium, llene y envíe el formulario una vez por cada fila.
  3. Después de cada envío, verifica que la URL de gracias.html contenga el nombre enviado (los datos de un formulario GET viajan en la URL).
  4. Entregable: los dos HTML, inscriptor.py, el CSV y la salida de la consola.

Nota: sin un servidor, los datos no se guardan en ninguna parte; en el curso, los formularios reales se trabajan con Flask a partir del Día 60.


8. 🧩 Comprueba lo aprendido

Conceptuales

  1. ¿Qué puede hacer Selenium que Beautiful Soup no puede?
  2. ¿Qué papel cumple el driver del navegador?
  3. ¿Qué diferencia hay entre close() y quit()?
  4. ¿Cuándo usarías By.CSS_SELECTOR y cuándo By.XPATH?
  5. ¿Por qué un programa de Selenium puede funcionar en un computador y fallar en otro?

Análisis de código

Python
times = driver.find_elements(By.CSS_SELECTOR, ".event-widget time")
names = driver.find_elements(By.CSS_SELECTOR, ".event-widget li a")
events = {n: {"time": times[n].text, "name": names[n].text} for n in range(len(times))}

Explica qué hace cada línea. ¿Qué pasa si names tiene un elemento más que times?

Predicción

Si ejecutas driver.find_element(By.CSS_SELECTOR, ".event-widget a").text en python.org, ¿qué texto crees que obtendrás? ¿Por qué? (Compruébalo: en la verificación de esta guía el resultado confirmó la "trampa" que explica la profesora.)

Depuración

Python
from selenium import webdriver
driver = webdriver.Chrome()
driver.get("https://en.wikipedia.org/wiki/Main_Page")
search = driver.find_element_by_name("search")
search.sendKeys("Python" + Keys.ENTER)

Encuentra tres errores.

Modificación

Modifica el reto de los eventos para que guarde el diccionario en un archivo JSON (Día 30).

Creación

Crea un bot que abra quotes.toscrape.com, haga clic en "Next" hasta llegar a la última página e imprima cuántas citas hay en total. Espera 1 segundo entre páginas.


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 de Selenium → modifica → explica con tus palabras.

Actividad 1 — Código antiguo. Pide a una IA "un ejemplo de Selenium en Python para buscar en Wikipedia". Revisa si usa find_element_by_name o si pide descargar ChromeDriver a mano. Compara con la documentación actual de Selenium (selenium.dev) y actualiza el código.

Actividad 2 — Un error que depende del entorno. Describe a una IA el ElementNotInteractableException de la sección 4 sin mencionar el tamaño de la ventana. ¿Llega a la causa? ¿Qué preguntas te hace? Compara con el hallazgo de esta guía.

Actividad 3 — Estrategia de juego. Pide a una IA una estrategia "óptima" para Cookie Clicker. Implementa la idea y compárala con la del curso en 5 minutos de juego. La IA propone; los datos deciden.


10. 🛠️ Correcciones técnicas al material original

Archivo Afirmación o comportamiento del curso Observación Tratamiento
351 Permitir chromedriver en la configuración de seguridad de Mac Desde Selenium 4.6, Selenium Manager descarga el driver automáticamente; con Selenium 4.49 no fue necesario ningún paso manual (verificado) Nota técnica de la sección 1
354 #articlecount a da el número de artículos de Wikipedia Hoy el primer enlace es el de editores activos (267 685); el de artículos es el segundo (7 245 031), verificado el 26/09/2026 Nota técnica de la sección 3; Reto 1
355 Métodos findElementByName y sendKeys (así aparecen en la transcripción) En Selenium 4 para Python: find_element(By.NAME, ...) y send_keys; find_element_by_* ya no existe (verificado) Tabla de errores de las secciones 2 y 4
355 find_element(By.NAME, "search") en Wikipedia Hay dos campos con ese nombre; con ventana angosta se toma el oculto y falla con ElementNotInteractableException (verificado) Hallazgo de la sección 4
357 El juego está en github.com/ozh/cookieclicker Esa dirección es el repositorio del código; el juego se abre en ozh.github.io/cookieclicker/ (verificado) Sección 5
352 Clases de precio de Amazon (a-price-whole, a-price-fraction) La propia profesora advierte que cambian; además, las Condiciones de Uso de Amazon prohíben los robots (Día 47) Sección 2

11. 📋 Matriz de trazabilidad

Contenido original Archivo de origen Concepto desarrollado Ejercicio original Reto adicional Proyecto relacionado
Qué es Selenium WebDriver, límites de Beautiful Soup, automatizar el navegador, Cookie Clicker, formularios 350 Sección 0 — — Sección 5
Chrome, pip install selenium, webdriver.Chrome(), ChromeDriver como puente, aviso de Mac, detach, close() y quit() 351 Sección 1 Abrir amazon.com Retos 1–4 de la sección 1 Secciones 5, 6 y 7
find_element, By.CLASS_NAME, .text, By.NAME, get_attribute, tag_name, By.ID, .size, By.CSS_SELECTOR, By.XPATH, find_elements, JavaScript y Beautiful Soup 352 Sección 2 Explorar python.org y Amazon Retos 1–4 de la sección 2 Secciones 5 y 6
Eventos de python.org, .event-widget time, .event-widget li a, diccionario anidado 353 Sección 3 Diccionario de eventos Retos 1–4 de la sección 3 Sección 6
Proyecto en blanco, interaction.py, #articlecount a 354 Sección 3 Contador de Wikipedia Retos 1–4 de la sección 3 —
click(), By.LINK_TEXT, send_keys, Keys.ENTER, formulario fName, lName, email, form button, no usar sitios reales 355 Sección 4 Formulario de registro Retos 1–4 de la sección 4 Secciones 6 y 7
Cookie Clicker: jugar primero, bot, mejoras, galletas por segundo, 5 minutos 356 Sección 5 Proyecto del día (presentación) — Sección 5
Instrucciones: idioma, clic continuo, comprar la mejora más cara cada 5 s, detener a los 5 min, mejorar el algoritmo 357 Sección 5 Proyecto del día 🧪 y ⭐ de la sección 5 Sección 5
(No existe en el curso original) — — — Probador automático de tu sitio Sección 6
(No existe en el curso original) — — — Asistente automático, etapa 4 Sección 7

12. 📦 Evidencias de aprendizaje del Día 48

  • Función crear_driver() reutilizable.
  • Los mismos elementos de python.org localizados con tres estrategias distintas.
  • El diccionario de eventos impreso y el contador de Wikipedia corregido.
  • Un formulario llenado automáticamente (en el sitio de práctica del curso o en tu propio formulario).
  • El bot de Cookie Clicker con el número de galletas por segundo alcanzado y la descripción de tu estrategia.
  • El reporte del probador automático (proyecto extra).
  • Etapa 4 del proyecto integrador: inscriptor.py y su salida.
  • Cuaderno de errores: find_element_by_*, text() con paréntesis, click sin paréntesis, NoSuchElementException por carga lenta, ElementNotInteractableException por el tamaño de la ventana, selectores que atrapan elementos de más.