Python Selenium: StaleElementReferenceException / set aria-pressed = "true" - python

I am using Python Selenium trying to get some data from a website and need to change the day of a date.
I tried the following: Get the table with all dates and iterate through all tds. If the right day appears click. Unfortunately that does not work. It prints the correct numbers but it does not click on the one it should or any.
day_table = depar_date.find_element_by_xpath("/html/body/div[8]/section/div/div/div[2]/div/table/tbody")
day_table.click()
for row in test.find_elements_by_css_selector('tr'):
for cell in row.find_elements_by_tag_name('td'):
print(cell.text)
if cell.text == "15":
cell.click()
I get the following error message:
StaleElementReferenceException: stale element reference: element is
not attached to the page document
I also see that for the selected day aria-pressed = "true", is there a way to set this "true" for the correct day?
many thanks for any help.

I guess you need to perform the action on the button itself, not the "tr" element because the event listener is on the button. Could you try to do the following and let me know what happens:
depar_date.find_element_by_xpath("//td/button[text()="15"]").click()

Related

Get value from pseudo element with python

I would like to get the price value from the pseudo element you see on the picture below. If I hover over the mouse only then I can see some value like the price it costs( that is what I need). I found the "move_to_element" so now I can hover over the mouse with the program but I still cant get the price out of the element.
The problem is that even if I hover over my mouse I cant see the element opening in the inspector tab.
Thank you!
This is the code I would like to get out the :before element from:
<div onclick="Game.UpgradesById[503].click(event);" class="crate upgrade enabled" onmouseout="Game.setOnCrate(0);Game.tooltip.shouldHide=1;" onmouseover="if (!Game.mouseDown) {Game.setOnCrate(this);Game.tooltip.dynamic=1;Game.tooltip.draw(this,function(){return function(){return Game.crateTooltip(Game.UpgradesById[503],'store');}();},'store');Game.tooltip.wobble();}" id="upgrade0" style="background-position:-1056px -1296px;"></div>
After this there is an en element ::before and end of the div
For your query on how to extract only the value:
Split the output x with the delimiter of next line: \n and then get it's first index. That would give you the value. Below are the lines.
spl = int(x.split("\n")[0])
print(f'value: {spl}')
print(type(spl))
Output:
15
???
[owned : 0]
value: 15 # this line is fetched using the above code in this answer. It fetches the value from the `x`
<class 'int'>
When I visited the website manually, I get somewhat like what you showed, but when I run through automation, it does not show any Upgrades. Hence, I had to stick to checking the identities (like Grandma, Cursor, etc); but they didn't show in automated version. Only the users ??? are seen. Snapshot
Eventually, I tried it with ??? only. I tried a different approach, instead of using ::before or ::after, I tried to see if any tooltip exists somewhere, and it does exist. The only caveat is that I was able to fetch all the text from the hover (from which again we have to extract what is required through code). On this context, here is the code:
driver.get("https://orteil.dashnet.org/cookieclicker/")
time.sleep(10)
ActionChains(driver).move_to_element(driver.find_element(By.ID, 'product0')).perform()
time.sleep(1)
x = driver.find_element(By.ID, 'tooltip').text
print(x)
Here is the output:
15
???
[owned : 0]
Process finished with exit code 0
Please change the element to the element that you want and you should get the result.

XPATH syntax for element in {} with Selenium for Python

I am currently trying to click each button on a webpage with Selenium in Python, the class and text is always the same for each button but each button has different ids. The ids, however, are within "data-paramaters" in {} and I can't figure out how to get the correct syntax for the xpath.
Here is a snippet of the website for one of the buttons:
<span class="contains-icon-details gc-btn gc-btn--s" data-isneededpromise="false" data-parameters="{"partner":"gs", "realId": "8da1d6a9-44d1-4556-bc12-92699749a30a", "tnId": "102086182829", "type": "details"}">More Details</span>
It seems the realId and the tnId are unique, so I would need to find the buttons with either one of those.
This works:
driver.find_element_by_xpath("//span[#class='contains-icon-details gc-btn gc-btn--s']").click()
but of course only for the first button as the class is always the same.
I tried something like this:
driver.find_element_by_xpath("//*[contains(#tnId, '102086182829')]").click()
but I get
Unable to locate element: //*[contains(#tnId, '102086182829')]
so definitely not the correct syntax.
I tried to find a solution online, but with no luck so far. Can anybody point me into the right direction? Thanks in advance.
In case realId value or tnId value is unique your XPath can be
driver.find_element_by_xpath("//*[contains(#data-parameters, '8da1d6a9-44d1-4556-bc12-92699749a30a')]").click()
or
driver.find_element_by_xpath("//*[contains(#data-parameters, '102086182829)]").click()
you should filter by the "data-parameters" attribute.
Try
driver.find_element_by_xpath("//span[contains(#data-parameters, '102086182829')]").click()
This is a dirty implementation of what you need. It would be better to extract the data-parameters field, deserialize JSON and check for the needed field;
spans = driver.find_element_by_xpath("//span[#class='contains-icon-details gc-btn gc-btn--s']")
for span in spans:
data_parameters = span.get_attribute("data-parameters")
try:
data_parameters = json.loads(data_parameters)
except:
continue
if 'tnId' in data_parameters and data_parameters['tnId'] == "102086182829":
span.click()
break

Refresh page until value changes in Python Selenium

I am trying to validate that a value changes to the correct text and if it does not to refresh the page and check again for up to set time.
I have tried while-loops, if statements and nested variations of both with no success. I am not even sure how to format it as this point.
element = driver.find_element_by_xpath('xpath')
While True:
if element contains textA
break
else if element contains textB
driver.refresh()
else
error
Something along those lines. Ignore any syntax errors, I am just trying to get the idea across
I have also tried using EC and By with no luck
Edit: Adding some details
So what I have is a table. I am inserting a new row with no problems. Then I need to check that one of the column values of the new row gets updated from 'new' to 'old' which usually takes about anywhere from 30secs to 2mins. This is all viewable from a web ui. I need to refresh the page in order to see the value change. I wish I had some more detailed code or error to post along with it but honestly I am just beginning to learn Selenium
Can you please try the following :
while True:
try:
driver.find_element_by_xpath('xpath'):
except NoSuchElementException:
driver.refresh
else:
print("Text found")
break
Note: I suggest to create text-based XPath to avoid an extra line of code to get and compare text.

Detecting when an element is refreshed, even if the value doesn't change

(Selenium/webscraping noob warning.)
selenium 3.141.0
chromedriver 78
MacOS 10.14.6
I'm compiling a list of URLs across a range of dates for later download. The URLs are in a table that displays information for the date selected on a nearby calendar. When the user clicks a new date on the calendar, the table is updated asynchronously with a new list of URLs or – if no files exist for that date – with a message inside a <td class="dataTables_empty"> tag.
For each date in the desired range, my code clicks the calendar, using WebDriverWait with a custom expectation to track when the first href value in the table changes (indicating the table has finished updating), and scrapes the URLs for that day. If no files are available for a given date, the code looks for the dataTables_empty tag to go away to indicate the next date's URLs have loaded.
if current_first_uri != NO_ATT_DATA:
element = WebDriverWait(browser, 10).until_not(
text_to_be_present_in_href((
By.XPATH, first_uri_in_att_xpath),
current_first_uri))
else:
element = WebDriverWait(browser, 10).until_not(
EC.presence_of_element_located((
By.CLASS_NAME, "dataTables_empty")))
This works great in all my use cases but one: if two or more consecutive days have no data, the code doesn't notice the table has refreshed, since the dataTables_empty class remains in the table (and the cell is identical in every other respect).
In the Chrome inspector, when I click from one date without data to another, the corresponding <td> flashes pink. That suggests the values are being updated, even though their values remain the same.
Questions:
Is there a mechanism in Selenium to detect that the value was refreshed, even if it hasn't changed?
If not, any creative ideas on how to determine the table has refreshed in the problem use case? I don't want to wait blindly for some arbitrary length of time.
UPDATE: The accepted answer answered the latter of the two questions, and I was able to replace my entire detection scheme using the MutationObserver.
You could use a MutationObserver:
driver.execute_script("""
new MutationObserver(() => {
window.lastRefresh = new Date()
}).observe(document.querySelector('table.my-table'), { attributes: true, childList: true, subtree: true } )
""")
And get the last time the table dom changed with:
lastRefresh = driver.execute_script("return window.lastRefresh")
I use this below method to check if element has gone stale or not. Usually expecting false.
The same may help in your case when you are expecting true.
isElementStale(driver, element) {
try:
wait = WebDriverWait(browser, 2)
element.isEnabled()
element = wait.until(EC.element_to_be_clickable(element))
if element != null:
return False
except:
print('')
return True
}
So you can pass element to this method and check if any change has occured to it like
# element = Get First element
# Make changes that causes the refresh
if (isElementStale(driver, element)):
print('Element refreshed')
else:
print('Element Not refreshed')

How to use Selenium function back() and I loss my variable in Python?

I'm new to Python.
I met a BIG problem in Python!!
I visit a website and put about 200 options from a dropdownlist in a array.
I want to click every options in the array and click javascript button to submit.
Take something I want from that page and back to previous page click another option.
Do those actions about 200 times in a for loop.
Here is the code:
for option in arrName:
if count > 0:
option.click()
string = u'Something'
link2 = browser.find_element_by_link_text(string.encode('utf8'))
link2.click()
//"do something I want"
browser.back()
count = count +1
In this code, I don't want to use first option.
PROBLEM comes,after the program click the second option, click the link2, and browser.back(), it answer me:
` StaleElementReferenceException: Message: stale element reference: element
is not attached to the page document
that means the options in array disappear?
How should I use the options in the array when the browser.back() in for loop?
Thanks
Yes, this is happening because of DOM refresh. You cannot simply iterate over an array and click back and forth. Best option is to find the element in runtime and then click. Avoid option.click() instead, find the next element with find_element. If you are not sure how to accomplish that then please provide the html

Categories