How to handle dynamic changing ID's In XPath? - python

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 :)

Related

Xpath locator unable to detect the element

I am trying to select a button using selenium however i believe i am doing something wrong while writing my xpath can anyone please help me on this i need to select the currency Euro.
link :- https://www.booking.com/
Locator which i want to select
Locator which i have written
USD = self.find_element_by_xpath(f"//a[contains(text(),'selected_currency='Euro']")
USD.click()
The below xpath
//a[contains(#href,'EUR') and starts-with(#class,'bui-list')]
is present two times in HTML DOM.
Steps to check:
Press F12 in Chrome -> go to element section -> do a CTRL + F -> then paste the xpath and see, if your desired element is getting highlighted with 1/1 matching node.
In case you would like to select the first one which is in Suggest for you, there is no need to do anything with respect to XPath.
#pmadhu answer is misleading, since why would anyone look for first index in XPath when using with Selenium ? If there are multiple matching node, Selenium is always going to pick up the first element, So it really does not make any sense to me that why would someone use [1]
Nevertheless,
to click on Suggest for you EURO :
try:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//a[contains(#href,'EUR') and starts-with(#class,'bui-list')]"))).click()
except:
pass
Imports :
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
In case you are looking to click on second matching node for EUR,
in that case it make sense to use index 2.
XPath would look something like this :
(//a[contains(#href,'EUR') and starts-with(#class,'bui-list')])[2]
The text you are looking for: selected_currency='EUR' is in the data-modal-header-async-url-param parameter of the a tag. You should run the contains on that.
Edit:
Locator: //a[contains(#data-modal-header-async-url-param, 'selected_currency=EUR')]
As already explained, selected_currency=EUR is in the attribute - data-modal-header-async-url-param.
However you can select the required option, with below code.
The xpath for the EUR option can be - //div[contains(text(),'EUR')]. Since it highlights 2 elements in the DOM using this xpath- (//div[contains(text(),'EUR')])[1]. Its important to find unique locators. Link to refer
# Imports required for Explicit wait
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
driver.get("https://www.booking.com/")
wait = WebDriverWait(driver,30)
# Click on Choose currency option
wait.until(EC.element_to_be_clickable((By.XPATH,"//span[#class='bui-button__text']/span[contains(text(),'INR')]"))).click()
# Click on the EUR option.
euro_option = wait.until(EC.element_to_be_clickable((By.XPATH,"(//div[contains(text(),'EUR')])[1]")))
euro_option.click()

Selenium Python Class Names are the same

I am trying to code a program that displays text from a website. the classes that the text is in have the same name. i have tried to do xpath, but I cant get that to work. i don't really know how to explain my question, sorry about that.
from selenium.webdriver.common.keys import Keys
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
(deleted code that doesn't affect this)
(I have a problem with the code below)
print('Assignment 1')
time.sleep(6)
assignment1 = driver.find_element_by_class_name("calendar-title-text")
print(assignment1.text)
assignment2 = driver.find_element_by_class_name("calendar-title-text"[1])
print(assignment2.text)
time.sleep(10)
So, I think what you are asking is: "How can I select 1 element using xpath if I have multiple elements?"
And, to answer, I provided a photo to you for demonstration purposes.
Say, for example, we go to Google.com and we search "Hello World Python"
Link: Hello World Python in Google.com
In this search, we are given a lot of results
When you open your Google Chrome Developer Tools ( F12 ) and you navigate to the Elements tab and press CTRL + F, a search bar will display at the bottom of the page.
When we search through the HTML code, we have an xpath that gives us multiple results when we search for a class name.
//div[#id='search']//div[#id='rso']//div[#class='g']
This xpath returns us a 1 of 12 result. But, we want 1 of 1. In order to accomplish this, we need to wrap our xpath in parenthesis (). This will help us in isolating only 1 of our elements.
(//div[#id='search']//div[#id='rso']//div[#class='g'])[1]
This xpath will result in 1 of 1. This should allow you to be able to interact with the web element.
driver.find_element(By.XPATH, "(//div[#id='search']//div[#id='rso']//div[#class='g'])[1]").click()
If you wanted to interact with the text of an element, as an example, we can print out the highlighted text with the following command
xpath = "((//div[#id='search']//div[#id='rso']//div[#class='g'] )[1]//div[#data-hveid]//div)[7]//div"
element_text = driver.find_element(By.XPATH, xpath).text
print(f'My Text Is: {element_text}')

How to select a value from drop down menu in python selenium

I am writing a python script which will call a webpage and will select an option from the drop down to download that file. To do this task, I am using chropath. It is a browser extension which can give you the relative xpath or id for any button or field on the webpage and using that we can call it from python selenium script.
Above image shows the drop down menu in which I have to select 2019 as year and the download the file. In the lower part of the image, you can see that I have used chropath to get the relative xpath of the drop down menu which is //select[#id='rain']
Below is the code I am using:
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Firefox()
driver.get("<URL>")
driver.maximize_window()
grbf = driver.find_element_by_xpath("//select[#id='rain']")
grbf.send_keys('2019')
grbf_btn = (By.XPATH, "//form[1]//input[1]")
WebDriverWait(driver, 20).until(EC.element_to_be_clickable(grbf_btn)).click()
from the above code, you can see that I am using xpath to select the drop down grbf = driver.find_element_by_xpath("//select[#id='rain']") and then sending keys as 2019 i.e. grbf.send_keys('2019') and after that I am calling download button to download it. But for some reason, its always selecting year 1999 from the drop down. I am not able to understand what is wrong in this. Is this correct approach to solve this. Please help. Thanks
I had the same problem time ago. Try this:
from selenium.webdriver.support.ui import Select
grbf = Select(driver.find_element_by_xpath("//select[#id='rain']"))
grbf.select_by_value('2019')
In the select_by_value() you have to use the value of the element in the dropdown.
By the way, if an element has id, use it.
grbf = Select(driver.find_element_by_id('rain'))
Try below code:
select = Select(driver.find_element_by_xpath("//select[#id='rain']"))
select.select_by_visible_text('2019')
Another approches to deal with dropdown:
Using Index of Dropdown :
select.select_by_index(Paass index)
Using valueof Dropdown :
select.select_by_value('value of element')
Using visible text of Dropdown :
select.select_by_visible_text('element_text')
In my opinion, I don't think this is the correct approach. You try to select the option which is dropdown (not a text box like ), so send key command does not work.
What you need to do is try to inspect HTML changing when clicking the dropdown and try to XPath for an option that you want to select.
If you still stuck at this problem, I recommend using katalon recorder which is a chrome extension to allow you to record and do UI testing

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']

Categories