Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to locate an element by class name and its text in python selenium

Hi I am trying to locate an element by its class name and the text that it contains

<div class="fc-day-number">15</div>

there are a bunch of fc-day-number on the page with different values, I need the one with for example 15.

I do

driver.find_element_by_class_name("fc-day-content") 

but I also need it to be equal to 15 and I am stuck here, please help.

like image 825
Nro Avatar asked Oct 14 '14 21:10

Nro


People also ask

Can I find element by class Selenium Python?

Luckily, Selenium offers a few methods you can use to find elements. One of the options the Selenium WebDriver library provides for locating HTML elements is the use of the class property. The HTML class property is used for setting a class to an HTML element.

How do I find an element that contains specific text in Selenium WebDriver Python?

We can find an element that contains specific text with Selenium webdriver in Python using the xpath. This locator has functions that help to verify a specific text contained within an element. The function text() in xpath is used to locate a webelement depending on the text visible on the page.

Can XPath locate element using text?

text(): A built-in method in Selenium WebDriver that is used with XPath locator to locate an element based on its exact text value.

How do you getText from an element in Selenium?

We can get text from a webelement with Selenium webdriver. The getText() methods obtains the innerText of an element. It fetches the text of an element which is visible along with its sub elements. It ignores the trailing and leading spaces.


3 Answers

You can use xpath:

driver.find_element_by_xpath("//div[@class='fc-day-content' and text()='15']")
like image 130
Richard Avatar answered Oct 18 '22 22:10

Richard


 fc_day_contents = driver.find_elements_by_class_name("fc-day-content")
 the_one_you_want = [x for x in fc_day_contents if "15" == x.text][0]

first line puts all elements with class name "fc-day-content" in a list ( also notice how its elementSSSSSSSSS with an S, this returns a list of all elements by_class_name, by_name, by_id or wahtever)

second line, goes through each element and looks to see if it has the text "15" as its text, and returns it as a (probably smaller) list

the [0] at the end of it, returns the first item in the list (you can remove it, if you want a list of all the ones that are "15" )

like image 36
TehTris Avatar answered Oct 18 '22 22:10

TehTris


For things like this, prefer to use JavaScript:

els = driver.execute_script("""
return Array.prototype.slice.call(document.getElementsByClassName("fc-day-content"))
    .filter(function (x) { return x.textContent === "15"; });
""")
assert len(els) == 1
el = els[0]

What this does is get all elements that have the class fc-day-content as the class. (By the way, your question uses fc-day-content and fc-day-number. Unclear which one you're really looking for but it does not matter in the grand scheme of things.) The call to Array.prototype.slice creates an array from the return value of getElementsByClassName because this method returns an HTMLCollection and thus filter is not available on it. Once we have the array, run a filter to narrow it to the elements that have 15 for text. This array of elements is returned by the JavaScript code. The assert is to make sure we don't unwittingly get more than one element, which would be a surprise. And then the element is extracted from the list.

(If you care about IE compatibility, textContent is not available before IE 9. If you need support for IE 8 or earlier, you might be able to get by with innerText or innerHTML or you could check that the element holds a single text node with value 15.)

I prefer not to do it like TehTris does (find_elements_by_class_name plus a Python loop to find the one with the text) because that method takes 1 round-trip between Selenium client and Selenium server to get all the elements of class fc-day-content plus 1 round-trip per element that was found. So if you have 15 elements on your page with the class fc-day-content, that's 16 round-trips. If you run a test through Browser Stack or Sauce Labs, that's going to slow things down considerably.

And I prefer to avoid an XPath expression with @class='fc-day-content' because as soon as you add a new class to your element, this expression breaks. Maybe the element you care about has just one CSS class now but applications change. You could use XPath's contains() function but then you run into other complications, and once you take care of everything, it becomes a bit unwieldy. See this answer for how to use it robustly.

like image 4
Louis Avatar answered Oct 18 '22 21:10

Louis