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

13

389

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 can I get text of an element in Selenium WebDriver, without including child element text?

Normally answered 7/9, 2012 at 18:20 Comment(1)
The current answers did not work for me. This one did: sqa.stackexchange.com/a/2486Vulcan
H
490

Try the following:

driver.find_elements_by_xpath("//*[contains(text(), 'My Button')]")
Harmony answered 9/9, 2013 at 14:54 Comment(7)
Thank you for the reply, it was 50% of what I needed (got me started). The form I arrived to is this "(//*[contains(text(), '" + text + "')] | //*[@value='" + text + "'])" it will search for given text not only inside element nodes, but also inside input elements whose text was set via 'value' attribute i.e. <button value="My Button" /> . Though do note, the value must be strict match, not just contain the text.Streusel
Also worth mentioning for other search engine visitors: if you're looking for a link, there are find_element(s)_by_link_text and find_element(s)_by_partial_link_text methodsEridanus
What if the text is dynamic? That is, might contain quotes. Wouldn't that break this solution?Autograft
Searching for certain names seems to break this. Take the following for an example: "//*[contains(text(), '"+username+"')]" if username = "O'Reilly"; then the xpath would become invalid. Is there a way around this?Leandra
It doesn't seem to work when the target text has multiple lines.Bioluminescence
does this work even when the element is not visible on the page? When I try to search like this in Chrome inspect element it doesn't find the element(it does support xpaths). Once i scroll the webpage it is however detected by inspect element search function.Bruxelles
Note that find_elements_by_xpath is now deprecated. Use something like find_element("xpath", "<insert xpath value here>"). SourceFray
M
56

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 + "']")
Manns answered 11/1, 2020 at 20:54 Comment(2)
you can also use * as a wildcard to select any type of element, e.g if you were using find_elements_by_xpathNekton
@9Guy yes, that's true.Manns
R
42

You could try an XPath expression like:

'//div[contains(text(), "{0}") and @class="inner"]'.format(text)
Rectum answered 7/9, 2012 at 18:44 Comment(6)
Thanks... so that helps distinguish inner from outer, but that actually works fine with xpath, I was only having that problem iterating through all the divs. My problem with xpath is I can't figure out how to make it case-insensitive?Normally
xpath 2.0 has a lower-case function, so this should work: '//div[contains(lower-case(text()), "{0}")]'.format(text)Rectum
thanks! although, my understanding is that xpath 2.0 isn't supported across the major browsers...Normally
selenium evaluates xpath expressions directly with the browser's own methods, so it depends which browser are you using with selenium. generally only ie 6,7 and 8 should not support xpath 2.0.Rectum
.format isn't recognized in my eclipse. it gives and error. any idea, why?Oconnell
format is a standard python string function, if I'm not mistaken it's available from python 2.6, basically a modern replacement for %s style formattingRectum
C
33

//* 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 HTML Element tag. Like:

driver.find_element_by_xpath("//div[contains(text(),'Add User')]")
driver.find_element_by_xpath("//button[contains(text(),'Add User')]")
Cosecant answered 17/12, 2018 at 10:18 Comment(1)
Thanks! This version worked for me but the latest version of Selenium uses: driver: driver.FindElement(By.XPath...Caralie
G
17

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

Try this code:

@FindBy(xpath = "//*[contains(text(), 'Best Choice')]")
WebElement buttonBestChoice;
Githens answered 17/1, 2017 at 9:44 Comment(0)
M
9

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

Monochasium answered 29/1, 2019 at 8:53 Comment(0)
A
6

Simply use This:

driver.find_elements_by_xpath('//*[text() = "My Button"]')
Aigneis answered 19/1, 2021 at 6:50 Comment(0)
G
3

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();
Guacin answered 13/5, 2019 at 22:16 Comment(0)
J
3

In Selenium 4 find_element_by_xpath is deprecated. See the docs, https://selenium-python.readthedocs.io/locating-elements.html

Here are two options:

from selenium import webdriver
from selenium.webdriver.firefox.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
service = Service('D:\\Path\\to\\geckodriver.exe')
driver = webdriver.Firefox(service=service)

element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//*[contains(text(), 'My Button')]")))

OR

element = driver.find_element(By.XPATH, "//*[contains(text(), 'My Button')]")
Jackass answered 8/4, 2023 at 21:16 Comment(0)
I
2

Text Containing

To find an element with the text (displayed in the HTML page) containing a specific text, you can use the following XPath expression:

driver.find_element(By.XPATH, "//*[contains(text(), 'text_to_be_contained')]")
  • '*', selects all elements in the document, regardless of the tag name. You can replace the asterisk (*) with a specific tag name if you want to search within a particular tag type.
  • [contains(text(), 'text_to_be_contained')] is a condition that checks if the text contains the specified text ('text_to_be_contained'). This will match elements whose text attribute contains the specified text, even if it is part of a larger text value.
  • 'text_to_be_contained', should be the text you want to find

Attribute Containing

To find an element with the attribute containing a specific text, you can use the following XPath expression:

//*[contains(@attribute_name, 'text_to_be_contained')]
  • Replace 'attribute_name' with the actual name of the attribute you want to search within, such as: class, name, href, value, or any other

Text/Attribute Is Equal

To find an element with the text or attribute a matching exactly a specific text, you can use the following XPath expressions: For the text

For the text:

//*[text()='exact_text']

For the attribute:

//*[@attribute_name='exact_text']
  • 'exact_text', should be the text you want to find

Note: All methods are case sensitive

Irrational answered 6/6, 2023 at 18:32 Comment(0)
I
1
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"));
Inequitable answered 11/8, 2017 at 16:27 Comment(0)
C
0

Use driver.find_elements_by_xpath and matches regex matching function for the case insensitive search of the element by its text.

driver.find_elements_by_xpath("//*[matches(.,'My Button', 'i')]")
Complexioned answered 30/8, 2018 at 12:47 Comment(1)
matches() is an xpath 2.0 function, and the browsers regretfully have support only for 1.0.Monochasium
M
-20

Try this. It's very easy:

driver.getPageSource().contains("text to search");

This really worked for me in Selenium WebDriver.

Mortician answered 7/2, 2014 at 14:54 Comment(6)
It does not work if the text is generated by JavaScript.Misapprehend
This is a very way of checking it, because you are transferring the whole contents of the page over the wire. For very small pages this is acceptablem but for very large pages you are transfer all the contents of the file and checking on the server side. A better approach would be to do it on the client side with xpath, javascript or css.Microgroove
I would think that the whole page source would already need to be transferred over the wire for the browser to render it?Guff
Josh is asking how to find the element by text, not to test if the text is present in the source of the page.Electrotechnics
For instances where all is needed is to find a static text on a page this solution is good enough. (It helped in my case).Sematic
Bad way to do in entire page source!Cassowary

© 2022 - 2024 — McMap. All rights reserved.