如何在Selenium Webdriver(Python)中找到包含特定文本的元素?

问题:如何在Selenium Webdriver(Python)中找到包含特定文本的元素?

我正在尝试使用Selenium(使用Python接口并在多个浏览器上)测试复杂的javascript接口。我有许多形式的按钮:

<div>My Button</div>

我希望能够基于“我的按钮”(或不区分大小写的部分匹配项,例如“我的按钮”或“按钮”)搜索按钮

我发现这非常困难,在某种程度上我感觉自己缺少明显的东西。到目前为止,我最好的是:

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

但是,这是区分大小写的。我尝试过的另一件事是遍历页面上的所有div,并检查element.text属性。但是,每次您得到以下形式的情况:

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

div.outer还使用“我的按钮”作为文本。为了解决这个问题,我尝试查看div.outer是否是div.inner的父级,但无法弄清楚该怎么做(element.get_element_by_xpath(’..’)返回元素的父级,但是测试不等于div.outer)。此外,至少使用Chrome网络驱动程序,迭代页面上的所有元素似乎真的很慢。

有想法吗?

编辑:这个问题有点模糊。在此处询问(并回答)一个更具体的版本:如何在Selenium WebDriver中(通过Python api)获取元素的文本而不包含子元素文本?

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?


回答 0

尝试以下方法:

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

Try the following:

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

回答 1

您可以尝试使用xpath:

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

You could try an XPath expression like:

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

回答 2

您还可以将其与“页面对象模式”一起使用,例如:

试试这个代码:

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

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

Try this code:

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

回答 3

// *将寻找任何HTML标记。如果某些文本对于Button和div标签是公用的,并且// *是类别,则将无法按预期工作。如果需要选择任何特定内容,则可以通过声明HTML Element标签来获取。喜欢:

driver.find_element_by_xpath("//div[contains(text(),'Add User')]")
driver.find_element_by_xpath("//button[contains(text(),'Add User')]")

//* 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')]")

回答 4

有趣的是,几乎所有答案都围绕着xpath的功能contains(),而忽略了它区分大小写的事实-与OP的要求相反。
如果您需要不区分大小写,则可以在xpath 1.0 (现代浏览器支持的版本)中实现,尽管效果不佳-通过使用该translate()函数。通过使用转换表,它将源字符替换为其所需的形式。

构造一个由所有大写字母组成的表格,可以将节点的文本有效地转换为lower()形式-允许不区分大小写的匹配(这里只是特权)

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

完整的python调用:

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

自然,这种方法有其缺点-如所给出的,它仅适用于拉丁文字;如果要覆盖Unicode字符-您必须将它们添加到翻译表中。我已经在上面的示例中做到了-最后符是西里尔字母符号"Й"


如果我们生活在其中承载的XPath 2.0及以上的浏览器世界(🤞,但不会很快☹️发生的任何时间),我们可以有使用的功能lower-case()(但不完全区域识别),以及matches(对于正则表达式搜索,以案例-insensitive('i')标志)。

Interestingly virtually all answers revolve around xpath’s function contains(), neglecting the fact it is case sensitive – contrary to 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 case-insensitive ('i') flag).


回答 5

在您提供的HTML中:

<div>My Button</div>

文本My Button为,innerHTML周围没有空格,因此您可以轻松地text()按以下方式使用:

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

注意text()选择上下文节点的所有文本节点子级


带有前导/后缀空格的文本

如果开头的相关文本包含空格

<div>   My Button</div>

或最后:

<div>My Button   </div>

或两端:

<div> My Button </div>  

在这些情况下,您有2个选择:

  • 您可以使用contains()确定第一个参数字符串是否包含第二个参数字符串并返回boolean true或false的函数,如下所示:

    my_element = driver.find_element_by_xpath("//div[contains(., 'My Button')]")
  • 您可以使用以下normalize-space()功能:从字符串中去除开头和结尾的空格,将空格字符序列替换为一个空格,然后返回结果字符串,如下所示:

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

变量文本的xpath

如果文本是变量,则可以使用:

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

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

Incase 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 2 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 for variable Text

Incase the text is a variable you can use:

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

回答 6

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"));
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"));

回答 7

类似的问题:查找 <button>Advanced...</button>

也许这会给您一些想法(请将概念从Java转移到Python):

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

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();

回答 8

使用driver.find_elements_by_xpath匹配正则表达式匹配函数,以按元素的文本区分大小写

driver.find_elements_by_xpath("//*[matches(.,'My Button', 'i')]")

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')]")

回答 9

试试这个。非常简单:

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

这对于硒网络驱动程序确实很有效。

Try this. It’s very easy:

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

This really worked for me in Selenium WebDriver.