¿Cómo esperar un elemento en Playwright sin usar sleep?
No hace falta time.sleep(): Playwright espera por sí solo antes de cada acción, y para los demás casos existen wait_for(), wait_for_url() y expect_response(). Con eso se cubre casi cualquier situación de contenido que aparece tarde.
Las pausas fijas dan problemas porque nunca están bien ajustadas. Si son cortas, el script falla cuando la página tarda más de lo normal, y si son largas, se pierde tiempo en cada ejecución. Las esperas de Playwright, en cambio, terminan en cuanto se cumple la condición.
Conviene conocer cuatro herramientas: la espera automática de las acciones, la espera explícita de un elemento, la espera de un cambio de página y la espera de una respuesta de red. Cada una resuelve un caso distinto, y todas dependen de unos tiempos límite que también se pueden ajustar.
Abrir una dirección
response = page.goto("https://quotes.toscrape.com")
print(response.status, response.ok) # 200 True
El método goto() devuelve un objeto de respuesta con el código de estado. Hay un detalle importante: una página que responde 404 o 500 no provoca ninguna excepción, porque desde el punto de vista del navegador la carga fue correcta. Solo falla cuando no se puede conectar, por ejemplo con un dominio inexistente. Por eso, cuando el código depende de que la página exista, conviene revisar response.ok.
Cuándo se considera cargada una página
Por defecto, goto() espera al evento load, que se dispara cuando la página y sus recursos iniciales terminaron de cargar. Con el parámetro wait_until se cambia ese criterio:
| Valor | goto() termina cuando… | Cuándo se usa |
|---|---|---|
"commit" | llega la respuesta del servidor y empieza el documento | casos muy concretos, hay que esperar el resto a mano |
"domcontentloaded" | el HTML está leído y procesado | es la opción más práctica en la mayoría de los scripts |
"load" | la página y sus recursos terminaron de cargar | es el valor por defecto |
"networkidle" | pasan 500 ms sin tráfico de red | la documentación lo desaconseja |
# Más rápido: no espera imágenes ni hojas de estilo
page.goto("https://quotes.toscrape.com", wait_until="domcontentloaded")
Muchos tutoriales recomiendan networkidle para las aplicaciones de una sola página. La documentación oficial de Playwright pide lo contrario, porque depende de que la red se quede en silencio, algo que no ocurre en páginas con anuncios, analíticas o conexiones permanentes. Es más fiable esperar al elemento concreto que interesa.
Las esperas automáticas
Antes de cada acción, Playwright comprueba que el elemento esté en la página, que sea visible, que no se esté moviendo, que esté habilitado y que nada lo tape. Si alguna condición falla, reintenta hasta que se cumple o se agota el tiempo. Gracias a eso, una línea como esta funciona aunque el botón tarde un segundo en aparecer:
page.get_by_role("button", name="Enviar").click()
No hace falta una espera previa, y ahí está la mayor diferencia con otras herramientas de automatización.
Esperar un elemento de forma explícita
Hay casos en que no se va a hacer ninguna acción sobre el elemento, pero se necesita saber que ya está. Entonces se usa wait_for():
page.goto("https://ejemplo.com/espera")
# Esperar a que desaparezca el mensaje de carga
page.locator("#cargando").wait_for(state="hidden")
# Esperar a que aparezca el precio y leerlo
precio = page.locator(".precio")
precio.wait_for()
print(precio.inner_text())
El parámetro state admite cuatro valores. Con "visible", que es el predeterminado, se espera a que el elemento exista y se vea. Con "hidden" se espera a que deje de verse o desaparezca. Los valores "attached" y "detached" hablan de presencia en el documento, aunque el elemento no sea visible.
Esperar la respuesta de la red, no solo el elemento
En una página con buscador, los resultados no existen hasta que el servidor responde. Esperar al elemento funciona, pero a veces conviene esperar la respuesta misma, porque trae los datos en formato JSON, más limpios que el HTML. Para eso sirve expect_response(), que sigue la misma lógica de preparar la escucha antes de provocar el evento.
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://ejemplo.com/busqueda")
# Esperar la respuesta de la API que llena los resultados
with page.expect_response(lambda r: "/api/productos" in r.url) as info:
page.get_by_placeholder("Buscar producto").fill("teclado")
page.get_by_role("button", name="Buscar").click()
productos = info.value.json()
print(f"La API devolvió {len(productos)} producto(s)")
page.locator(".resultado").first.wait_for()
print(page.locator(".resultado").all_inner_texts())
browser.close()
La escucha se prepara antes de hacer clic. Si el orden fuera el inverso, la respuesta podría llegar antes de que el script estuviera atento y se perdería. El predicado recibe cada respuesta y devuelve True para la que interesa, que aquí es cualquiera cuya dirección contenga /api/productos. Con info.value.json() se obtienen los datos tal como los envió el servidor, y después wait_for() espera a que la página termine de dibujarlos. Leer la API directamente suele ser más completo, porque trae campos que la página no siempre muestra.
Los tiempos límite
Toda espera tiene un máximo, que por defecto es de 30 segundos. Si se supera, Playwright lanza un TimeoutError. El tiempo se indica en milisegundos, un detalle que provoca errores de bulto, porque timeout=10 son diez milisegundos y no diez segundos.
# Para una sola acción
page.locator("h1").wait_for(timeout=10000)
# Para todas las acciones de la página
page.set_default_timeout(10000)
# Solo para las navegaciones (goto, reload...)
page.set_default_navigation_timeout(60000)
Los mismos métodos existen en el contexto, y en ese caso el valor se aplica a todas las páginas que se abran desde él.
Esperar un cambio de dirección o de estado
Tras un clic que cambia de página, se usa wait_for_url() o wait_for_load_state():
page.wait_for_url("**/dashboard")
page.wait_for_load_state("domcontentloaded")
El primero acepta patrones con asteriscos, y el segundo espera a que el documento llegue a un estado de carga determinado.
Por qué no usar time.sleep
Una pausa fija es siempre un error, ya sea porque espera de más o porque espera de menos. Si dura tres segundos y el elemento aparece en uno, el script pierde dos. Si el servidor un día tarda cuatro, el script falla. Las esperas de Playwright terminan en el momento exacto en que la condición se cumple, y por eso los scripts resultan más rápidos y también más estables.