Technically you can do the search using a CSS selector but the locator alone would not find the elements you want. You would have to loop through all the elements and look for contained text using .getText(). The below is an example
List<WebElement> labels = driver.findElements(By.cssSelector("div.entity-label.ng-binding[ng-bind-html='entity.Label']"));
int count = 0;
for (WebElement label : labels)
{
if (label.getText().equals("Name"))
{
count++;
}
}
System.out.println(count);
The more efficient way to do it (and the only way to locate an element with containing text) is to use an XPath. The XPath would contain the text you are looking for so that with only the locator you would find all the desired elements. The code below would return the count of elements that you are looking for.
driver.findElements(By.xpath("//div[.='Name'][ng-bind-html='entity.Label']")).size();
Answer from JeffC on Stack OverflowHi all. I'm trying to use Selenium to find an element by CSS selector and although I've tried using the documentation, I think I'm misunderstanding something.
This is what I'm trying:
driver.find_element(By.CSS_SELECTOR,f"img [src='images/icons/new.gif'][alt='Add new item to list']").click()
This is the element I want to find and click:
<img src="images/icons/new.gif" alt="Add new item to list">
The error I get is as follows:
Exception has occurred: NoSuchElementException
Message: no such element: Unable to locate element: {"method":"css selector","selector":"img [src='images/icons/new.gif'][alt='Add new item to list']"}I can see it's on the page, however, so I'm not sure what I'm missing. I can find other items using XPATH and ID so selenium is up and running, just to rule that out. Can anybody suggest anything?
(I'm using selenium 4.8.2 and python 3.11.1, if that helps)
Technically you can do the search using a CSS selector but the locator alone would not find the elements you want. You would have to loop through all the elements and look for contained text using .getText(). The below is an example
List<WebElement> labels = driver.findElements(By.cssSelector("div.entity-label.ng-binding[ng-bind-html='entity.Label']"));
int count = 0;
for (WebElement label : labels)
{
if (label.getText().equals("Name"))
{
count++;
}
}
System.out.println(count);
The more efficient way to do it (and the only way to locate an element with containing text) is to use an XPath. The XPath would contain the text you are looking for so that with only the locator you would find all the desired elements. The code below would return the count of elements that you are looking for.
driver.findElements(By.xpath("//div[.='Name'][ng-bind-html='entity.Label']")).size();
No, you cannot. The only way is using Xpath. Use text()='your text' or contains(text(),'your text') as a condition to find the element.
private By lblNameField = By.xpath("//*[@class='entity-label ng-binding' and text()='Name']");
Selenium CSS selector by id AND multiple classes - Stack Overflow
java - Find div element by multiple class names? - Stack Overflow
Pulling multiple elements from the same page
finding items with selenium by CSS selector using multiple attributes
Videos
Actually the two are quite different selectors.
Here is your cssSelector:
div#someId.aClass.andAnother input[type=text]
But what you really wanted to write was:
div#someId .aClass.andAnother input[type=text]
notice the space between ID and class. you need that.
findElement() finds an element in the current context, which means your first snippet of code is really finding an element that matches .aClass.andAnother input[type=text], which is contained within #someId. The element with that ID may or may not contain the two classes; WebDriver doesn't assume you're referring to the same element; it just finds the input as long as its ancestors are #someId and .aClass.andAnother.
This is completely different from div#someId.aClass.andAnother input[type=text], which finds any input[type=text] within div#someId.aClass.andAnother only (i.e. it's a div that contains both the ID and the classes).
I don't think barak manos's answer has fully explained it.
Imagine we have few elements as the followings:
<div class="value test"></div><div class="value test "></div><div class="first value test last"></div><div class="test value"></div>
How XPath matches
Match only 1 (exact match), barak's answer
driver.findElement(By.xpath("//div[@class='value test']"));Match 1, 2 and 3 (match class contains
value test, class order matters)driver.findElement(By.xpath("//div[contains(@class, 'value test')]"));Match 1, 2, 3 and 4 (as long as elements have class
valueandtest)driver.findElement(By.xpath("//div[contains(@class, 'value') and contains(@class, 'test')]"));
Also, in cases like this, Css Selector is always in favor of XPath (fast, concise, native).
Match 1
driver.findElement(By.cssSelector("div[class='value test']"));Match 1, 2 and 3
driver.findElement(By.cssSelector("div[class*='value test']"));Match 1, 2, 3 and 4
driver.findElement(By.cssSelector("div.value.test"));
Try this:
test = driver.findElement(By.xpath("//div[@class='value test']"));
So I am making a Garmin crawling script and I want it to pull multiple elements if they are from the same day and add the time together for some activities, time, distance and heart rate for another for example.
Layout of website
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
import login as login
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import datetime
import time
x = datetime.datetime.now()
x = x.strftime("%b %d")
driver = browser = webdriver.Firefox()
driver.get("https://connect.garmin.com/modern/activities")
driver.implicitly_wait(1)
iframe = driver.find_element(By.ID, "gauth-widget-frame-gauth-widget")
driver.switch_to.frame(iframe)
driver.find_element("name", "username").send_keys(login.username)
driver.find_element("name", "password").send_keys(login.password)
driver.find_element("name", "password").send_keys(Keys.RETURN)
driver.switch_to.default_content()
time.sleep(10)
driver.find_element("name", "search").send_keys("Reading")
driver.find_element("name", "search").send_keys(Keys.RETURN)
time.sleep(2)
element = driver.find_element(By.CSS_SELECTOR, '.activity-date > span:nth-child(1)').text
time.sleep(2)
print(element)
time_read = 0
if element == x:
spent = driver.find_element(By.CSS_SELECTOR, 'li.list-item:nth-child(1) > div:nth-child(2) > div:nth-child(5) > div:nth-child(2) > span:nth-child(1) > span:nth-child(1)').text
result = time.strptime(spent, "%H:%M:%S")
time_read += result.tm_hour * 60
time_read += result.tm_min
print(time_read)
So this is my current code. It finds the date, checks if it is today and adds the minutes to the variable time_read.
Now I need some help in how I go about adding multiple elements, and if this can be done with some kind of for loop, where it loops between the dates and can then extract the time from the element?
Do I need to set them up one by one, since I need to provide which element a specific iteration needs to pull from? So maybe I should have 5 or 6 checks for example, instead of some kind of loop that goes through and does it? Then it will be a lot of manual work, which makes me question if there isn't a better way to deal with it.
I do not want to use CSV.
Some relevant HTML
<div class="pull-left activity-date date-col">
<span class="unit">Sep 14</span>
<span class="label">2022</span>
</div>
<span class="unit" title="3:32:00"><span class="" data-placement="top" title="3:32:00">3:32:00</span></span>
<span class="unit" title="1:00:00"><span class="" data-placement="top" title="1:00:00">1:00:00</span></span>
<span class="" data-placement="top" title="1:00:00">1:00:00</span>Also a bit unsure what the best way is to locate elements? Is CSS.SELECTOR good or should I use XPATH preferably?
Thanks