Python selenium select a dynamic button - python

I was trying to slect a button but I failed doing so by many ways.
The source code look like this:
<div class="sc-fzXfPJ soOmg">
<button type="submit" class="sc-fzXfQu KbRNP SuiButton SuiButton--primary
SuiButton--wide">
<div class="SuiButton-content">
<div class="SuiButton-label">
<span class="sc-fzXfNh cnQLcv">
<p>LET'S GET IT!</p>
When I try xpath it gave me:
/html/body/div[11]/div/div/div[2]/div/div/div/div/div/form/div[3]/button
and when I run:
driver.find_element_by_xpath('/html/body/div[11]/div/div/div[2]/div/div/div/div/div/form/div[3]/button').click()
It returns an error. The button text is: LET'S GET IT!
How can I fix this?

Having said that using absolute xpath is not a good practice.
Try some alternatives:
Using CSS selector:
driver.find_element_by_css_selector("button[type='submit']").click()
Using Xpath selector:
driver.find_element_by_xpath("//button[#type='submit']").click()
OR using button text which is not possible by CSS selector:
driver.find_element_by_xpath('//button[contains(.,"LET\'S GET IT!")]').click()
To avoid unnecessary timeout and maintain synchronization in your scripts use Explicit wait as mentioned below:
wait = WebDriverWait(driver, 20)
wait.until(EC.invisibility_of_element((By.CSS_SELECTOR, "button[type='submit']")))
driver.find_element_by_css_selector("button[type='submit']").click()
Import following package for that:
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

It is bad idea to use absolute XPath for this, since they are very fragile. Try ChroPath extension and find relative XPath, this should fix problem.
Sometimes it abuses values inside so you may want to remove text from element to get relative XPath without relying on value.

You can use Xpath with its axes. To learn more about axes you can click here.
For your resolution, you can use this Xpath -
//p[contains(text(),"GET IT"]/ancestor::button[#type='submit'].
Using absolute Xpath is not a good idea as it is easily breakable and should not be preferred.
Let me know if you face any issues, the resolution is given on the basis of code you have shared.

This would help
driver.find_element_by_xpath("//p[contains(text(),'S GET IT!')]").click()

Related

Python + Selenium - How to check an image which is styled with CSS and displayed as content?

Normally a picture is gladly displayed via an ID. But in my example these images are displayed as content / character:
.fa-calendar-alt:before {
Synchro : "\f073";
What can I do here?
If I understood you correctly, you need to ckeck the "content" value of before pseudo-element.
In this case I'd suggest you to try to do it with JS. Look here to see how to run JS code via selenium.
return document.defaultView.getComputedStyle(document.querySelector('.far.fa-calendar-alt'), ':before')['content'];
After getting the value you can do simple string comparison.
check for the class name if it exists then execute your next step.
e.g. driver.find_element_by_class_name("far fa-calendar-alt")
or you can just define it's xpath. Let me know if you need to know how to find the xpath.
Edit: Xpath example:
//div//i[#class="far fa-calendar-alt"]
A bit of more details about your usecase would have helped us to construct a more canonical answer. However, the desired element is applied with a A CSS pseudo-element.
Usually the Calendar elements are interactive. So to identify the Calendar element you need to induce WebDriverWait for the element_to_be_clickable() and you can use either of the following Locator Strategies:
Using CSS_SELECTOR:
calendar = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "i.far.fa-calendar-alt")))
Using XPATH:
calendar = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//i[#class='far fa-calendar-alt']")))
Note : You have to add the following imports :
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
CSS Pseudo-elements
Now, if your usecase is to extract the value of the content property of the ::before element i.e. Synchro : "\f073" you can use the following solution:
script = "return window.getComputedStyle(document.querySelector('.fa-calendar-alt'),':before').getPropertyValue('content')"
print(driver.execute_script(script).strip())
Reference
You can find a detailed discussion in:
How locate the pseudo-element ::before using Selenium Python
There are two things here. Your code is font-awesome library. And the calendar icon is suppose to use the class fa-calendar-alt.
Now if you expect your icon to be a calendar, just checking if the class fa-calendar-alt exists on your element should be good enough to check if the calendar icon will appear.
Once this class is there you can assume that the calendar icon will be displayed. Now the other assumption that we make is that the font-awesome library was actually included by the html, because if for some reason the library is not included then even though class of calendar is correct, the icon will still not load. So for that you can check if a given class actually exists in CSS or not. Below thread can help you for the same
How can you determine if a css class exists with Javascript?
I would never worry about checking this, because chances of such occurrences will always be very low.
I would discourage checking content values of the class itself, as you are then making it implementation dependent, which you shouldn't. Like in Font Awesome 5.0, it use SVG to do all this instead of a font

using Selenium in Python to click on the right checkbox

I am very new to using selenium but I cannot find way around a very simple task.
I need to be able to click on the element that specifies bedrooms: 2.
I have used I don't know how many references by xpath, by id, by name, by class but selenium just won't find the element. I also have tried to browse the internet but could not find solutions that help me.
Here's the sanpshot
For instance: using
driver.find_element_by_id('agatha_bedrooms1588844814480_advancedSearch1').click()
This won't work. Selenium cannot find the element. It seems that this element is within another element but I don't understand how to access it.
Could you help me please?
Thanks a lot to you.
G
The ids seem to be dynamically generated, in which case you cannot rely on them. Try with this xpath:
driver.find_element_by_xpath("//*[#name='bedrooms' and #value='2']/following::label").click()
Although it is generally good practice to work with waits. So something like:
WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//*[#name='bedrooms' and #value='2']/following::label"))).click()
Ensure to have these imports for the wait to work
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
Thanks a lot, after multiple and multiple trials I could get around that way:
elemt = driver.find_element_by_xpath("//*#name='bedrooms']").find_element_by_xpath("//[#value='2']")
idvar = elemt.get_attribute("id")
elemt2 = driver.find_element_by_xpath("//label[#for='" + idvar + "']")
elemt2.click()
It seems that the checkbox was hidden under the label (?!) so that Selenium did not want to click on it.
If the checkbox is inside of an iframe, do this:
# basically just select the iframe any way you want
frame = driver.find_element_by_css_selector("iframe")
driver.switch_to.frame(frame)
driver.find_element_by_id('agatha_bedrooms1588844814480_advancedSearch1').click()
edit:
I've found solution. Kinda ugly but works lol
element = driver.find_elements_by_css_selector("input[name=bedrooms][value='2']")[0]
element.find_element_by_xpath("..").click()
You can try this xpath. hope its helps:
//*[#name='bedrooms']/following::*/*[text()='2']

How to handle dynamic changing ID's In XPath?

I'm trying to automate login into a website.
The id of email box changes each time I search for the page
For example:
//*[#id="undefined-undefined-E-mail-53172"]
When I refresh the page:
//*[#id="undefined-undefined-E-mail-33458"]
So I have to change the code each time like:
driver.find_element_by_id("undefined-undefined-E-mail-53172").send_keys("X#gmail.com")
driver.find_element_by_id("undefined-undefined-E-mail-33458").send_keys("X#gmail.com")
And when I refresh again, it doesn't work.
To handle dynamic element induce WebDriverWait and element_to_be_clickable()
since ID is dynamic use starts-with() xpath expression.
WebDriverWait(driver,20).until(EC.element_to_be_clickable((By.XPATH,"//input[starts-with(#id,'undefined-undefined-E-mail-')]"))).send_keys("X#gmail.com")
Or
WebDriverWait(driver,20).until(EC.element_to_be_clickable((By.XPATH,"//*[starts-with(#id,'undefined-undefined-E-mail-')]"))).send_keys("X#gmail.com")
Note: You need following imports to execute above code.
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
Can you try this it will work
driver.findElement(By.Xpath("//*[contains(#id,'undefined-undefined-E-mail')]").sendKeys("aa#yyy.com");
or
driver.findElement(By.Xpath("//*[starts-with(#id,'undefined-undefined-E-mail')]").sendKeys("aa#yyy.com");
You can do it 2 ways:
1/ check if somekinda element contains something ( like part of a string, which someone has already answered in a different answer ... )
2/ The one that I use -> using a full Xpath. Here is how you do that:
Download this ( or something like ) this extension https://chrome.google.com/webstore/detail/xpath-helper/hgimnogjllphhhkhlmebbmlgjoejdpjl
Go to the website and copy xpath ... when you get xpath that contains a specific ID ( example: //*[#id="5633040"]/div/input , you will just go above that ID and you will try to create a static XPath ( thats what is this extension good for ) .. //*[#id="app_content"]/div/div/form/div[1]/div/div <- lets say this is above that //*[#id="5633040"]
so if you put something like this in your xpath search, you should get the same result as with dynamic one //*[#id="app_content"]/div/div/form/div[1]/div/div/div/div/input or this one if there is more divs //*[#id="app_content"]/div/div/form/div[1]/div/div/div[number]/div/input
P.S -> if you cant install extensions, you will just have to try to see if you typed it right and it works
~ Hope it helps :)

Clicking on a button with dynamic selector in python selenium webdriver

I am working on this automation thing and I am trying to click on this button with selector
<button id="ember2570" class="ember-view btn btn-default btn btn-default" type="button"> <i class="fa fa-upload"></i>
PDMLink<!----></button>
I have tried find_element_by_id but the ID changes with every reload and the class names are also not unique to the button.
if I try including wildcard like "ember*" then it clicks somewhere else. Almost all the elements of the web page has id="embersomeRandomNumber"
I cannot share the url as it is an intranet site.
Using CSS Selector:
The Operator ^ - Match element that starts with the given value.
In your case:
driver.find_element_by_css_selector('button[id^="ember"] i.fa-upload')
Using XPath:
The keyword contains Match element that contains the given value.
driver.find_element_by_xpath("//button[contains(#id,'ember')/i[contains(#class,'fa-upload')]")
Edit:
If you are looking for the button with the text of "PDMLink":
You can use text in the XPath:
driver.find_element_by_xpath("//button[text()='PDMLink']")
So here is how i would approach this simple "problem":
driver.find_element_by_css_selector("button[id^="ember"] i.fa-upload").click()
If click doesnt work, then you can also import keys and do .send_keys(Keys.RETURN)
I havent worked with selenium in quite a bit so give that a try and let me know.
I usually work with my own API i built last year which has functions like type(), find(), find_path(), find_id(), click() and all of them are built to avoid capcha cashe from building and avoiding most of those "im not a robot" things which crash bots. I just use time delays at random intervals. The type() actually recieves the string and types char by char on a small time delay which is always random and that is the main thing
As you have mentioned ...the ID changes with every reload and the class names are also not unique to the button... additionally the desired element is a Ember.js enabled element so to click() on the element you have to induce WebDriverWait for the element_to_be_clickable() and you can use the following Locator Strategy:
Using XPATH:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//button[#class='ember-view btn btn-default btn btn-default' and contains(., 'PDMLink')][.//i[#class='fa fa-upload']]"))).click()
Note : You have to add the following imports :
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

Python Selenium how to find input button

On the website, the input tag is this:
<input value="Submit" type="submit">
How can I make selenium select this input button?
I tried this:
browser.find_element_by_css_selector("input[value='submit']")
But it wasn't able to find the input button.
I would use this xpath to find the above input:
browser.find_element_by_xpath(".//input[#value='Submit' and #type='submit']")
If there are multiple inputs with the same attributes, you may need to find by index as well.
UPDATE:
Since you are having trouble finding the element, and there are no iframes on the page, I would suggest using WebDriverWait in case there are any AJAX/JavaScript/Dynamic load events that are creating the input after the pageload is complete.
Import these into your script:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
Then try this:
wait = WebDriverWait(browser, 10)
wait.until(EC.element_to_be_clickable((By.XPATH, ".//input[#value='Submit' and #type='submit']"))).click()
As mentioned in your comment, you had to switch to a new tab. The way to do this in Selenium is:
#this will switch to the newest tab
browser.switch_to_window(browser.window_handles[-1])
Please make sure that input tag is in the an iframe?
if it in an iframe you should use
browser.switch_to.frame('iframe's id') to enter the iframe, if you enter this frame, you can use xpath or css selector to find the tag you want.
There's a clear mispelling, the 'Submit' value has a capitalised S in your sourcecode, but you have provided lowercase in your selector. Try this instead!
browser.find_element_by_css_selector("input[value='Submit']")

Categories