Ir al contenido

¿Cómo esperar a que se cargue la página en Selenium? Una guía de expertos

Déjame adivinar: has comenzado a raspar un sitio con Selenium y de repente te enfrentas a temidos errores de tiempo de espera, excepciones de elementos obsoletos y localizadores defectuosos. ¿Suena familiar?

¡Muchos de nosotros hemos estado ahi! En la web dinámica actual, esperar adecuadamente a que las páginas se carguen por completo antes de interactuar es fundamental para una automatización confiable.

En esta guía completa de más de 3200 palabras, aprovecharé mis más de 5 años como experto profesional en web scraping para explorar los diversos métodos y mejores prácticas para una espera elegante en Selenium.

Ya sea que esté comenzando o sea un profesional experimentado, una lógica de espera sólida es una herramienta imprescindible para la estabilidad. ¡Vamos a sumergirnos!

Por qué no puedes simplemente apresurarte

En los primeros días de la web, las páginas eran en su mayoría HTML simple representado de forma secuencial. Los raspadores podrían comenzar a extraer inmediatamente al cargar la página.

Pero la red actual es muy dinámica. De acuerdo a Investigación de Google, el tiempo medio para pintar por primera vez es de 1.7 segundos, pero el tiempo medio para interactuar completamente es enorme 15 segundos. Eso es mucho tiempo para que se cargue el contenido.

Como raspador, si se apresura demasiado rápido, aquí hay algunos problemas comunes que enfrentará:

  • Errores al hacer clic en el botón porque el elemento aún no se ha renderizado
  • Intentando leer datos de una tabla que no ha cargado el contenido del servidor
  • Enviar texto a una entrada que no es visible en la pantalla
  • Eliminación de elementos vacíos que se completarán después de cargar la página

Este tipo de excepciones son síntomas de que es necesario esperar más tiempo para que la página esté lista antes de interactuar.

En cifras: tiempos de carga de la página

Para comprender cuánto tiempo tendremos que esperar, veamos algunas métricas del mundo real sobre el rendimiento de carga de la página desde el Informe sobre el estado de la web 2020 por Akamai:

  • Tiempo medio para interactuar: Los 15s
  • Peso promedio de página: 2744KB
  • Número promedio de solicitudes: 105
  • Imágenes promedio por página: 53
  • Bytes de JavaScript por página: 453KB

Las páginas son más grandes y complejas hoy en día, y se realiza mucho más trabajo después de la respuesta inicial. Es fundamental que los raspadores esperen la interactividad, no solo la primera pintura.

Excepciones comunes causadas por no esperar

A continuación se muestran algunas excepciones específicas que pueden ocurrir cuando los elementos aún no están listos:

  • Excepción de referencia de elemento obsoleto – Elemento eliminado del DOM después de la recuperación
  • ElementoNotInteractableException – Intentar hacer clic en un elemento invisible
  • Ninguna excepción de elemento tal – Se agotó el tiempo de búsqueda porque el elemento aún no existe

Cada uno de estos indica que el raspador requiere más espera.

Las esperas explícitas son tus amigas

Para evitar estos errores, debemos esperar a que la página se represente por completo antes de interactuar. Hay dos enfoques principales en Selenium:

Esperas implícitas – Establecer un tiempo de espera global en el conductor.

Esperas explícitas – Espere a que se produzcan condiciones específicas.

En la mayoría de los casos, se prefiere una espera explícita a una espera implícita. Entendamos por qué.

Esperas implícitas: el enfoque del mazo

Las esperas implícitas establecen un tiempo de espera en el controlador para sondear el DOM cuando encuentra elementos. Esto significa que cada vez que llame:

driver.find_element_by_id(‘some-id‘)

El controlador volverá a intentar hasta la duración de espera implícita para localizar ese elemento antes de generar una excepción NoSuchElementException.

Podrías usarlo como:

driver = webdriver.Chrome()
driver.implicitly_wait(10) 

Ahora todas las búsquedas volverán a intentarlo durante hasta 10 segundos para encontrar elementos si no están presentes inmediatamente.

La desventaja es que espera a todos los localizadores, incluso a los que no son necesarios para determinar la preparación de la página. Esto realmente puede ralentizar el raspador.

Piense en esperas implícitas como agregar un sueño de 5 segundos a cada elemento recuperado. ¡Se suma!

La precisión de las esperas explícitas

Las esperas explícitas nos permiten esperar con precisión condiciones específicas que indiquen que estamos preparados antes de continuar.

Las ideas clave son:

  • Espera solo cuando sea necesario – Evite esperas innecesarias no relacionadas con la preparación de la página.
  • Condiciones precisas – Espere elementos o estados exactos, no solo el tiempo general
  • Flexibilidad – Personaliza la lógica de espera por página con diferentes condiciones.
  • legible – Intención fácil de entender al revisar código antiguo

A continuación se muestra un ejemplo típico esperando a que aparezca un elemento:

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait 

WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "myDynamicElement"))
)

Esto detiene la ejecución hasta que se carga el elemento con ID "myDynamicElement", o pasan 10 segundos.

Otras condiciones útiles esperadas proporcionado por selenio incluyen:

  • title_contains() – Espere a que se actualice el título de la página.
  • staleness_of() – Espere a que el elemento ya no esté adjunto al DOM
  • element_to_be_clickable() – Espere a que el elemento sea visible y habilitado.

Lo explícito supera a lo implícito: un ejemplo del mundo real

Comparemos las dos esperas con un ejemplo real.

Digamos que estoy raspando un sitio que tiene una barra de navegación, un panel lateral izquierdo y contenido principal.

El elemento clave que debo esperar es un ID "#main-content" donde se representan mis datos.

Con espera implícita:

  • Se agregan 10 segundos a cada búsqueda de elementos, incluso si no son necesarios
  • Todavía propenso a errores de elementos obsoletos si es demasiado rápido

Con espera explícita:

  • Espere solo cuando sea necesario para el # selector de contenido principal
  • Evite esperas innecesarias en la navegación y el panel lateral
  • Espere específicamente hasta que se carguen los datos antes de continuar

Al esperar selectivamente una única condición de preparación, como un elemento, evito retrasos innecesarios.

Patrones para esperas explícitas efectivas

Ahora que está convencido de que las esperas explícitas son el camino a seguir, exploremos algunas de las mejores prácticas para utilizarlas de forma eficaz.

La carga de la página espera

Esperar a que el documento esté listo es una técnica común para determinar cuándo se completa la carga:

WebDriverWait(driver, 10).until(
   lambda d: d.execute_script(‘return document.readyState‘) == ‘complete‘
)

Esto sondea el navegador hasta que el estado listo esté "completo", lo que indica que todos los recursos están cargados.

Un patrón más ligero busca elementos específicos de alto nivel:

WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "main-content"))
) 

Esto se logra cuando se carga la sección de contenido principal, sin esperar a todo lo demás.

Esperas por acción

También puedes esperar justo antes de realizar una acción, como hacer clic en un elemento:

menu = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "top-menu"))
)

submenu = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.ID, "submenu"))
)

submenu.click()

Esto garantiza que tanto el menú superior como el submenú estén listos antes de hacer clic.

Esperas paralelas

Esperar varias condiciones puede confirmar que la página está lista:

WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "header")),
    EC.presence_of_element_located((By.ID, "footer")), 
    EC.presence_of_element_located((By.ID, "main"))
)

Requerir que se carguen el encabezado, el pie de página y el contenido principal reduce los falsos positivos.

Esperas encadenadas y anidadas

Para escenarios avanzados, también puedes anidar esperas:

element = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "dropdown"))
)

menu = WebDriverWait(element, 10).until(
    EC.presence_of_element_located((By.ID, "menu"))  
)

Esto espera primero un elemento principal y luego un elemento secundario dentro de ese.

Esperas de sondeo AJAX

Algunos sitios se cargan mediante solicitudes AJAX continuas. Puedes hacer un bucle esperando cambios:

while True:

    current_count = driver.find_element_by_id(‘result-count‘).text

    # If count changed since last check, page is still loading
    if current_count != previous_count:
        previous_count = current_count
        continue 

    break # Page loaded!

Esto sondea un elemento en busca de cambios para detectar la carga.

Esperas asincrónicas

En marcos asíncronos como asyncio, puedes esperar promesas:

await page.waitForSelector(‘#content‘)

La sintaxis es un poco diferente pero proporciona espera asincrónica.

Combinación implícita + explícita

Incluso puedes combinar esperas implícitas y explícitas:

driver.implicitly_wait(10) 

my_element = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "my-element"))
)

De esta forma tienes una espera global además de una específica. Solo asegúrese de que utilicen duraciones razonables.

Elegir localizadores que indiquen la preparación

Al seleccionar localizadores para esperar, desea elementos que coincidan con estos criterios:

  • Aparecer tarde en el proceso de carga.
  • Tener identificaciones o clases únicas que no cambiarán
  • Ubicado en la mitad superior de la página para controles rápidos
  • Es poco probable que sean reubicados debido a cambios en el sitio.
  • No te eliminen del DOM y te quedes obsoleto

Algunos ejemplos comunes son:

  • Encabezado principal o navegador cargado después de los activos
  • Contenedores de contenido primario o widgets
  • Pie de página
  • Pequeños elementos dinámicos de la interfaz de usuario como botones

Los indicadores de carga, como los hilanderos, también son excelentes desencadenantes de espera cuando desaparecen.

Ajuste de tiempos de espera para una espera óptima

Establecer tiempos de espera demasiado largos puede realmente ralentizar su raspador, pero demasiado cortos pueden causar fallas esporádicas.

A continuación se muestran algunas prácticas recomendadas para ajustar las duraciones:

  • Establezca tiempos de espera de carga de página más largos, entre 10 y 20 segundos.
  • Utilice tiempos de espera más cortos, como de 3 a 5 segundos, para elementos individuales.
  • Considere el rendimiento del navegador, móvil frente a escritorio.
  • Tenga en cuenta la latencia de la red, banda ancha frente a 3G.
  • Supervise los errores de tiempo de espera y ajuste un nivel más alto si es necesario.
  • Analice la cascada de carga de la página para conocer los tiempos de carga típicos.
  • Presupuesta entre 1 y 2 segundos adicionales como amortiguador.
  • Estandarice esperas similares en todo su código base.

A medida que recorra más páginas, obtendrá una mejor intuición sobre las esperas óptimas para la confiabilidad.

Manejo de fallas de espera y tiempo de espera

Incluso con esperas intensas, es posible que encuentre tiempos de espera ocasionales. A continuación se muestran algunas formas de manejarlos:

  • Detalles de depuración de registros – Agregar impresiones ayuda a diagnosticar dónde fallan las esperas.
  • Reintentar en tiempo de espera – Vuelva a intentar esperas breves y explícitas hasta 3 veces en caso de error.
  • aumentar el tiempo de espera – Si se producen muchos tiempos de espera, aumente las esperas de forma incremental.
  • Utilice probar/excepto – Detectar excepciones específicas como StaleElementReference.
  • Deshabilitar en caso de error – Puede omitir esperas después de fallas repetidas para permitir que las pruebas continúen.

Con algo de resiliencia incorporada, estos problemas esporádicos no dañarán su raspador.

Esperando en otros idiomas

Hasta ahora los ejemplos han estado en Python, pero las esperas explícitas están disponibles en todos los idiomas:

  • JavaWebDriverWait y ExpectedConditions
  • C#WebDriverWait y ExpectedConditions
  • RubíWebDriver::Wait y ExpectedConditions
  • JavaScriptbrowser.wait() y métodos de utilidad

Los conceptos son muy similares, sólo que la sintaxis difiere ligeramente.

Más allá del selenio: más herramientas de espera

También hay otras bibliotecas de espera útiles además de Selenium:

  • Horariotime.sleep() es simple pero pausa toda la ejecución.
  • Reintente - El Reintentar paquete facilita los reintentos y las esperas.
  • Aiohttpawait response.text() espera que se completen las llamadas de red.
  • Hermosa sopaBeautifulSoup(page.content, features="lxml") esperará el análisis completo.
  • Scrapyyield scrapy.Request(url, callback=self.parse) es asincrónico.

Combinarlos con Selenium proporciona esperas sólidas en todo el código.

En resumen: espere bien y deseche de manera confiable

Para terminar, aquí hay cinco conclusiones clave:

  1. Utilice esperas explícitas – Evitan tiempos de espera innecesarios y se dirigen a condiciones específicas.

  2. Espere múltiples señales – Combine esperas de encabezado, cuerpo, pie de página, etc. para confirmar que la página está lista.

  3. Ajusta los tiempos de espera sabiamente – Establezca valores basados ​​en datos de carga de páginas del mundo real para optimizar los retrasos.

  4. Estandarizar esperas – Reutilice patrones consistentes en su base de código.

  5. Añadir resiliencia – Implementar reintentos y manejo de fallas para tener en cuenta las páginas dinámicas.

La espera puede parecer tediosa al principio. Pero invertir en una lógica de espera sólida lo recompensará con raspadores confiables y resistentes preparados para la web moderna.

Espero que estos patrones y consejos extraídos de mis años como especialista profesional en web scraping te ayuden a esperar con éxito. ¡Adelante!

Tags:

Únase a la conversación

Su dirección de correo electrónico no será publicada. Las areas obligatorias están marcadas como requeridas *