StaleElementException al iterar con Python
Estoy intentando crear un raspador web básico para los resultados de Amazon. Mientras repito los resultados, a veces llego a la página 5 (a veces solo a la página 2) de los resultados y luego StaleElementException
aparece un. Cuando miro el navegador después de que se lanza la excepción, puedo ver que el controlador/página no se desplazó hacia abajo hasta donde están los números de página (barra inferior).
Mi código:
driver.get('https://www.amazon.com/s/ref=nb_sb_noss_1?url=search-alias%3Daps&field-keywords=sonicare+toothbrush')
for page in range(1,last_page_number +1):
driver.implicitly_wait(10)
bottom_bar = driver.find_element_by_class_name('pagnCur')
driver.execute_script("arguments[0].scrollIntoView(true);", bottom_bar)
current_page_number = int(driver.find_element_by_class_name('pagnCur').text)
if page == current_page_number:
next_page = driver.find_element_by_xpath('//div[@id="pagn"]/span[@class="pagnLink"]/a[text()="{0}"]'.format(current_page_number+1))
next_page.click()
print('page #',page,': going to next page')
else:
print('page #: ', page,'error')
Revisé esta pregunta y supongo que se puede aplicar una solución similar, pero no estoy seguro de cómo encontrar algo en la página que desaparece. Además, en función de la rapidez con la que se imprimen las declaraciones, puedo ver que implicitly_wait(10)
en realidad no está esperando 10 segundos completos.
La excepción apunta a la línea que comienza con "driver.execute_script". Esta es la excepción:
StaleElementReferenceException: Message: The element reference of <span class="pagnCur"> is stale; either the element is no longer attached to the DOM, it is not in the current frame context, or the document has been refreshed
A veces obtengo un ValueError:
ValueError: invalid literal for int() with base 10: ''
Entonces, estos errores/excepciones me llevan a creer que algo sucede al esperar a que la página se actualice por completo.
Si solo desea que su secuencia de comandos repita todas las páginas de resultados, no necesita ninguna lógica complicada; simplemente haga clic en el botón Siguiente mientras sea posible:
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait as wait
from selenium.common.exceptions import TimeoutException
driver = webdriver.Chrome()
driver.get('https://www.amazon.com/s/ref=nb_sb_noss_1?url=search-alias%3Daps&field-keywords=sonicare+toothbrush')
while True:
try:
wait(driver, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, 'a > span#pagnNextString'))).click()
except TimeoutException:
break
PD: también tenga en cuenta que implicitly_wait(10)
no debe esperar 10 segundos completos , sino esperar hasta 10 segundos para que el elemento aparezca en HTML DOM . Entonces, si el elemento se encuentra dentro de 1 o 2 segundos, entonces la espera ha terminado y no esperará a descansar de 8 a 9 segundos...
Este mensaje de error...
StaleElementReferenceException: Message: The element reference of <span class="pagnCur"> is stale; either the element is no longer attached to the DOM, it is not in the current frame context, or the document has been refreshed
...implica que la referencia anterior del elemento ahora está obsoleta y la referencia del elemento ya no está presente en el DOM de la página.
Las razones comunes detrás de este problema son:
- El elemento ha cambiado de posición dentro del HTML.
- El elemento ya no está adjunto al ÁRBOL DOM.
- Se ha actualizado la página web en la que formaba parte el elemento.
- La instancia anterior del elemento ha sido actualizada por JavaScript o AjaxCall .
Este caso de uso
Manteniendo su concepto de desplazarsescrollIntoView()
e imprimir un par de mensajes de depuración útiles, hice algunos ajustes menores que inducen WebDriverWait y puede usar la siguiente solución :
Bloque de código:
from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC options = Options() options.add_argument("start-maximized") options.add_argument('disable-infobars') options.add_argument("--disable-extensions") driver = webdriver.Chrome(chrome_options=options, executable_path=r'C:\Utility\BrowserDrivers\chromedriver.exe') driver.get("https://www.amazon.com/s/ref=nb_sb_noss_1?url=search-alias%3Daps&field-keywords=sonicare+toothbrush") while True: try: current_page_number_element = WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "span.pagnCur"))) driver.execute_script("arguments[0].scrollIntoView(true);", current_page_number_element) current_page_number = current_page_number_element.get_attribute("innerHTML") WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "span.pagnNextArrow"))).click() print("page # {} : going to next page".format(current_page_number)) except: print("page # {} : error, no more pages".format(current_page_number)) break driver.quit()
Salida de consola:
page # 1 : going to next page page # 2 : going to next page page # 3 : going to next page page # 4 : going to next page page # 5 : going to next page page # 6 : going to next page page # 7 : going to next page page # 8 : going to next page page # 9 : going to next page page # 10 : going to next page page # 11 : going to next page page # 12 : going to next page page # 13 : going to next page page # 14 : going to next page page # 15 : going to next page page # 16 : going to next page page # 17 : going to next page page # 18 : going to next page page # 19 : going to next page page # 20 : error, no more pages