Estoy tratando de obtener datos del sitio web de Sunshine List (http://www.sunshinelist.ca/) usando el paquete Selenium pero obtengo el siguiente error mencionado a continuación. De varias otras publicaciones relacionadas entiendo que necesito usar WebDriverWait para pedirle explícitamente al controlador que espere / actualice, pero no puedo identificar dónde y cómo debo llamar a la función.

Captura de pantalla de error

StaleElementReferenceException: Mensaje: La referencia del elemento de (tr class = "even") obsoleta: el elemento ya no está adjunto al DOM o la página se ha actualizado

import numpy as np
import pandas as pd
import requests
import time
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import StaleElementReferenceException
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

ffx_bin = FirefoxBinary(r'C:\Users\BhagatM\AppData\Local\Mozilla Firefox\firefox.exe')
ffx_caps = DesiredCapabilities.FIREFOX
ffx_caps['marionette'] = True
driver = webdriver.Firefox(capabilities=ffx_caps,firefox_binary=ffx_bin)
driver.get("http://www.sunshinelist.ca/")
driver.maximize_window()

tablewotags1=[]

while True:
    divs = driver.find_element_by_id('datatable-disclosures')
    divs1=divs.find_elements_by_tag_name('tbody')

    for d1 in divs1:
        div2=d1.find_elements_by_tag_name('tr')
        for d2 in div2:
            tablewotags1.append(d2.text)

    try:
        driver.find_element_by_link_text('Next →').click()
    except NoSuchElementException:
        break

year1=tablewotags1[0::10]
name1=tablewotags1[3::10]
position1=tablewotags1[4::10]
employer1=tablewotags1[1::10]  


df1=pd.DataFrame({'Year':year1,'Name':name1,'Position':position1,'Employer':employer1})
df1.to_csv('Sunshine List-1.csv', index=False)
-1
Mazahir Bhagat 31 oct. 2017 a las 21:33

5 respuestas

La mejor respuesta

Por cada clic en el botón "Siguiente", debe encontrar ese botón y hacer clic en él.

O haz algo como esto:

max_attemps = 10

while True:

    next = self.driver.find_element_by_css_selector(".next>a")

    if next is not None:

        break

    else:

        time.sleep(0.5)
        max_attemps -= 1

    if max_attemps == 0:

        self.fail("Cannot find element.")

Y después de que este código haga clic en la acción.

PD: también intente agregar solo time.sleep(x) después del elemento fiding y luego haga clic en la acción.

0
Ratmir Asanov 21 ene. 2019 a las 10:57
>>>Stale Exceptions can be handled using **StaleElementReferenceException** to continue to execute the for loop. When you try to get the element by any find_element method in a for loop.    

from selenium.common import exceptions  

and customize your code of for loop as:  

for loop starts:  
   try:  
        driver.find_elements_by_id("data")  //method to find element
        //your code 
   except exceptions.StaleElementReferenceException:  
        pass
1
Vishal 26 oct. 2018 a las 04:36

Si su problema es hacer clic en el botón "Siguiente" , puede hacerlo con el xpath:

driver = webdriver.Firefox(executable_path=r'/pathTo/geckodriver')
driver.get("http://www.sunshinelist.ca/")
wait = WebDriverWait(driver, 20)
el=wait.until(EC.presence_of_element_located((By.XPATH,"//ul[@class='pagination']/li[@class='next']/a[@href='#' and text()='Next → ']")))
el.click()
1
Davide Patti 31 oct. 2017 a las 20:39

Cuando levantas el StaleElementException eso significa que algo cambió en el sitio, pero no en la lista que tienes. Entonces, el truco es actualizar esa lista cada vez, dentro del ciclo como este:

while True:
    driver.implicitly_wait(4)
    for d1 in driver.find_element_by_id('datatable-disclosures').find_element_by_tag_name('tbody').find_elements_by_tag_name('tr'):
            tablewotags1.append(d1.text)

    try:
        driver.switch_to.default_content()
        driver.find_element_by_xpath('//*[@id="datatable-disclosures_wrapper"]/div[2]/div[2]/div/ul/li[7]/a').click()
    except NoSuchElementException:
        print('Don\'t be so cryptic about error messages, they are good\n
              ...Script broke clicking next') #jk aside put some info there
        break

Espero que esto te ayude, saludos.

Editar: Así que fui a dicho sitio web, el diseño es bastante sencillo, pero la estructura se repite como cuatro veces. Entonces, cuando vas a rastrear el sitio de esa manera, algo cambiará.

Así que he editado el código para desechar solo un árbol tbody. Este árbol proviene del primer datatable-disclousure. Y agregó algunas esperas.

0
MikaS 8 ene. 2018 a las 17:29

Prueba este código a continuación. Cuando el elemento ya no está asociado al DOM y se invoca la excepción StaleElementReferenceException, busque nuevamente el elemento para hacer referencia al elemento.

Tenga en cuenta que verifiqué con Chrome:

try:
    driver.find_element_by_css_selector('div[id="datatable-disclosures_wrapper"] li[class="next"]>a').click()
except StaleElementReferenceException:
    driver.find_element_by_css_selector('div[id="datatable-disclosures_wrapper"] li[class="next"]>a').click()
except NoSuchElementException:
    break
1
Ratmir Asanov 8 ene. 2018 a las 16:50