添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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?

Improve this answer Follow 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. – Ivan Koshelev Oct 11 '14 at 20:37 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 methods – Dan Passaro Nov 17 '14 at 23:45 What if the text is dynamic? That is, might contain quotes. Wouldn't that break this solution? – IcedDante Jul 8 '15 at 17:59 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? – Sakamoto Kazuma Jun 7 '16 at 19:37 Improve this answer Follow 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? – josh Sep 7 '12 at 19:33 xpath 2.0 has a lower-case function, so this should work: '//div[contains(lower-case(text()), "{0}")]'.format(text) – andrean Sep 7 '12 at 20:18 thanks! although, my understanding is that xpath 2.0 isn't supported across the major browsers... – josh Sep 7 '12 at 21:05 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. – andrean Sep 7 '12 at 21:14
<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 + "']")
            
                
                        Improve this answer
                    Follow
                    you can also use * as a wildcard to select any type of element, e.g if you were using find_elements_by_xpath
    – 9 Guy
                    Dec 23 '20 at 18:50
    

    //* 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')]")
            
                
                        Improve this answer
                    Follow
            
                
                        Improve this answer
                    Follow
    

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

    Improve this answer Follow

    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();
            
                
                        Improve this answer
                    Follow
            
                
                        Improve this answer
                    Follow
    
    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"));
            
                
                        Improve this answer
                    Follow
    

    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')]")
            
                
                        Improve this answer
                    Follow
                    matches() is an xpath 2.0 function, and the browsers regretfully have support only for 1.0.
    – Todor Minakov
                    Jan 29 '19 at 8:56
    

    If using C#

    ChromeOptions options = new ChromeOptions();
    var driver = new ChromeDriver(options);
    var urlLink = "https://www.pexels.com/tr-tr/arama/do%C4%9Fa/";
    driver.Navigate().GoToUrl(urlLink);
    Thread.Sleep(10000);
    var divList = driver.FindElementsByXPath(".//div[contains(@class,'hide-featured-badge')]");
    foreach (var divItem in divList)
        var photoOwnerName = divItem.FindElement(By.XPath(".//span[@class='photo-item__name']")).GetAttribute("innerHTML");
            
                
                        Improve this answer
                    Follow
            
                
                        Improve this answer
                    Follow
                    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.
    – thomas.han
                    Aug 25 '14 at 8:22
                    I would think that the whole page source would already need to be transferred over the wire for the browser to render it?
    – René
                    Sep 5 '14 at 18:31
                    Josh is asking how to find the element by text, not to test if the text is present in the source of the page.
    – Cedric
                    May 28 '15 at 17:19
                    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).
    – Karlth
                    Sep 20 '15 at 23:00
    
  •