This question already has an answer here:
find element with xpath selenium
(1 answer)
Closed last year.
Suppose I get a list of elements on a webpage by class name, like so:
driver.find_elements_by_class_name("something")
How can I get the XPath of each element in that list?
Note: I am not trying to find elements using XPaths. I already have the elements from the code above. I just want the XPath of each of those elements I have already found.
driver.find_element_by_* and driver.find_elements_by_* are deprecated.
Best way to use it is as #undetected-selenium wrote.
First import By class
from selenium.webdriver.common.by import By
You can then use it with multiple location strategies.
XPath
driver.find_elements(By.XPATH, "//*[#attribute='something']")
CSS Selector
driver.find_elements(By.CSS_SELECTOR, "[attribute~=”value”]")
You should use this ones from now on since it's very common to get an error with the latest webdrivers saying find_element_by_* commands are deprecated
WebElement identified through classname as something can also be identified using the following Locator Strategies:
xpath:
driver.find_elements(By.XPATH, "//*[#class='something']")
css-selector:
driver.find_elements(By.CSS_SELECTOR, ".something")
Note: You have to add the following imports :
from selenium.webdriver.common.by import By
You can install XPath Finder extension for your browser and automatically generate the unique XPath for each element.
After that you can use the Function :
driver.find_element_by_xpath("XPath string") .
Related
Xpath
I'm looking for Unique Xpath for this element (Select)
in google language settings
https://myaccount.google.com/language?hl=en
already have this xpath but I need something more accurate and unique
WebDriverWait(driver, 30).until(
EC.element_to_be_clickable((By.XPATH,
"//*[#id='yDmH0d']/div[11]/div/div[2]/div[3]/div[2]/button"))).click()
If you pay attention to the HTMLDOM :
You can construct a xpath based on attribute name only. You do not need their values nor their text.
//div[#data-is-touch-wrapper]/button[#data-id and #aria-label]
represent two matching nodes.
(//div[#data-is-touch-wrapper]/button[#data-id and #aria-label])[2]
should get the job done.
I would try to copy the X-Path from the Browser as discribed here:
Mozilla Help
My result would be: //*[#id="lang-selector"]
I have a very complex website which I am trying to test with Selenium. But when I try to get the XPath, I get like this for example.
//*[#id="datatable1595356931082"]/div[1]/div[2]/table/tbody/tr[3]/td[2]/div/select/option[8]
Absolute XPath:
/html/body/div[4]/div[2]/div[2]/div/div[2]/div[2]/div/div/div[1]/div[2]/table/tbody/tr[3]/td[2]/div/select/option[8]
in Selenium I tried with absolute path like
driver.find_element_by_xpath("/html/body/div[4]/div[2]/div[2]/div/div[2]/div[2]/div/div/div[1]/div[2]/")
and it tries to error out saying it is unable to find XPath. The datatable seems to create a dynamic number during runtime.
What here I am trying to do is to select a drop down which looks like this
<div role="columnheader" class="webix_hcell webix_ss_filter">
<select>
<option value=""></option>
<option value="A">A</option>
<option value="B">B</option>
.
.
.
</select>
</div>
I have also tried
driver.find_element_by_class_name('webix_hcell webix_ss_filter')
But that too errors out
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":".webix_hcell webix_ss_filter"}
For the above one
selenium.common.exceptions.InvalidSelectorException: Message: invalid selector: Unable to locate an element with the xpath expression /html/body/div[4]/div[2]/div[2]/div/div[2]/div[2]/div/div/div[1]/div[2]/ because of the following error:
SyntaxError: Failed to execute 'evaluate' on 'Document': The string '/html/body/div[4]/div[2]/div[2]/div/div[2]/div[2]/div/div/div[1]/div[2]/' is not a valid XPath expression.
(Session info: chrome=84.0.4147.89)
This won't work, because *_by_class_name is only for single class name:
driver.find_element_by_class_name('webix_hcell webix_ss_filter')
Try using *_by_css_selector:
element = driver.find_element_by_css_selector('div.webix_hcell.webix_ss_filter select')
There are a couple of things you need to consider as follows:
From your first attempt the WebElement appears as a dynamic element due to presence of the id attribute value as datatable1595356931082, which possibly will change on every access or periodically:
//*[#id="datatable1595356931082"]/div[1]/div[2]/table/tbody/tr[3]/td[2]/div/select/option[8]
In your second attempt you have used an absolute xpath. As the website is dynamic, elements will be repositioned on every access or periodically:
/html/body/div[4]/div[2]/div[2]/div/div[2]/div[2]/div/div/div[1]/div[2]/table/tbody/tr[3]/td[2]/div/select/option[8]
In your third attempt the xpath ends with / which isn't desired. Hence you face InvalidSelectorException
In your forth attempt you have passed multiple classes through driver.find_element_by_class_name('webix_hcell webix_ss_filter') where as driver.find_element_by_class_name() accepts only one classname as an argument.
You can find a detailed discussion in Invalid selector: Compound class names not permitted error using Selenium
Finally, it's a <select> node, so you need to use the Select class.
You can find a detailed discussion in How to select a drop-down menu value with Selenium using Python?
Solution
The relevant text based HTML would have helped us to construct a canonical answer. However as per the HTML provided to click on the option with text as A you can use the following either of the following Locator Strategies:
Using XPATH and select_by_visible_text():
select = Select(WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//div[#class='webix_hcell webix_ss_filter' and #role='columnheader']//following::select[1]"))))
select.select_by_visible_text('A')
Using CSS_SELECTOR and select_by_value():
select = Select(WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "div.webix_hcell.webix_ss_filter[role='columnheader'] +select"))))
select.select_by_value('A')
Note : You have to add the following imports :
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
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
This question already has answers here:
How to retrieve the title attribute through Selenium using Python?
(3 answers)
Closed 3 years ago.
I am running a little Python Selenium script and I want to access attributes from the first element on this site: https://www.mydealz.de/gruppe/spielzeug. Every few minutes the first element is different and has therefore a different Xpath identifier.
What are the possibilites to access all the time this first element, which has different id's/Xpaths? The first result I meant.
Thanks a lot in advance!
I've keep an eye open on the website for the last 15 minutes, but for me the page has not changed.
Nevertheless, I tried to scrape the data with BS4 (which you could populate with Selenium's current browser session), where it should always return the first element first.
from bs4 import BeautifulSoup
import requests
data = requests.get('https://www.mydealz.de/gruppe/spielzeug')
soup = BeautifulSoup(data.text, "html.parser")
price_info = soup.select(".cept-tp")
for element in price_info:
for child in element:
print(child)
Of course this is just for the price, but you can apply the same logic for the other elements.
To print the first title you have to induce WebDriverWait for the desired visibility_of_element_located() and you can use either of the following Locator Strategies:
Using CSS_SELECTOR:
print(WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "div.threadGrid div.threadGrid-title.js-contextual-message-placeholder>strong.thread-title>a"))).get_attribute("title"))
Using XPATH:
print(WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.XPATH, "//div[#class='threadGrid']//div[#class='threadGrid-title js-contextual-message-placeholder']/strong[#class='thread-title']/a"))).text)
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
Console Output of two back to back execution:
[Mediamarkt #Ebay.de] diverse Gravitrax Erweiterungen günstig!
[Mediamarkt #Ebay.de] diverse Gravitrax Erweiterungen günstig!
As per the documentation:
get_attribute(name)
method Gets the given attribute or property of the element.
text
attribute returns The text of the element.
I am dealing with a situation where every time I login a report is displayed in a table whose ID is dynamically generated with random text ending with "table".
I am automating this table with selenium python web driver. It has Syntax
driver.find_element_by_xpath('//*[#id="isc_43table"]/tbody/tr[1]/td[11]').click();
help me editing this syntax to match it with table ending id with "table".
(only one table is generated).
The ends-with XPath Constraint Function is part of XPath v2.0 but as per the current implementation Selenium supports XPath v1.0.
As per the HTML you have shared to identify the element you can use either of the Locator Strategies:
XPath using contains():
driver.find_element_by_xpath("//*[contains(#id,'table')]/tbody/tr[1]/td[11]").click();
Further, as you have mentioned that table whose ID is dynamically generated so to invoke click() on the desired element you need to induce WebDriverWait for the element to be clickable and you can use the following solution:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//*[contains(#id,'table')]/tbody/tr[1]/td[11]"))).click()
Alternatively, you can also use CssSelector as:
driver.find_element_by_css_selector("[id$='table']>tbody>tr>td:nth-of-type(11)").click();
Again, you can also use CssSelector inducing WebDriverWait as:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "[id$='table']>tbody>tr>td:nth-of-type(11)"))).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
I hope, either these 2 will work for you
driver.find_element_by_xpath("//table[ends-with(#id,'table')]/tbody/tr[1]/td[11]").click();
OR
driver.find_element_by_xpath("//table[substring(#id,'table')]/tbody/tr[1]/td[11]").click();
If not getting, remove the tags from tbody.
For such situations, when you face randomly generated ids, you can use the below functions with XPATH expression
1) Contains,
2) Starts-with &
3) Ends-with
4) substring
Syntax
//table[ends-with(#id,'table')]
//h4/a[contains(text(),'SAP M')]
//div[substring(#id,'table')]
You need to identify the element which is having that id, whether its div or input or table. I think its a table.
You can try below XPath to simulate ends-with() syntax:
'//table[substring(#id, string-length(#id) - string-length("table") +1) = "table"]//tr[1]/td[11]'
You can also use CSS selector:
'table[id$="table"] tr>td:nth-of-type(11)'