¿Por qué son necesarias las esperas?
flowchart TB
Wait[Esperas Python] --> Imp[Implícita]
Wait --> Exp[Explícita]
Wait --> Flu[Fluent]
Imp --> ImpC[driver.implicitly_wait segundos]
Imp --> Glob[Aplica a find_element]
Exp --> WW["WebDriverWait driver, timeout"]
WW --> Until[wait.until condición]
Until --> EC[expected_conditions]
EC --> Vis[visibility_of_element_located]
EC --> Click[element_to_be_clickable]
EC --> Pres[presence_of_element_located]
EC --> Text[text_to_be_present_in_element]
Flu --> PF["poll_frequency=0.5"]
Flu --> IE["ignored_exceptions=..."]
Flu --> Cust[Lambda personalizada]
Las aplicaciones web modernas son asíncronas: cargan datos vía AJAX, animan elementos, obtienen datos de APIs externas y modifican el DOM dinámicamente. Un script que busque un elemento antes de que exista en el DOM lanzará una excepción NoSuchElementException.
Las esperas permiten que el script se sincronice con el estado real de la página, esperando a que se cumpla una condición antes de continuar.
# Sin espera (PROBLEMA): el elemento puede no existir todavía
elemento = driver.find_element(By.ID, "resultado-ajax") # ¡Puede fallar!
# Con espera (SOLUCIÓN): esperamos hasta que el elemento esté visible
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
elemento = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.ID, "resultado-ajax"))
)
Espera implícita
La espera implícita le indica al WebDriver que espere un tiempo máximo cada vez que intenta localizar un elemento. Se configura una sola vez para toda la sesión.
from selenium import webdriver
driver = webdriver.Chrome()
# Configurar espera implícita de 10 segundos para toda la sesión
driver.implicitly_wait(10)
driver.get("https://www.ejemplo.com")
# Ahora cada find_element esperará hasta 10 segundos antes de fallar
elemento = driver.find_element(By.ID, "carga-dinamica")
driver.quit()
Limitaciones de la espera implícita
- Se aplica globalmente a todos los
find_elementyfind_elements - No permite esperar condiciones complejas (como que un elemento sea clickable)
- Puede ocultar problemas reales de rendimiento
- No se recomienda combinarla con esperas explícitas en la misma sesión
Espera explícita con WebDriverWait
La espera explícita es la estrategia recomendada. Permite especificar exactamente qué condición esperar y durante cuánto tiempo.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
with webdriver.Chrome() as driver:
driver.get("https://www.ejemplo.com/carga-dinamica")
# Esperar hasta 10 segundos a que el elemento sea visible
espera = WebDriverWait(driver, 10)
elemento = espera.until(
EC.visibility_of_element_located((By.ID, "mensaje-exito"))
)
print(elemento.text)
Condiciones de espera más usadas (expected_conditions)
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
espera = WebDriverWait(driver, 10)
# El elemento existe en el DOM (puede estar oculto)
espera.until(EC.presence_of_element_located((By.ID, "elemento")))
# El elemento existe Y es visible
espera.until(EC.visibility_of_element_located((By.ID, "elemento")))
# El elemento es visible y clickable
espera.until(EC.element_to_be_clickable((By.ID, "boton")))
# El elemento ya no es visible (desapareció)
espera.until(EC.invisibility_of_element_located((By.ID, "spinner")))
# El texto exacto está en el elemento
espera.until(EC.text_to_be_present_in_element((By.ID, "estado"), "Completado"))
# El texto está en el valor del input
espera.until(EC.text_to_be_present_in_element_value((By.ID, "campo"), "valor"))
# La URL contiene una cadena específica
espera.until(EC.url_contains("/dashboard"))
# La URL es exactamente esta
espera.until(EC.url_to_be("https://www.ejemplo.com/bienvenido"))
# El título contiene un texto
espera.until(EC.title_contains("Inicio"))
# El título es exactamente este
espera.until(EC.title_is("Mi Aplicación - Inicio"))
# Hay al menos N ventanas abiertas
espera.until(EC.number_of_windows_to_be(2))
# El frame está disponible y cambia a él
espera.until(EC.frame_to_be_available_and_switch_to_it((By.ID, "mi-frame")))
# Una alerta está presente
espera.until(EC.alert_is_present())
# El elemento tiene un atributo específico
espera.until(EC.element_attribute_to_include((By.ID, "imagen"), "src"))
Espera de múltiples elementos
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
with webdriver.Chrome() as driver:
driver.get("https://www.ejemplo.com/productos")
# Esperar a que haya al menos un elemento con esa clase
productos = WebDriverWait(driver, 10).until(
EC.presence_of_all_elements_located((By.CLASS_NAME, "producto-card"))
)
print(f"Se cargaron {len(productos)} productos")
Espera fluida (Fluent Wait)
La espera fluida es una variante de la espera explícita que permite configurar la frecuencia de sondeo y las excepciones a ignorar durante la espera.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException
with webdriver.Chrome() as driver:
driver.get("https://www.ejemplo.com/carga-lenta")
# Espera fluida: máximo 30s, comprueba cada 2s, ignora ciertos errores
espera_fluida = WebDriverWait(
driver,
timeout=30,
poll_frequency=2, # Comprueba cada 2 segundos
ignored_exceptions=[
NoSuchElementException,
StaleElementReferenceException
]
)
elemento = espera_fluida.until(
EC.visibility_of_element_located((By.ID, "dato-cargado"))
)
print(elemento.text)
Condiciones personalizadas con lambda
Para condiciones no disponibles en expected_conditions, usa funciones lambda:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
with webdriver.Chrome() as driver:
driver.get("https://www.ejemplo.com/contador")
# Esperar hasta que el contador llegue a 5
WebDriverWait(driver, 15).until(
lambda d: int(d.find_element(By.ID, "contador").text) >= 5
)
# Esperar hasta que haya exactamente 3 elementos en una lista
WebDriverWait(driver, 10).until(
lambda d: len(d.find_elements(By.CLASS_NAME, "item")) == 3
)
# Esperar hasta que el atributo disabled desaparezca
WebDriverWait(driver, 10).until(
lambda d: not d.find_element(By.ID, "boton-enviar").get_attribute("disabled")
)
Espera negativa: until_not
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# Esperar hasta que el spinner YA NO sea visible
WebDriverWait(driver, 30).until_not(
EC.visibility_of_element_located((By.CLASS_NAME, "spinner-loading"))
)
# Continuar con la prueba una vez que el spinner ha desaparecido
boton = driver.find_element(By.ID, "boton-enviar")
boton.click()
Cuándo usar cada tipo de espera
| Situación | Estrategia recomendada |
|-----------|----------------------|
| Prototipos rápidos | Espera implícita |
| Elemento que tarda en cargarse | Espera explícita (visibility_of_element_located) |
| Botón que tarda en activarse | Espera explícita (element_to_be_clickable) |
| Spinner que desaparece | Espera explícita + until_not |
| API lenta con tiempos variables | Espera fluida con poll_frequency alto |
| Condición personalizada | Lambda con WebDriverWait |
Ejemplo completo: formulario con carga asíncrona
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def test_formulario_asincrono():
"""Prueba un formulario que valida en tiempo real."""
with webdriver.Chrome() as driver:
driver.get("https://www.ejemplo.com/registro")
espera = WebDriverWait(driver, 10)
# Rellenar email
campo_email = espera.until(
EC.element_to_be_clickable((By.ID, "email"))
)
campo_email.send_keys("test@ejemplo.com")
# Esperar validación asíncrona del email
espera.until(
EC.visibility_of_element_located((By.CSS_SELECTOR, ".email-valido"))
)
# Rellenar contraseña
campo_password = driver.find_element(By.ID, "password")
campo_password.send_keys("contraseña_segura_123")
# Esperar a que el botón de envío se active
boton_enviar = espera.until(
EC.element_to_be_clickable((By.ID, "btn-registrar"))
)
boton_enviar.click()
# Esperar mensaje de éxito
mensaje = espera.until(
EC.visibility_of_element_located((By.ID, "mensaje-registro-exitoso"))
)
assert "registro" in mensaje.text.lower()
Fuentes y referencias
Documentación oficial y recursos externos para profundizar en Selenium
Documentación oficial de Selenium
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Selenium es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Selenium
Explora más contenido relacionado con Selenium y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Comprender por qué son necesarias las esperas en la automatización web. Usar implicitly_wait para esperas implícitas a nivel de sesión. Implementar WebDriverWait con expected_conditions para esperas explícitas precisas. Configurar WebDriverWait con poll_frequency e ignored_exceptions para esperas fluidas. Elegir la estrategia de espera adecuada según el contexto de la prueba. Combinar esperas explícitas con condiciones personalizadas usando lambdas.