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

Posted on

Question :

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

I’m trying to test a complicated JavaScript interface with Selenium (using the Python interface, and across multiple browsers). I have a number of buttons of the form:

<div>My Button</div>

I’d like to be able to search for buttons based on “My Button” (or non-case-sensitive, partial matches such as “my button” or “button”).

I’m finding this amazingly difficult, to the extent to which I feel like I’m missing something obvious. The best thing I have so far is:

driver.find_elements_by_xpath('//div[contains(text(), "' + text + '")]')

This is case-sensitive, however. The other thing I’ve tried is iterating through all the divs on the page, and checking the element.text property. However, every time you get a situation of the form:

<div class="outer"><div class="inner">My Button</div></div>

div.outer also has “My Button” as the text. To fix that, I’ve tried looking to see if div.outer is the parent of div.inner, but I couldn’t figure out how to do that (element.get_element_by_xpath(‘..’) returns an element’s parent, but it tests not equal to div.outer).

Also, iterating through all the elements on the page seems to be really slow, at least using the Chrome webdriver.

Ideas?


I asked (and answered) a more specific version here: How to get text of an element in Selenium WebDriver, without including child element text?

Asked By: josh

||

Answer #1:

Try the following:

driver.find_elements_by_xpath("//*[contains(text(), 'My Button')]")
Answered By: Ricky

Answer #2:

You could try an XPath expression like:

'//div[contains(text(), "{0}") and @class="inner"]'.format(text)
Answered By: andrean

Answer #3:

In the HTML which you have provided:

<div>My Button</div>

The text My Button is the innerHTML and have no whitespaces around it so you can easily use text() as follows:

my_element = driver.find_element_by_xpath("//div[text()='My Button']")

Note: text() selects all text node children of the context node


Text with leading/trailing spaces

In case the relevant text containing whitespaces either in the beginning:

<div>   My Button</div>

or at the end:

<div>My Button   </div>

or at both the ends:

<div> My Button </div>

In these cases you have two options:

  • You can use contains() function which determines whether the first argument string contains the second argument string and returns boolean true or false as follows:

      my_element = driver.find_element_by_xpath("//div[contains(., 'My Button')]")
    
  • You can use normalize-space() function which strips leading and trailing white-space from a string, replaces sequences of whitespace characters by a single space, and returns the resulting string as follows:

      driver.find_element_by_xpath("//div[normalize-space()='My Button']]")
    

XPath expression for variable text

In case the text is a variable, you can use:

foo= "foo_bar"
my_element = driver.find_element_by_xpath("//div[.='" + foo + "']")
Answered By: DebanjanB

Answer #4:

You can also use it with Page Object Pattern, e.g:

Try this code:

@FindBy(xpath = "//*[contains(text(), 'Best Choice')]")
WebElement buttonBestChoice;

Answer #5:

//* This will be looking for any HTML tag. Where if some text is common for Button and div tag and if //* is categories, it will not work as expected. If you need to select any specific then you can get it by declaring an HTML element tag. Like:

driver.find_element_by_xpath("//div[contains(text(),'Add User')]")
driver.find_element_by_xpath("//button[contains(text(),'Add User')]")
Answered By: Ishita Shah

Answer #6:

Interestingly virtually all answers revolve around XPath’s function contains(), neglecting the fact it is case sensitive – contrary to the OP’s ask.

If you need case insensitivity, that is achievable in XPath 1.0 (the version contemporary browsers support), though it’s not pretty – by using the translate() function. It substitutes a source character to its desired form, by using a translation table.

Constructing a table of all upper case characters will effectively transform the node’s text to its lower() form – allowing case-insensitive matching (here’s just the prerogative):

[
  contains(
    translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'),
    'my button'
  )
]
# will match a source text like "mY bUTTon"

The full Python call:

driver.find_elements_by_xpath("//*[contains(translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ?', 'abcdefghijklmnopqrstuvwxyz?'), 'my button')]")

Naturally this approach has its drawbacks – as given, it’ll work only for Latin text; if you want to cover Unicode characters – you’ll have to add them to the translation table. I’ve done that in the sample above – the last character is the Cyrillic symbol "?".


And if we lived in a world where browsers supported XPath 2.0 and up (?, but not happening any time soon ??), we could having used the functions lower-case() (yet, not fully locale-aware), and matches (for regex searches, with the case-insensitive ('i') flag).

Answered By: Todor Minakov

Answer #7:

Similar problem: Find <button>Advanced...</button>

Maybe this will give you some ideas (please transfer the concept from Java to Python):

wait.until(ExpectedConditions.elementToBeClickable(//
    driver.findElements(By.tagName("button")).stream().filter(i -> i.getText().equals("Advanced...")).findFirst().get())).click();
Answered By: Reto Höhener

Answer #8:

wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[contains(text(), 'YourTextHere')]")));
assertNotNull(driver.findElement(By.xpath("//*[contains(text(), 'YourTextHere')]")));
String yourButtonName = driver.findElement(By.xpath("//*[contains(text(), 'YourTextHere')]")).getAttribute("innerText");
assertTrue(yourButtonName.equalsIgnoreCase("YourTextHere"));
Answered By: mike oganyan

Leave a Reply

Your email address will not be published. Required fields are marked *