XPath / Selenium 无法使用包含 / 开头的部分 id 定位元素

Posted

技术标签:

【中文标题】XPath / Selenium 无法使用包含 / 开头的部分 id 定位元素【英文标题】:XPath / Selenium can't locate an element using a partial id with contains / start-with 【发布时间】:2012-11-28 15:20:48 【问题描述】:

我使用 AjaxFormLoop 生成了以下 html

<div id="phones">
    <div class="t-forminjector tapestry-forminjector" id="rowInjector_13b87fdd8b6">
        <input id="number_13b87fdd8b6" name="number_13b87fdd8b7" type="text"/>
        <a id="removerowlink_13b87fdd8b6" href="#" name="removerowlink_13b87fdd8b6">remove</a>
    </div>
    
    <div class="t-forminjector tapestry-forminjector" id="rowInjector_13b87fdda70" style="background-image: none; background-color: rgb(255, 255, 251);">
        <input id="number_13b87fdda70" name="number_13b87fdda70" type="text" />
        <a id="removerowlink_13b87fdda70" href="#" name="removerowlink_13b87fdda70">remove</a>
    </div>
</div>

我正在尝试使用部分 ID 访问子 2 中的第二个输入字段,但是我没有成功地让它工作。

到目前为止我已经尝试过。

String path = "//input[contains(@id,'number_')][2]";
String path = "(//input[contains(@id,'number_')])[2]";

我什至不能使用 1 而不是 2 访问输入 1,但是如果我删除 [2] 并且只使用

String path = "//input[contains(@id,'number_')]";

我可以毫无问题地访问第一个字段。

如果我使用准确的 ID,我可以毫无问题地访问任一字段。

如果可能,我确实需要使用 id,因为在每个 t-forminjector 行中还有更多字段在此示例中不存在。

使用 Selenium 实现。

    final String path = "(//input[starts-with(@id,'quantity_')])[2]";
    
    new Wait() 
        @Override
        public boolean until() 
            return isElementPresent(path);
        
    .wait("Element should be present", TIMEOUT);

已解决

我注意到我似乎无法使用以下开头/包含来定位 dom 中的任何元素,但是如果我使用完整的 id,它就可以工作。

//Partial ID - fails
//*[starts-with(@id,"quantity_")]

//Exact ID - works
//*[starts-with(@id,"quantity_-112409575185705")]

【问题讨论】:

您的第二个 XPath 看起来正确。它选择inputid="number_13b87fdda70" XPath 实现不合规/有缺陷的气味。您的第二个 XPath 表达式是正确的。更精确的表达是:(//input[starts-with(@id, 'number_')])[2] @Dimitree Novatchev,我将 xpath 更改为使用starts-with,但仍然没有成功。我将它与硒一起使用,所以我在上面发布了我的代码。我知道这些元素正在使 dom 做到这一点,我可以使用 selenium 的 getHtmlSource 方法将它们打印出来,并从 selenium 的 getAllFields() 方法构建字段 id 列表,然后使用 xpath //input[@id= 'number_2308423']。这让我相信我要么错误地使用了 xpath,要么其中存在错误。 @DimitreNovaatchev 是对的。使用 xpathtester.com 之类的东西来运行快速测试。 @Lwburk,我发布了一个示例,其中包含从应用程序生成的代码。也许你们将能够帮助指出我的问题。 xpathtester.com/saved/73f39fd7-f240-4e41-bd88-527cc8a8789a 【参考方案1】:

生成的输出you pasted here 根本不包含字符串number_。它确实包含Number_——注意大写N——但它不是字符串的第一部分。也许你的意思是这样的(至少选择 something):

(//input[contains(@id, 'Number_')])[2]

或者:

(//input[starts-with(@id,'catalogNumber_')])[2]

【讨论】:

我从一个简单的电话示例开始我的问题,以简化问题。由于这无助于澄清问题,我设置了一个真正复杂得多的测试用例。我已经尝试了您的两种解决方案,但均未成功。 @George - 我针对您在该链接上发布的内容运行了它,并且效果很好。 是的,我也可以让它在那个测试页面上工作,但是如果我尝试在实际的应用程序中运行它,确切的代码,但嵌套在表单的其余部分中,我会收到一个错误,找不到定位器。 IDE 和 firefox selenium 插件都发生相同的错误。我正在使用 selenium 2.14.0,所以我不确定正在使用哪个版本的 xpath。我可以使用“css=div#lineItem div.t-forminjector:nth-of-type(2) input.quantity”找到元素,但不是最优雅的解决方案。您认为这里可能发生了什么? 可能是命名空间问题。文档前面是否定义了命名空间? 好的,是的,这些元素位于默认命名空间 -- xmlns="http://www.w3.org/1999/xhtml" -- 这意味着您需要将该命名空间与任意前缀相关联,并在 XPath 中使用该前缀限定所有元素名称表达。这就是 XPath 处理器如何知道那些确实是您想要的元素的方式。 Selenium 应该有办法做到这一点,但我不使用 Selenium,所以我帮不上什么忙。【参考方案2】:

正如 Iwburk 所说,这是一个命名空间问题。根据 Selenium API,

http://release.seleniumhq.org/selenium-remote-control/0.9.0/doc/java/com/thoughtworks/selenium/Selenium.html

在使用 xpath 表达式时,我需要使用 xpath=xpathExpression 将我的查询字符串更改为:

String path = "xpath=(//input[starts-with(@id,'quantity_')])[2]";

我在这里找到了一个相关的帖子, Element is found in XPath Checker but not in Selenium

【讨论】:

【参考方案3】:

您无法访问它,因为您没有将元素定位为在页面中是唯一的。 使用使其独一无二的 xpath , - 你的 xpath 看起来不错。 更多信息在这里 http://www.seleniumhq.org/docs/appendix_locating_techniques.jsp

【讨论】:

【参考方案4】:

除了 selenium 语法问题之外,还有一个与标记结构相关的 xpath 问题。 xpath 1://input[starts-with(@id,'number_')][1] xpath 2:(//input[starts-with(@id,'number_')])[1]

在下面的示例中,xpath 1 将返回 2 个节点(不正确),xpath 2 将正确,因为 input 节点不是同级节点,因此需要使用括号来引用生成的节点集

<div id="phones">
    <div>
        <input id="number_1" name="number_1" type="text"/>
    </div>
    <div>
        <input id="number_2" name="number_2" type="text" />
    </div>
</div>

不带括号的结果

/ > xpath //input[starts-with(@id,'number_')][1]
Object is a Node Set :
Set contains 2 nodes:
1  ELEMENT input
    ATTRIBUTE id
    TEXT
        content=number_1
    ATTRIBUTE name
    TEXT
        content=number_1
    ATTRIBUTE type
    TEXT
        content=text
2  ELEMENT input
    ATTRIBUTE id
    TEXT
        content=number_2
    ATTRIBUTE name
    TEXT
        content=number_2
    ATTRIBUTE type
    TEXT
        content=text

在下一个示例中,括号不会产生影响,因为节点是兄弟节点

<div id="other">
<input id="pre_1" type="text"/>
<input id="pre_2" type="text" />
<div>a</div>
</div>

带括号

/ > xpath (//input[starts-with(@id,'pre_')])[1]
Object is a Node Set :
Set contains 1 nodes:
1  ELEMENT input
    ATTRIBUTE id
    TEXT
        content=pre_1
    ATTRIBUTE type
    TEXT
        content=text

没有括号

/ > xpath //input[starts-with(@id,'pre_')][1]
Object is a Node Set :
Set contains 1 nodes:
1  ELEMENT input
    ATTRIBUTE id
    TEXT
        content=pre_1
    ATTRIBUTE type
    TEXT
        content=text

测试是用 xmllint shell 完成的 xmllint --html --shell test.html

【讨论】:

以上是关于XPath / Selenium 无法使用包含 / 开头的部分 id 定位元素的主要内容,如果未能解决你的问题,请参考以下文章

使用包含和小写的xpath的Selenium ChromeDriver

Internet Explorer 的 Selenium 问题:org.openqa.selenium.NoSuchElementException:无法使用 xpath 找到元素

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

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

Python Selenium 无法使用 Selenium 和 Python 在#shadow-root (open) 中通过 xpath 找到元素

Selenium XPath 合并跨越文本并查找包含子字符串的元素