Python/Selenium button click on class does not work - python

<fieldset class = "A">
::before
<button class = "ButtonA" type ="submit">
::before
Click Me
</button>
::after
</fieldset>
I am trying to open a page and click a button on the page. However, no matter what function call I make it throws the same message telling my the class does not exist, however, I'm positive it does. I don't see it nested in a iFrame. So what am I not understanding here, also why does it throw a error for a css selector if I am looking for an html class.
I get the following error
File "C:\Python38\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 564, in find_element_by_class_name
return self.find_element(by=By.CLASS_NAME, value=name)`
File "C:\Python38\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 976, in find_element
return self.execute(Command.FIND_ELEMENT, { `
File "C:\Python38\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute
self.error_handler.check_response(response)`
File "C:\Python38\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response`
raise exception_class(message, screen, stacktrace)selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":".ButtonA"}`
driver.get("https://www.websiteWithButton.com")
#ensure html is loaded
driver.implicitly_wait(10)
#check for iFrame
#driver.switch_to.frame('PossibleiFrame')
#ButtonA is a a class that exists, I'm positive.
button = driver.find_element_by_class_name('ButtonA')
button.click()

I just give my entire solution.Base on your step
driver.get("https://www.websiteWithButton.com")
#ensure html is loaded
driver.implicitly_wait(10)
#check for iFrame
#driver.switch_to.frame('PossibleiFrame')
#ButtonA is a a class that exists, I'm positive.
button_list = driver.find_elements_by_tag('button')
for i, x in enumerate(button_list):
print(f'Index: {i}')
x.click() # you can find the index of ButtonA you want by click them step by step
After you get the index of ButtonA, just click it directly
button_list[index].click().
the index reflect the position of ButtonA in page source, as long as page source unchange, you can access it.
This might not be a smart way, but at least you can find button element you want as long as you are in correct page.

Related

Message: element not interactable Error While Sending Keys [search_bar.send_keys(course_name)] To Search Bar Of Youtube using selenium python

I tried Most of the solution of StackOverflow but didn't work for me
I am trying to send some course name to youtube search bar using selenium python it works fine before but now it gives this error while doing this
And search_bar.send_keys(course_name) works fine for other websites but not in YT
Traceback (most recent call last):
File "src/gevent/greenlet.py", line 766, in gevent._greenlet.Greenlet.run
File "/home/sh4d0w/PycharmProjects/AutoMate/venv/lib/python3.7/site-packages/eel/__init__.py", line 257, in _process_message
return_val = _exposed_functions[message['name']](*message['args'])
File "/home/sh4d0w/PycharmProjects/AutoMate/SmallTalk.py", line 72, in SingleQueryinputValue
RecommendCourse.getUdacityCourse(str(val))
File "/home/sh4d0w/PycharmProjects/AutoMate/RecommendCourse.py", line 160, in getUdacityCourse
getYoutubeCourse(course_name, driver)
File "/home/sh4d0w/PycharmProjects/AutoMate/RecommendCourse.py", line 98, in getYoutubeCourse
search_bar.send_keys(course_name)
File "/home/sh4d0w/PycharmProjects/AutoMate/venv/lib/python3.7/site-packages/selenium/webdriver/remote/webelement.py", line 479, in send_keys
'value': keys_to_typing(value)})
File "/home/sh4d0w/PycharmProjects/AutoMate/venv/lib/python3.7/site-packages/selenium/webdriver/remote/webelement.py", line 633, in _execute
return self._parent.execute(command, params)
File "/home/sh4d0w/PycharmProjects/AutoMate/venv/lib/python3.7/site-packages/selenium/webdriver/remote/webdriver.py", line 321, in execute
self.error_handler.check_response(response)
File "/home/sh4d0w/PycharmProjects/AutoMate/venv/lib/python3.7/site-packages/selenium/webdriver/remote/errorhandler.py", line 242, in check_response
raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.ElementNotInteractableException: Message: element not interactable
(Session info: chrome=81.0.4044.129)
2020-04-29T08:00:02Z <Greenlet at 0x7fd2089c67b8: _process_message({'call': 2.1877049007713376, 'name': 'SingleQueryi, <geventwebsocket.websocket.WebSocket object at 0x7)> failed with ElementNotInteractableException
Code sample
option = webdriver.ChromeOptions()
option.add_argument("window-size=1200x600");
driver = webdriver.Chrome('/usr/bin/chromedriver', options=option)
driver.get("https://www.youtube.com")
getYoutubeCourse(course_name, driver)
getYoutubeCourse() function body
def getYoutubeCourse(course_name, driver):
time.sleep(2)
search_bar = driver.find_element_by_xpath('//*[#id="search"]')
search_bar.send_keys(course_name)
search_bar_button = WebDriverWait(driver, 5).until(EC.element_to_be_clickable(
(By.XPATH, '//*[#id="search-icon-legacy"]')))
search_bar_button.click()
......
then after this, the logic of scraping the youtube links is there
I also tried web driver wait and all, and my drivers are also up to date
Please help I am new in python and selenium
There is 3 elements found by xpath : //*[#id="search"]
You have to correct it to
//input[#id="search"]
I also got the same problem. After hours filled with agony I finally found a solution.
While copying xpath (or any another id you are using to refer it) it points out to the entire search box container. After repeated trials and observations I found that a particular element (of the webpage which you can view in developer) with input mentioned was the one to be looked for:
<input id="search" autocapitalize="none" autocomplete="off" autocorrect="off" name="search_query" tabindex="0" type="text" spellcheck="false" placeholder="Search" aria-label="Search" aria-haspopup="false" role="combobox" aria-autocomplete="list" dir="ltr" class="ytd-searchbox" style="outline: none;">
The problem while copying xpath is that you get id of the container.
So use 'copy full xpath'. This will refer to the particular element within the container which can be given input.
full xpath for the input element of search box is:
'/html/body/ytd-app/div/div/ytd-masthead/div[3]/div[2]/ytd-searchbox/form/div/div[1]/input'
You can use it like:
search_bar = driver.find_element_by_xpath('/html/body/ytd-app/div/div/ytd-masthead/div[3]/div[2]/ytd-searchbox/form/div/div[1]/input')
search_bar.send_keys(song_name)
time.sleep(1)#You can skip this.
search_bar.send_keys(Keys.ENTER)
P.S. Excuse the terminology. I am also a beginner and this is my first answer.
You can try this:
from selenium.webdriver.common.keys import Keys
def getYoutubeCourse(course_name, driver):
time.sleep(2)
search_bar = driver.find_element_by_xpath('//input[#id="search"]')
search_bar.send_keys(course_name)
time.sleep(1)
search_bar.send_keys(Keys.ENTER)
....
I'm the one who also got the same problem.
I tried full Xpath but it was still not working. And then, I plused [0] after xpath and it worked.
full xpath for the input element of search box is:
'/html/body/ytd-app/div/div/ytd-masthead/div[3]/div[2]/ytd-searchbox/form/div/div[1]/input[0]'
And this if full source code
chrome_driver = '/Users/Downloads/chromedriver'
driver = webdriver.Chrome(chrome_driver)
driver.get('https://www.youtube.com')
search_bar = driver.find_elements_by_xpath('/html/body/ytd-app/div/div/ytd-masthead/div[3]/div[2]/ytd-searchbox/form/div/div[1]/input')[0]
time.sleep(2)
search_bar.send_keys("black pink")
time.sleep(2)
search_bar.send_keys(Keys.RETURN)
Thank you

Xpath exists in chrome, but not in my script. no such element: Unable to locate element:

I am trying to find the button element by xpath, which is found when I type it in chrome, but the script gives me the no attribute id error. I have tried switching into an iframe and frame, I have used webdriver.wait to wait for the button element to show and none of those work. I would also like to cycle through and click the first button if it says "Follow" and then move to the next button if it says "Follow".The Script runs on chrome and I am trying to do this on instagram Html here
popup = browser.find_element_by_xpath('//button[#class="_qv64e _gexxb _4tgw8 _njrw0"]')
ActionChains(browser)\
.move_to_element(popup).click()\
.perform()
File "/Users/trevaroneill/PycharmProjects/Insta/instafollow.py", line 91, in <module>
popup = browser.find_element_by_xpath('//button[#class="_qv64e _gexxb _4tgw8 _njrw0"]')
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"xpath","selector":"//button[#class="_qv64e _gexxb _4tgw8 _njrw0"]"}
Since you are using find_elements, you should rather write:
ActionChains(browser)\
.move_to_element(popup[0]).click()\
.perform()
in order to access first element of the list returned by find_elements.
The problem is if you have several webelements selected by your xpath, in this case, it is not sure that the first one is the one you actually aim at. I would suggest you use find_element if there are no particular reason for which you are using find_elements
You are getting the wanted element in a list.
change
popup = browser.find_elements_by_xpath('//button[#class="_qv64e _gexxb _4tgw8 _njrw0"]')
into
popup = browser.find_element_by_xpath('//button[#class="_qv64e _gexxb _4tgw8 _njrw0"]')
find_element and not find_elements.

iterated click on href link list

I want to iterate through href links and click on each one. My problem is that I cannot extract href, because it is not help me, when I click it i get an empty page. I should click directly on list of buttons.
There are total 21 nodes, hrefList consist of 21 items.
Here is example of html :
<div class="inner25">
כרטיס רופא >
</div>
<div class="inner25">
כרטיס רופא >
</div>
My code is :
hrefList = driver.find_elements_by_xpath(".//a[contains(text(), 'כרטיס רופא')]")
for link in hrefList:
link.click()
The link of site is :https://www.ida.org.il/?pageType=19&langId=1&paramIds=%2Con_321%2Con_322%2Con_354%2Con_355%2Con_320&scope=&parameterSearch=
In every square you see the doctor. i want to click on each of them.
I found that loop open first link and then failed. What can be a problem?
I tried also this code:
for href in range(1,3):
hrefList[href].click()
First link it open, and then failed.
This is an error :
File "C:\Program Files\Python36\lib\site-packages\selenium\webdriver\remote\webelement.py", line 78, in click
self._execute(Command.CLICK_ELEMENT)
File "C:\Program Files\Python36\lib\site-packages\selenium\webdriver\remote\webelement.py", line 499, in _execute
return self._parent.execute(command, params)
File "C:\Program Files\Python36\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 297, in execute
self.error_handler.check_response(response)
File "C:\Program Files\Python36\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 194, in check_response
raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.StaleElementReferenceException: Message: stale element reference: element is not attached to the page document
(Session info: chrome=60.0.3112.113)
(Driver info: chromedriver=2.32.498550 (9dec58e66c31bcc53a9ce3c7226f0c1c5810906a),platform=Windows NT 10.0.15063 x86_64)
Use this xapth it returns 21 nodes of your website
//a[contains(#href, 'https://www.ida.org.il/?categoryId=96318&itemId')]
This code in Java. You have to come back to main page and locate element
public static void main(String[] args) {
String URL="https://www.ida.org.il/?pageType=19&langId=1&paramIds=%2Con_321%2Con_322%2Con_354%2Con_355%2Con_320&scope=&parameterSearch=";
WebDriver driver=new FirefoxDriver();
driver.get(URL);
List<WebElement> links=driver.findElements(By.xpath("//a[contains(#href, 'https://www.ida.org.il/?categoryId=96318&itemId')]"));
System.out.println("Total links: "+links.size());
for (int i=0;i<links.size();i++) {
links.get(i).click();
System.out.println(i+"Current URL is: "+driver.getCurrentUrl());
driver.navigate().back();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
links=driver.findElements(By.xpath("//a[contains(#href, 'https://www.ida.org.il/?categoryId=96318&itemId')]"));
}
driver.close();
}
Beginner issue.You can find a lot of questions related to stale element exception.
You should never do actions that are changing the page in a loop using a list of already found objects, else you will get stale element since the element is not attached to the DOM.
You should save all hrefs values in a list, then in your loop you should do the find element based on the saved href value and call click on the found element.
If the page changes/reloads you need to find the element again.

Selenium with Python: Stale Element Reference Exception

Working from Test Driven Development with Python, and I'm currently encountering a 'StaleElementReferenceException' when running the functional test immediately after migration. Here's the full text of the error:
ERROR: test_start_and_retrieve_list (__main__.NewVisitorTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "functional_tests.py", line 53, in test_start_and_retrieve_list
rows = table.find_elements_by_tag_name('tr')
File "/usr/local/lib/python3.5/dist-packages/selenium/webdriver/remote/webelement.py", line 237, in find_elements_by_tag_name
return self.find_elements(by=By.TAG_NAME, value=name)
File "/usr/local/lib/python3.5/dist-packages/selenium/webdriver/remote/webelement.py", line 527, in find_elements
{"using": by, "value": value})['value']
File "/usr/local/lib/python3.5/dist-packages/selenium/webdriver/remote/webelement.py", line 493, in _execute
return self._parent.execute(command, params)
File "/usr/local/lib/python3.5/dist-packages/selenium/webdriver/remote/webdriver.py", line 256, in execute
self.error_handler.check_response(response)
File "/usr/local/lib/python3.5/dist-packages/selenium/webdriver/remote/errorhandler.py", line 194, in check_response
raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.StaleElementReferenceException: Message: The element reference of <table id="id_list_table"> stale: either the element is no longer attached to the DOM or the page has been refreshed
----------------------------------------------------------------------
Ran 1 test in 8.735s
FAILED (errors=1)
Here's the test:
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import unittest
class NewVisitorTest(unittest.TestCase):
def setUp(self):
self.browser = webdriver.Firefox()
self.browser.implicitly_wait(3)
def tearDown(self):
self.browser.close()
def check_for_row(self, row_text):
table = self.browser.find_element_by_id('id_list_table')
rows = table.find_elements_by_tag_name('tr')
self.assertIn(row_text, [row.text for row in rows])
def test_start_and_retrieve_list(self):
self.browser.get('http://localhost:8000')
self.assertIn('To-Do', self.browser.title)
header_text = self.browser.find_element_by_tag_name('h1').text
self.assertIn('To-Do', header_text)
inputbox = self.browser.find_element_by_id('id_new_item')
self.assertEqual(
inputbox.get_attribute('placeholder'),
'Enter a to-do item'
)
inputbox.send_keys('Buy peacock feathers')
inputbox.send_keys(Keys.ENTER)
self.check_for_row('1: Buy peacock feathers')
inputbox = self.browser.find_element_by_id('id_new_item')
inputbox.send_keys('Use peacock feathers to make a fly')
inputbox.send_keys(Keys.ENTER)
table = self.browser.find_element_by_id('id_list_table')
rows = table.find_elements_by_tag_name('tr')
self.check_for_row('1: Buy peacock feathers')
self.check_for_row('2: Use peacock feathers to make a fly')
self.fail('Finish the test!')
if __name__ == '__main__':
unittest.main(warnings='ignore')
How do I configure the test to prevent this? Selenium's own page says this issue can occur when the page refreshes, but this is a necessary part of the application logic as it's configured so far.
Add these imports:
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
Change these lines
inputbox.send_keys(Keys.ENTER)
self.check_for_row('1: Buy peacock feathers')
to:
inputbox.send_keys(Keys.ENTER)
WebDriverWait(self.browser, 10).until(
expected_conditions.text_to_be_present_in_element(
(By.ID, 'id_list_table'), 'Buy peacock feathers'))
self.check_for_row('1: Buy peacock feathers')
This replaces the time.sleep(1) with something more "reasonable"
I have been using selenium for a while now so I understand the struggles of the Stale Element Exception. While not perfect, selenium provides a series of "wait" commands to allow for the website to load complete. Unfortunately, its not perfect as loading can take different time on each run, but these are the tools provided by selenium.
I haven't worked in python but have worked on java/selenium. But,I can give you the idea to overcome staleness.
Generally we will be getting the Stale Exception if the element attributes or something is changed after initiating the webelement. For example, in some cases if user tries to click on the same element on the same page but after page refresh, gets staleelement exception.
To overcome this, we can create the fresh webelement in case if the page is changed or refreshed. Below code can give you some idea.(It's in java but the concept will be same)
Example:
webElement element = driver.findElement(by.xpath("//*[#id='StackOverflow']"));
element.click();
//page is refreshed
element.click();//This will obviously throw stale exception
To overcome this, we can store the xpath in some string and use it create a fresh webelement as we go.
String xpath = "//*[#id='StackOverflow']";
driver.findElement(by.xpath(xpath)).click();
//page has been refreshed. Now create a new element and work on it
driver.fineElement(by.xpath(xpath)).click(); //This works
Another example:
for(int i = 0; i<5; i++)
{
String value = driver.findElement(by.xpath("//.....["+i+"]")).getText);
System.out.println(value);
}
Hope this helps you. Thanks
To prevent an element to become stale, place a new element on the current page, hit the link and wait until the element is not available anymore. Then wait for an element on the new page to appear
script_to_execute = """
var new_element = document.createElement('span');
new_element.setAttribute('id', 'wait4me');
document.body.appendChild(new_element);"""
self.driver.execute_script(script_to_execute)
self.driver.find_element_by_xpath("{}".format(locator)).click()
WebDriverWait(self.driver, self.time_out).until (
lambda x: not x.find_elements_by_id("wait4me"))
This issue happens when the loop starts before an updated page has fully loaded. Especially when you update a page in an application or a form.
One workaround is to place an element on the current page, then update and use the WebDriverWait statement until the element is not found anymore.
Then start your loop. (Otherwise the reload happens during the loop...)
I read the same book as you do and encountered the same problem (solutions from this page didn't work for me).
Here's how I resolved it.
Problem
Exception is thrown whenever you try to access a stale object. So we have to wait for situation when this exception is NOT thrown anymore.
My solution
I created method that waits for my actions until they pass
from selenium.common.exceptions import StaleElementReferenceException
[...]
def stale_aware_for_action(self, action):
while(True):
try:
action()
break
except StaleElementReferenceException:
continue
And in test method I defined actions that I want to wait to finish:
def test_can_start_a_list_and_retrieve_it_later(self):
[...]
def insert_second_item_to_inputbox():
inputbox = self.browser.find_element_by_id('id_new_item')
inputbox.send_keys('Use peacock feathers to make a fly')
inputbox.send_keys(Keys.ENTER)
self.stale_aware_for_action(insert_second_item_to_inputbox)
def check_for_first_item():
self.check_for_row_in_list_table('1: Buy peacock feathers')
def check_for_second_item():
self.check_for_row_in_list_table('2: Use peacock feathers to make a fly')
self.stale_aware_for_action(check_for_first_item)
self.stale_aware_for_action(check_for_second_item)

Click on a dropdown element menu with Selenium Webdriver

I'm trying to automate an administration task, so far I have made selenium to click on an element to show a dropdown menu.
When it comes the time to click on one of those menu elements I've got an error saying that the element must be displayed.
Code:
driver = webdriver.Chrome()
driver.implicitly_wait(10)
driver.get(url)
doc = driver.find_element_by_css_selector('td.ms-vb-title > table')
try:
doc.click()
time.sleep(4)
menu = driver.find_element_by_xpath('//menu/span[5]')
time.sleep(4)
print dir(menu)
menu.click()
except:
traceback.print_exc()
driver.quit()
Error:
Traceback (most recent call last):
File "aprobar_docs.py", line 22, in main
menu.click()
File "C:\Python27\lib\site-packages\selenium\webdriver\remote\webelement.py",
line 52, in click
self._execute(Command.CLICK_ELEMENT)
File "C:\Python27\lib\site-packages\selenium\webdriver\remote\webelement.py",
line 205, in _execute
return self._parent.execute(command, params)
File "C:\Python27\lib\site-packages\selenium\webdriver\remote\webdriver.py", l
ine 156, in execute
self.error_handler.check_response(response)
File "C:\Python27\lib\site-packages\selenium\webdriver\remote\errorhandler.py"
, line 147, in check_response
raise exception_class(message, screen, stacktrace)
ElementNotVisibleException: Message: u'Element must be displayed to click'
As you can see the code waits a lot to get the element loaded. I've also tried to set the element's is_displayed property to True but didn't work neither.
Note: the element that's not displayed is the one on the xpath search, it is present because I've logged it with dir(menu)
Edit:
The menu variable is not the menu itself it's one of the spans that are elements of the menu, doc is the Perfil html element getting clicked to show the dropdown.
Edit 2:
Inspecting the DOM on chrome tools, when you click a doc a new menu gets created on the tree, I don't know if it's because of an ajax call or vanilla js, I don't think it's really that important how it's created. I can't retrieve it from the page and make a python object from it, it's just not being displayed at least on code.
Final Edit:
I ended up executing some JavaScript to make it work. Apparently when Selenium finds the menu item the first element that triggers the menu drop down loses the focus and it makes the menu invisible again, if you don't select a menu item and wait for some time the menu dropdown still is shown, if you try to select one element from the menu the menu disappears.
Why don't you select an option like this
el = driver.find_element_by_id('id_of_select')
for option in el.find_elements_by_tag_name('option'):
if option.text == 'The Options I Am Looking For':
option.click() # select() in earlier versions of webdriver
If your click is not firing an ajax call to populate your list, you don't actually need to execute the click.
You need to find the link of the target. You don't really click elements, you click links... (or rather, you click elements with links inside them). That being said, the most sure fire way to click a link is to isolate the link element.
frame = driver.find_element_by_id('this_is_your_frame_name')
links = frame.find_elements_by_xpath('.//a')
links[1].click()
or alternatively,
for link in links:
if link.text() == "Administratar Permisos":
link.click()

Categories