Espere hasta que la página esté cargada con Selenium WebDriver para Python

Quiero raspar todos los datos de una página implementada por un desplazamiento infinito. El siguiente código de 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, necesito esperar 5 segundos, lo que generalmente es suficiente para que la página termine de cargar el contenido recién generado. Pero, esto puede no ser eficiente de 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 los nuevos contenidos 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 el tiempo.

El webdriver esperará a que se cargue una página de forma predeterminada a través del método .get() .

Como puede que esté buscando un elemento específico como dijo @ user227215, debe usar WebDriverWait para esperar un elemento ubicado en su 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 comprobar las alertas. Puede utilizar cualquier otro método de tipo para encontrar el localizador.

EDITAR 1:

Debo mencionar que el webdriver esperará a que se cargue una página de forma predeterminada. No espera para cargar dentro de marcos o para solicitudes ajax. Significa que cuando usa .get('url') , su navegador esperará hasta que la página esté completamente cargada y luego irá al siguiente comando en el código. Pero cuando está publicando una solicitud de ajax, webdriver no espera y es su responsabilidad esperar un tiempo adecuado para que la página o una parte de la página se cargue; así que hay un módulo llamado expected_conditions .

Al intentar pasar find_element_by_id al constructor de presence_of_element_located (como se muestra en la respuesta aceptada ), se generó la NoSuchElementException . Tuve que usar la syntax 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 en la documentación . Aquí hay un enlace a la documentación de By .

Encuentra a continuación 3 métodos:

estado listo

Comprobando la página readyState (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 función wait_for helper es buena, pero desafortunadamente, click_through_to_new_page está abierto a la condición de carrera en la que conseguimos ejecutar el script en la página anterior, antes de que el navegador haya comenzado a procesar el clic, y page_has_loaded simplemente devuelve verdadero de inmediato.

id

Comparando nuevos identificadores de página con el anterior:

 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 ids no sea tan efectivo como esperar excepciones de referencias obsoletas.

staleness_of

Usando el método 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 más detalles, revisa el blog de Harry .

Desde selenium / 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")) 

Como se mencionó en la respuesta de David Cullen , siempre he recomendado usar una línea como la siguiente:

 element_present = EC.presence_of_element_located((By.ID, 'element_id')) WebDriverWait(driver, timeout).until(element_present) 

Me fue difícil encontrar en cualquier lugar todos los localizadores posibles que puedan usarse con la syntax By , así que pensé que sería útil proporcionar aquí la lista. Según Web Scraping with Python por Ryan Mitchell:

ID

Utilizado en el ejemplo; encuentra elementos por su atributo de identificación HTML

CLASS_NAME

Se utiliza para encontrar elementos por su atributo de clase HTML. ¿Por qué esta función CLASS_NAME no es simplemente CLASS ? El uso del formulario object.CLASS crearía problemas para la biblioteca Java de Selenium, donde .class es un método reservado. Para mantener la syntax de Selenium consistente entre diferentes idiomas, se usó CLASS_NAME su lugar.

CSS_SELECTOR

Busque elementos por su clase, id o nombre de etiqueta, utilizando la #idName , .className , tagName .

LINK_TEXT

Encuentra las tags HTML por el texto que contienen. Por ejemplo, un enlace que dice “Siguiente” se puede seleccionar usando (By.LINK_TEXT, "Next") .

PARTIAL_LINK_TEXT

Similar a LINK_TEXT , pero coincide en una cadena parcial.

NAME

Encuentra tags HTML por su nombre de atributo. Esto es útil para los formularios HTML.

TAG_NAME

Alisa las tags HTML por su nombre de etiqueta.

XPATH

Utiliza una expresión XPath … para seleccionar elementos coincidentes.

En una nota al margen, en lugar de desplazarse hacia abajo 100 veces, puede verificar si no hay más modificaciones en el DOM (estamos en el caso de que la parte inferior de la página esté cargada de AJAX)

 def scrollDown(driver, value): driver.execute_script("window.scrollBy(0,"+str(value)+")") # Scroll down the page def scrollDownAllTheWay(driver): old_page = driver.page_source while True: logging.debug("Scrolling loop") for i in range(2): scrollDown(driver, 500) time.sleep(2) new_page = driver.page_source if new_page != old_page: old_page = new_page else: break return True 

¿Qué hay de poner WebDriverWait en While loop y capturar las excepciones?

 from selenium import webdriver from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException browser = webdriver.Firefox() browser.get("url") delay = 3 # seconds while True: try: WebDriverWait(browser, delay).until(EC.presence_of_element_located(browser.find_element_by_id('IdOfMyElement'))) print "Page is ready!" break # it will break from the loop once the specific element will be present. except TimeoutException: print "Loading took too much time!-Try again" 

Aquí lo hice utilizando una forma bastante simple:

 from selenium import webdriver browser = webdriver.Firefox() browser.get("url") searchTxt='' while not searchTxt: try: searchTxt=browser.find_element_by_name('NAME OF ELEMENT') searchTxt.send_keys("USERNAME") except:continue 

¿Has probado driver.implicitly_wait . Es como un ajuste para el controlador, por lo que solo lo llama una vez en la sesión y básicamente le dice al controlador que espere el tiempo dado hasta que se pueda ejecutar cada comando.

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

Entonces, si establece un tiempo de espera de 10 segundos, ejecutará el comando tan pronto como sea posible, esperando 10 segundos antes de que se rinda. He usado esto en escenarios de desplazamiento hacia abajo similares, por lo que no veo por qué no funcionaría en su caso. Espero que esto sea útil 🙂