Como encontro um elemento que contém texto específico no Selenium Webdriver (Python)?

259

Estou tentando testar uma interface javascript complicada com o Selenium (usando a interface Python e em vários navegadores). Eu tenho vários botões do formulário:

<div>My Button</div>

Gostaria de poder procurar botões com base em "Meu botão" (ou correspondências parciais que não diferenciam maiúsculas de minúsculas, como "meu botão" ou "botão")

Estou achando isso incrivelmente difícil, na medida em que sinto que estou perdendo algo óbvio. A melhor coisa que tenho até agora é:

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

Isso faz distinção entre maiúsculas e minúsculas, no entanto. A outra coisa que tentei é percorrer todas as divs da página e verificar a propriedade element.text. No entanto, toda vez que você obtém uma situação do formulário:

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

div.outer também tem "Meu botão" como o texto. Para corrigir isso, tentei verificar se div.outer é o pai do div.inner, mas não consegui descobrir como fazer isso (element.get_element_by_xpath ('..') retorna o pai de um elemento, mas ele testes diferentes de div.outer). Além disso, a iteração de todos os elementos da página parece muito lenta, pelo menos usando o driver da web do Chrome.

Ideias?

Edit: Esta questão saiu um pouco vaga. Pediu (e respondeu) uma versão mais específica aqui: Como obter o texto de um elemento no Selenium WebDriver (via API do Python) sem incluir o texto do elemento filho?

Josh
fonte
As respostas atuais não funcionaram para mim. Este foi o seguinte: sqa.stackexchange.com/a/2486
alejandro

Respostas:

328

Tente o seguinte:

driver.find_elements_by_xpath("//*[contains(text(), 'My Button')]")
Ricky
fonte
3
Obrigado pela resposta, foram 50% do que eu precisava (comecei). O formulário que cheguei é este "(// * [contém (texto (), '" + texto + "')) | // * [@ value = '" + texto + "'])" que ele procurará dado texto não apenas dentro dos nós do elemento, mas também dentro dos elementos de entrada cujo texto foi definido através do atributo 'value', isto é, <button value = "My Button" />. Embora observe, o valor deve ser uma correspondência estrita, não apenas conter o texto.
Ivan Koshelev 11/11
9
Também vale a pena mencionar para outros visitantes do mecanismo de pesquisa: se você está procurando um link, existem find_element(s)_by_link_texte find_element(s)_by_partial_link_textmétodos #
Dan Passaro 17/14 /
3
E se o texto for dinâmico? Ou seja, pode conter aspas. Isso não quebraria essa solução?
IcedDante
3
A procura de certos nomes parece quebrar isso. Veja o exemplo a seguir: "// * [contains (text (), '" + nome de usuário + "'))" se nome de usuário = "O'Reilly"; o xpath se tornaria inválido. Existe uma maneira de contornar isso?
Sakamoto Kazuma
Parece não funcionar quando o texto de destino possui várias linhas.
Shawn
29

você pode tentar um xpath como:

'//div[contains(text(), "{0}") and @class="inner"]'.format(text)
andrean
fonte
Obrigado ... para ajudar a distinguir interno do externo, mas que realmente funciona bem com o xpath, eu só estava tendo esse problema repetindo todas as divs. Meu problema com o xpath é que não consigo descobrir como fazer distinção entre maiúsculas e minúsculas?
josh
2
O xpath 2.0 possui uma função em minúscula, portanto, isso deve funcionar: '// div [contém (minúscula (texto ()), "{0}")]'. format (text)
andrean
obrigado! embora, o meu entendimento é que XPath 2.0 não é suportado através dos principais navegadores ...
josh
O selenium avalia expressões xpath diretamente com os métodos do navegador, portanto depende de qual navegador você está usando com selenium. geralmente apenas 6,7 e 8 não devem suportar o xpath 2.0.
andrean
.formatnão é reconhecido no meu eclipse. dá e erro. alguma ideia por que?
anujin
16

Você também pode usá-lo com o Padrão de objeto de página, por exemplo:

Tente este código:

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

// * procurará por qualquer tag HTML. Onde, se algum texto for comum para as tags Button e div, e se // * for categorias, não funcionará conforme o esperado. Se você precisar selecionar algum específico, poderá obtê-lo declarando a tag Elemento HTML. Gostar:

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

Curiosamente, virtualmente todas as respostas giram em torno da função do xpath contains(), negligenciando o fato de ser sensível a maiúsculas e minúsculas - ao contrário da pergunta do OP.
Se você precisar de distinção entre maiúsculas e minúsculas, isso é possível no xpath 1.0 (a versão que os navegadores contemporâneos suportam) , embora não seja bonito - usando a translate()função Ele substitui um caractere de origem para a forma desejada, usando uma tabela de conversão.

A construção de uma tabela com todos os caracteres maiúsculos transformará efetivamente o texto do nó em sua forma lower () - permitindo a correspondência sem distinção entre maiúsculas e minúsculas (eis apenas a prerrogativa) :

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

A chamada completa do python:

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

Naturalmente, essa abordagem tem suas desvantagens - como dado, funcionará apenas para texto em latim; se você quiser cobrir caracteres unicode - precisará adicioná-los à tabela de tradução. Eu fiz isso na amostra acima - o último caractere é o símbolo cirílico "Й".


E se vivêssemos em um mundo em que os navegadores suportassem o xpath 2.0 e posterior (🤞, mas não acontecerá tão cedo ☹️) , poderíamos ter usado as funções lower-case()(ainda que não totalmente cientes do código de idioma) e matches(para pesquisas de expressões regulares, com maiúsculas e minúsculas) -insensitive ( 'i') flag).

Todor Minakov
fonte
3

No HTML que você forneceu:

<div>My Button</div>

O texto My Buttoné innerHTMLe não possui espaços em branco, portanto você pode usar facilmente da text()seguinte maneira:

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

Nota : text()seleciona todos os filhos do nó de texto do nó de contexto


Texto com espaços iniciais / finais

Encaixe o texto relevante que contém espaços em branco no início:

<div>   My Button</div>

ou no final:

<div>My Button   </div>

ou nas duas extremidades:

<div> My Button </div>  

Nestes casos, você tem 2 opções:

  • Você pode usar a contains()função que determina se a primeira cadeia de argumentos contém a segunda cadeia de argumentos e retorna booleano true ou false da seguinte maneira:

    my_element = driver.find_element_by_xpath("//div[contains(., 'My Button')]")
  • Você pode usar a normalize-space()função que retira o espaço em branco inicial e final de uma sequência, substitui sequências de caracteres de espaço em branco por um único espaço e retorna a sequência resultante da seguinte maneira:

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

xpath para a variável Text

Caso o texto seja uma variável, você pode usar:

foo= "foo_bar"
my_element = driver.find_element_by_xpath("//div[.='" + foo + "']")
DebanjanB
fonte
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"));
mike oganyan
fonte
1

Problema semelhante: Localizar <button>Advanced...</button>

Talvez isso lhe dê algumas idéias (transfira o conceito de Java para Python):

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

Use driver.find_elements_by_xpath e partidas REGEX função de correspondência para o caso de busca insensível do elemento pelo seu texto.

driver.find_elements_by_xpath("//*[matches(.,'My Button', 'i')]")
Andriy Ivaneyko
fonte
matches()é uma função xpath 2.0, e os navegadores infelizmente têm suporte apenas para 1.0.
Todor Minakov
-19

Tente isso. É muito fácil:

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

Isso realmente funcionou para mim no driver da web selênio.

Amit
fonte
9
Não funciona se o texto for gerado por JavaScript.
8284
2
Essa é uma maneira muito interessante de verificar isso, porque você está transferindo todo o conteúdo da página pela rede. Para páginas muito pequenas, isso é aceitável, mas para páginas muito grandes você transfere todo o conteúdo do arquivo e verifica no lado do servidor. Uma abordagem melhor seria fazê-lo no lado do cliente com xpath, javascript ou css.
thomas.han
Eu pensaria que toda a fonte da página já precisaria ser transferida por cabo para o navegador renderizá-la?
René
3
Josh está perguntando como encontrar o elemento pelo texto, para não testar se o texto está presente na fonte da página.
Cedric
1
Nos casos em que tudo que é necessário é encontrar um texto estático em uma página, essa solução é boa o suficiente. (Isso ajudou no meu caso).
Karlth