如何使用 Selenium xpath 查找包含“下载”一词的所有元素?

Posted

技术标签:

【中文标题】如何使用 Selenium xpath 查找包含“下载”一词的所有元素?【英文标题】:How to find all elements containing the word "download" using Selenium x-path? 【发布时间】:2016-02-23 06:20:44 【问题描述】:

我正在使用 Selenium 进行一些网页抓取,现在我想找到用户可以点击的所有元素,并且在链接文本、按钮文本、元素id、元素classhref。这可以包括链接、按钮或任何其他元素。

在this answer 中,我找到了一个 xpath 供某人寻找 xpath 以根据特定文本(或不区分大小写和部分匹配)搜索按钮:

text = 'download'
driver.find_elements_by_xpath("(//*[contains(text(), 'download')]")

但是在this page 上没有返回任何结果,即使下面的链接在那里:

<a id="downloadTop" class="navlink" href="javascript:__doPostBack('downloadTop','')">Download</a>

有人知道我如何在网站中找到所有包含“下载”一词的元素吗?

[编辑] 此问题被标记为与获得an answer 的问题重复,建议将其更改为"//*[text()[contains(.,'download')]]"。所以我尝试了以下方法:

>>> from selenium import webdriver
>>> d = webdriver.Firefox()
>>> link = 'https://www.yourticketprovider.nl/LiveContent/tickets.aspx?x=492449&y=8687&px=92AD8EAA22C9223FBCA3102EE0AE2899510C03E398A8A08A222AFDACEBFF8BA95D656F01FB04A1437669EC46E93AB5776A33951830BBA97DD94DB1729BF42D76&rand=a17cafc7-26fe-42d9-a61a-894b43a28046&utm_source=PurchaseSuccess&utm_medium=Email&utm_campaign=SystemMails'
>>> d.get(link)
>>> d.find_elements_by_xpath("//*[text()[contains(.,'download')]]")
[]  # As you can see it still doesn't get any results..
>>>

有谁知道我如何获取用户可以点击的所有元素,并且在链接文本、按钮文本、元素id、元素class 或@ 中包含“下载”一词987654333@?欢迎所有提示!

【问题讨论】:

【参考方案1】:

试试这个

//*[(@id|@class|@href|text())
       [contains(translate(.,'DOWNLOAD','download'), 'download')]]

此 Xpath 1.0 表达式选择:所有具有 idclasshref 属性或文本节点子节点的元素,其字符串值包含字符串 "download: in any大写。

这是一个运行证明。下面的 XSLT 转换用于评估 XPath 表达式并将所有选定的节点复制到输出:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

  <xsl:template match="/">
    <xsl:copy-of select=
    "//*[(@id|@class|@href|text())
       [contains(translate(.,'DOWNLOAD','download'), 'download')]]
    "/>
  </xsl:template>
</xsl:stylesheet>

当我们将转换应用于以下测试文档时

<html>
  <a id="downloadTop" class="navlink" 
    href="javascript:__doPostBack('downloadTop','')">Download</a>
  <b id="y" class="x_downLoad"/>
  <p>Nothing to do_wnLoad</p>
  <a class="m" href="www.DownLoad.com">Get it!</a>
  <b>dOwnlOad</b>
</html>

选择想要的元素,然后复制到输出

<a id="downloadTop" class="navlink" href="javascript:__doPostBack('downloadTop','')">Download</a>
<b id="y" class="x_downLoad"/>
<a class="m" href="www.DownLoad.com">Get it!</a>
<b>dOwnlOad</b>

【讨论】:

【参考方案2】:

因为您需要不区分大小写的匹配项和XPath 1.0 does not support it - you'll have to use translate() function。另外,由于您需要通配符匹配 - 您需要使用 contains()。而且,由于您还想检查idclasshref 属性以及文本:

from selenium import webdriver

driver = webdriver.Firefox()
driver.get("https://www.yourticketprovider.nl/LiveContent/tickets.aspx?x=492449&y=8687&px=92AD8EAA22C9223FBCA3102EE0AE2899510C03E398A8A08A222AFDACEBFF8BA95D656F01FB04A1437669EC46E93AB5776A33951830BBA97DD94DB1729BF42D76&rand=a17cafc7-26fe-42d9-a61a-894b43a28046&utm_source=PurchaseSuccess&utm_medium=Email&utm_campaign=SystemMails")

condition = "contains(translate(%s, 'DOWNLOAD', 'download'), 'download')"
things_to_check = ["text()", "@class", "@id", "@href"]
conditions = " or ".join(condition % thing for thing in things_to_check)

for elm in driver.find_elements_by_xpath("//*[%s]" % conditions):
    print(elm.text)

这里我们基本上是通过字符串格式化和连接来构造表达式,对text()classidhref 属性进行不区分大小写的检查,并将条件与or 连接起来。

【讨论】:

请注意,@Dimitre's answer 在这种情况下使动态 XPath 构造完全没有必要(您可能应该接受他的回答是最简单直接的)。【参考方案3】:

嗯,你找到的答案已经告诉你如何做你想做的事。我看到的问题是text = 'download' 以小写开头,而&lt;a id="downloadTop" class="navlink" href="javascript:__doPostBack('downloadTop','')"&gt;Download&lt;/a&gt; 中的文本以大写开头。

首先将您的文本更改为text = 'Download',然后看看它现在是否找到了您的元素。如果这是问题所在,那么您可以使用像

这样的小技巧
text = 'ownload'

driver.find_elements_by_xpath("(//*[contains(text(), '" + text + "')] | //*[@value='" + text + "'])")

忽略第一个字符。

编辑:是的,你可以让它不区分大小写。

driver.find_elements_by_xpath("(//*[contains(translate(text(), 'DOWNLOAD', 'download'), 'download')])")

【讨论】:

问题是我想定义它不区分大小写。包含 id="DOWNLOAD" 或 id="dOwNLoAd" 并且还包含通配符的元素也一样,例如 id="downloadthisstuff" 或 id="yourdownloadishere"。有什么想法可以做到吗?【参考方案4】:

您可以使用下面的翻译功能,它对任何单词都不区分大小写:

driver.find_elements_by_xpath("//*[translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = 'download']")

>>> driver.find_elements_by_xpath("//*[translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = 'download']")
[<selenium.webdriver.remote.webelement.WebElement (session="0b07fcba-86ee-3945-a0ae-85619e97ca31", element="4278753b-8b59-bf45-ae3b-f60f40aed071")>, <selenium.webdriver.remote.webelement.WebElement (session="0b07fcba-86ee-3945-a0ae-85619e97ca31", element="8aed425c-063e-7846-915d-d8948219cc12")>]

【讨论】:

【参考方案5】:

如果您仍然想要更多地概括 xpath 并且不想使用 translate 函数,您可以使用 itertools.product 并将字符串 download 的所有变体生成为 @ 987654325@文本属性如下。

from  itertools import  product
from selenium import webdriver

driver = webdriver.Firefox()
driver.get("https://www.yourticketprovider.nl/LiveContent/tickets.aspx?x=492449&y=8687&px=92AD8EAA22C9223FBCA3102EE0AE2899510C03E398A8A08A222AFDACEBFF8BA95D656F01FB04A1437669EC46E93AB5776A33951830BBA97DD94DB1729BF42D76&rand=a17cafc7-26fe-42d9-a61a-894b43a28046&utm_source=PurchaseSuccess&utm_medium=Email&utm_campaign=SystemMails")
txt = 'Download' # text to be searched
#Generate variants of that txt
l = [(c, c.lower()) if not c.isdigit() else (c,) for c in txt.upper()] #make tuple of upper and lower of each lettern that string (Download)
variants = ["".join(item) for item in product(*l)] # make all variant of the string Download
anchors = ["text()", "@class", "@id", "@href"] #node attribute to be searched
#Generate xpaths
xpaths_or = " or ".join(["contains(%s,'%s')"%(i,j) for i in anchors for j in variants])
xpaths = "//*[%s]" %xpaths_or
for download_tag in driver.find_elements_by_xpath(xpaths):
    print(download_tag.text)
driver.quit()

输出-

Download
Download

注意isdigit 函数可避免更改数字的大小写(如果存在)。

【讨论】:

【参考方案6】:

但是在这个没有返回结果的页面上,即使下面的链接在那里:

这是因为有不同的文字。看:

Download
download

一个字母是大写的。 因此,您需要为此使用不区分大小写的 xpath:

driver.find_elements_by_xpath("(//*[contains(lower-case(text()), 'download')]")

它必须对你足够好

【讨论】:

【参考方案7】:

好吧,我不太了解硒,但我可以提出一个可行的解决方案。您可以使用正则表达式首先解析整个页面源。例如,如果您只需要具有属性的元素,包含“下载”子字符串,请使用此正则表达式:

<\w*([a-zA-Z]+).*\w+([a-zA-Z]+)="(.*?download.*?)"?\/?>

然后使用 re.finditer 函数查找所有数学,每个匹配对象将包含标签名称(组(1)),属性名称(组(2)和属性值(组(3))

import re

# wd == webdriver

for m in re.finditer('<\w*([a-zA-Z]+).*\w+([a-zA-Z]+)="(.*?download.*?)"?\/?>', wd.page_source):
    tag, attr, val = m.group(1), m.group(2), m.group(3)

然后,您可以使用 wd.find_elements_by_css_selector (或其他)查​​找硒树结构中的所有标签:

wd.find_elements_by_css_selector('0[1=2]'.format(tag, attr, val))

【讨论】:

Aaargh ... 是的,你不能解析html并制作树形结构,因为它不是常规语言。您甚至无法使用正则表达式找到任意数量的左括号和右括号。但是这种情况下我不想找到结束标签,我只想按顺序找到所有开始标签,用正则表达式完全可行。 只要这些标签都没有出现在其他文本中是可行的,例如在 JavaScript 中。【参考方案8】:

在使用 Selenium 并查找 Web 元素时,最好始终先搜索“ID”或“类名”,因为它比使用 XPath 更可靠、更容易,通常只有在使用前 2 个找不到元素时才使用 XPath提到的方法。

在这种情况下,您在该网站的下载元素中有一个非常清晰的 ID 标签。

尝试改用这个:

downloadButton = driver.find_element_by_id('downloadTop')

然后你可以用它来点击它:

downloadButton.click()

【讨论】:

问题是这次是“downloadTop”。然而,由于我正在构建一个刮板,我希望它更通用。所以我希望所有包含“下载”一词的元素不区分大小写。包含id="DOWNLOAD"id="dOwNLoAd" 并且还包含通配符(例如id="downloadthisstuff"id="yourdownloadishere")的元素也是如此。有什么想法可以做到吗?

以上是关于如何使用 Selenium xpath 查找包含“下载”一词的所有元素?的主要内容,如果未能解决你的问题,请参考以下文章

在selenium中使用xpath查找值大于文本的span

按文本查找元素并获取xpath - selenium webdriver junit

如何使用 Xpath、css 或 Selenium 中的任何其他定位器在 html 中的结束标记后查找带有“== $0”的元素

在 Selenium 测试中使用 XPath 通过文本获取 WebElement

XPath 查找包含 CSS 类的祖先节点

Selenium 通过 xpath 查找所有元素