Espere hasta que la página se cargue con Selenium WebDriver para Python
Quiero extraer todos los datos de una página implementada mediante un desplazamiento infinito. El siguiente código Python funciona.
for i in range(100):
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(5)
Esto significa que cada vez que me desplazo hacia abajo, debo esperar 5 segundos, lo que generalmente es suficiente para que la página termine de cargar el contenido recién generado. Pero es posible que esto no ahorre tiempo. La página puede terminar de cargar los nuevos contenidos en 5 segundos. ¿Cómo puedo detectar si la página terminó de cargar el nuevo contenido cada vez que me desplazo hacia abajo? Si puedo detectar esto, puedo desplazarme hacia abajo nuevamente para ver más contenidos una vez que sepa que la página terminó de cargarse. Esto es más eficiente en cuanto a tiempo.
Esperará webdriver
a que se cargue una página de forma predeterminada mediante .get()
el método.
Como puedes estar buscando algún elemento específico como dijo @user227215, debes usar WebDriverWait
para esperar por un elemento ubicado en tu página:
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
browser = webdriver.Firefox()
browser.get("url")
delay = 3 # seconds
try:
myElem = WebDriverWait(browser, delay).until(EC.presence_of_element_located((By.ID, 'IdOfMyElement')))
print "Page is ready!"
except TimeoutException:
print "Loading took too much time!"
Lo he usado para verificar alertas. Puede utilizar cualquier otro tipo de método para encontrar el localizador.
EDITAR 1:
Debo mencionar que webdriver
esperará a que se cargue una página de forma predeterminada. No espera la carga dentro de los marcos ni las solicitudes de ajax. Significa que cuando use .get('url')
, su navegador esperará hasta que la página esté completamente cargada y luego pasará al siguiente comando en el código. Pero cuando publica una solicitud ajax, webdriver
no espera y es su responsabilidad esperar una cantidad de tiempo adecuada para que se cargue la página o parte de la página; entonces hay un módulo llamado expected_conditions
.
Intentar pasar find_element_by_id
al constructor for presence_of_element_located
(como se muestra en la respuesta aceptada ) provocó NoSuchElementException
un aumento. Tuve que usar la sintaxis en el comentario de fragles :
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
driver = webdriver.Firefox()
driver.get('url')
timeout = 5
try:
element_present = EC.presence_of_element_located((By.ID, 'element_id'))
WebDriverWait(driver, timeout).until(element_present)
except TimeoutException:
print "Timed out waiting for page to load"
Esto coincide con el ejemplo de la documentación . Aquí hay un enlace a la documentación de By .
Encuentre a continuación 3 métodos:
estado listo
Comprobando el estado de preparación de la página (no confiable):
def page_has_loaded(self):
self.log.info("Checking if {} page is loaded.".format(self.driver.current_url))
page_state = self.driver.execute_script('return document.readyState;')
return page_state == 'complete'
La
wait_for
función auxiliar es buena, pero desafortunadamenteclick_through_to_new_page
está abierta a la condición de carrera en la que logramos ejecutar el script en la página anterior, antes de que el navegador haya comenzado a procesar el clic, ypage_has_loaded
simplemente devuelve verdadero de inmediato.
id
Comparando los nuevos ID de página con los antiguos:
def page_has_loaded_id(self):
self.log.info("Checking if {} page is loaded.".format(self.driver.current_url))
try:
new_page = browser.find_element_by_tag_name('html')
return new_page.id != old_page.id
except NoSuchElementException:
return False
Es posible que comparar identificadores no sea tan efectivo como esperar excepciones de referencia obsoletas.
staleness_of
Método de uso staleness_of
:
@contextlib.contextmanager
def wait_for_page_load(self, timeout=10):
self.log.debug("Waiting for page to load at {}.".format(self.driver.current_url))
old_page = self.find_element_by_tag_name('html')
yield
WebDriverWait(self, timeout).until(staleness_of(old_page))
Para obtener más detalles, consulte el blog de Harry .
Como se menciona en la respuesta de David Cullen , siempre he visto recomendaciones para usar una línea como la siguiente:
element_present = EC.presence_of_element_located((By.ID, 'element_id'))
WebDriverWait(driver, timeout).until(element_present)
Fue difícil para mí encontrar en algún lugar todos los posibles localizadores que se pueden usar con By
, así que pensé que sería útil proporcionar la lista aquí. Según Web Scraping con Python de Ryan Mitchell:
ID
Usado en el ejemplo; encuentra elementos por su atributo de identificación HTML
CLASS_NAME
Se utiliza para buscar elementos por su atributo de clase HTML. ¿Por qué esta función
CLASS_NAME
no es simpleCLASS
? Usar el formularioobject.CLASS
crearía problemas para la biblioteca Java de Selenium, donde.class
hay un método reservado.CLASS_NAME
En su lugar, se utilizó para mantener la sintaxis de Selenium coherente entre diferentes idiomas .
CSS_SELECTOR
Busca elementos por su clase , ID o nombre de etiqueta, utilizando la convención
#idName
, ..className
tagName
LINK_TEXT
Encuentra etiquetas HTML por el texto que contienen. Por ejemplo, se puede seleccionar un enlace que dice "Siguiente" usando
(By.LINK_TEXT, "Next")
.
PARTIAL_LINK_TEXT
Similar a
LINK_TEXT
, pero coincide con una cadena parcial.
NAME
Encuentra etiquetas HTML por su atributo de nombre. Esto es útil para formularios HTML.
TAG_NAME
Encuentra etiquetas HTML por su nombre de etiqueta.
XPATH
Utiliza una expresión XPath... para seleccionar elementos coincidentes.
Desde selenio/webdriver/support/wait.py
driver = ...
from selenium.webdriver.support.wait import WebDriverWait
element = WebDriverWait(driver, 10).until(
lambda x: x.find_element_by_id("someId"))