0

I have the following code to find some buttons that I need to click using Selenium and Python:

elems = driver.find_elements_by_xpath('//div[@id = "RangeViewer"]//button[starts-with(@class,"range")]')

print(len(elems))
print("\n")


for e in elems:
    print("ID attrib: " + e.get_attribute("id"))
    print("text: " + e.text )
    print("class attrib: " + e.get_attribute("class"))
    print(e.get_attribute("xpath"))
    # e.click()
    time.sleep(5)
    print("_________\n")

And I get the following print out:

4


ID attrib: Fold 2.2
text: Fold 2.2
0.58
class attrib: range-button
None
_________

ID attrib: Call 8
text: Call 8
0.23
class attrib: range-button active-tab
None
_________

ID attrib: Raise to 22.6
text: Raise to 22.6
0.15
class attrib: range-button
None
_________

ID attrib: All-In 100
text: All-In 100
0.04
class attrib: range-button
None
_________

So all the elements that I want are there but as soon as I uncomment e.click() I get either stale element error or NoSuchElementError from Selenium. The behaviour is erratic, sometimes it clicks on 2 elements before reporting the error, sometimes it exits after first click.

I do not get it, what am I doing wrong?

Also, why do I get None from e.getattribute("xpath")? Shouldn't every element have an xpath?

rioZg
  • 530
  • 1
  • 6
  • 17
  • This might help: https://stackoverflow.com/questions/27003423/staleelementreferenceexception-on-python-selenium/57728860#57728860 – Cres Jan 30 '21 at 18:29

2 Answers2

0

stalement happens not when element is not present in the DOM , it happens when the reference changes emaning the DOM was modified after it was fetched

so when you click there will be some changes in the DOM and so the referrence of the element changes . Hence you will get that error.

You have to find the elements again:

elems = driver.find_elements_by_xpath('//div[@id = "RangeViewer"]//button[starts-with(@class,"range")]')

print(len(elems))
print("\n")

count=0
while count<len(elems):
    e=elems[count]
    count+=1
    print("ID attrib: " + e.get_attribute("id"))
    print("text: " + e.text )
    print("class attrib: " + e.get_attribute("class"))
    print(e.get_attribute("xpath"))
    e.click()
    time.sleep(5)
    print("_________\n")
    
    elems = WebDriverWait(driver, 10).until(
    EC.presence_of_all_elements_located((By.XPATH, '//div[@id = "RangeViewer"]//button[starts-with(@class,"range")]'))
)

For using webdriver wait import

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
PDHide
  • 18,113
  • 2
  • 31
  • 46
  • 1
    Thanks you code worked after I changed ++count to count += 1. – rioZg Feb 01 '21 at 19:33
  • However I would really like to understand: 1. why only when clicking these buttons I get stale error? Otherwise I use the same method for locating other buttons and clicking them without any errors. 2. my code actually clicks the element and the screen in Chrome changes, can I just ignore stale error? – rioZg Feb 01 '21 at 19:36
  • Because when you click there is changes in the page , which resets the element reference . When ever page structure changes after you finds an element , you have to find element again – PDHide Feb 01 '21 at 19:38
0

I had similar problems especially when using ActionChains. If you are using ActionChains, and the page has changed, be sure to reinstantiate your ActionChains object.

Something like this:

action1 = ActionChains(driver)
first = driver.find_element_by_css_selector("td[data-key='profile']")
action1.move_to_element(first).perform()
driver.back()
action2 = ActionChains(driver)
second = driver.find_element_by_xpath("//td[@data-key='search']")
action2.move_to_element(second).perform()

And I don't think we can extract xpath from any HTML element. I don't see it listed in the documentation either. https://www.w3schools.com/tags/ref_attributes.asp

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
Cres
  • 90
  • 8