【问题标题】:How to write XPATH for nodes under dynamic hierarchy如何为动态层次结构下的节点编写 XPATH
【发布时间】:2012-12-30 01:35:08
【问题描述】:

我得到了一个很大的 XML。该 XML 的 sn-p 如下所示:

<div class="x-column-inner" id="ext-gen422" style="width: 850px;">
 <div id="ext-comp-1206" style="width: 14px;" class=" x-column">
  <div tabindex="-1" class="x-form-item  x-hide-label" id="ext-gen434">
   <label class="x-form-item-label" style="width:100px;" for="ext-comp-1180" id="ext-gen435"></label>
  <div style="padding-left:105px" id="x-form-el-ext-comp-1180" class="x-form-element">
   <div class="x-form-check-wrap" id="ext-gen436" style="width: 14px; height: 28px;">
    <input type="checkbox" name="ext-comp-1180" id="ext-comp-1180" autocomplete="off" class=" x-form-checkbox x-form-field">
     <label class="x-form-cb-label" for="ext-comp-1180" id="ext-gen437">&nbsp;</label>
    </div></div>  <div class="x-form-clear-left">
    </div>
   </div>
  </div>
 <div id="ext-comp-1207" style="width: 150px;" class=" x-column">
  <label id="ext-comp-1203" style="width: 140px;">Add to Watchlist</label>
</div>
<div id="ext-comp-1208" style="width: 107px;" class=" x-column">

我需要根据具有文本“添加到监视列表”的标签节点找到复选框类型的“输入”节点。

由于 'input' 和 'label' 节点位于不同的层次结构中, // 语法似乎不起作用:

//div[label[contains(text(),'Add to Watchlist')]]

只会给出子标签的父 div。 我试图从这个 sn -p 的最顶层节点开始

$x("//div[@class='x-column-inner' and //label[contains(text(),'Add to Watchlist')]]")

但这给出了 6 个可能的匹配项。

注意:不能使用@id 属性,因为这是动态分配给节点的,所以下次页面加载时@id 会有所不同。 我不想使用 position() 谓词,因为这会使 XPATH 静态,并且 xpath 可能会随着位置的任何变化而中断。

【问题讨论】:

  • 您的输入和您文档中的标签标签之间可能有什么关系?另外,您要检索标签还是输入标签?
  • 似乎只有逻辑关系,因为标签是输入的标签。我无法破译句法关系:(
  • 打折你的例子是畸形的,他们似乎是兄弟姐妹,所以有些事情你没有告诉我们。
  • 抱歉缩进不好。我已经更正了一点。

标签: xpath


【解决方案1】:

你可以尝试这样的事情,但它看起来很贪婪......基本上它所做的是在input标签的每个轴上搜索,看看是否有关联的label标签。因此,对于每个input,它都会在其祖先、后代和兄弟姐妹中进行搜索。 当然有一些更智能的解决方案。

//input[@type = 'checkbox' and (@id = ancestor::label/@for or @id = descendant::label/@for or @id = following::label/@for or @id = preceding::label/@for)]

但是您的 sn-p 并不有趣,不会匹配 input 标签,请考虑提供更好的 sn-p。这将提高答案的准确性。

编辑:这是添加“添加到关注列表”约束的(未经测试的)方法。

//input[@type = 'checkbox' and (@id = ancestor::label[. = 'Add to Watchlist']/@for or @id = descendant::label[. = 'Add to Watchlist']/@for or @id = following::label[. = 'Add to Watchlist']/@for or @id = preceding::label[. = 'Add to Watchlist']/@for)]

但是再一次,这些 xpath 请求非常贪婪,并且不能保证您匹配与 label 关联的每个 input 元素,例如以下 input 在此 sn-p 中将不匹配:

<div>
  <div>
    <label for="id">Add to Watchlist</label>
  </div>
  <div>
    <input type="checkbox" id="id" />
  </div>
<div>

在一个 xpath 请求中可能有更有效的解决方案,但您应该考虑执行多个请求。
例如,一个请求查找带有文本“添加到监视列表”的label 元素的每个for 属性值,然后执行另一个请求以查找关联的input 元素。 我还应该尝试将您的请求限制在底层 form 元素的范围内。如果我有时间,也许我会提出更好的要求来编辑。

编辑 2 这是一个有效且更智能的请求

//form//input[@type = 'checkbox' and @id = ancestor::form[1]//label[. = 'Add to Watchlist']/@for]

你可以面对这个sn-p

<html>
  <form>
    <label for="bar">Add to Watchlist</label>
    <div>
      <div>
        <label for="id">Add to Watchlist</label>
      </div>
      <div>
        <input type="checkbox" id="id" />
        <input type="checkbox" id="foo" />
        <input type="checkbox" id="bar" />
        <input type="checkbox" />
        <input type="checkbox" id="" />
      </div>
    </div>
  </form>
  <label for="foo">Add to Watchlist</label>
</html>

最重要的是您了解它的工作原理以及它为什么更好。请花点时间考虑一下。

【讨论】:

  • 请原谅我的sn-p。给予更大的 sn-p,即这个 sn-p 的超集,会使它变得更好吗?我没有添加太大的 sn-p 因为它会使问题变得笨拙。还有什么其他想法可以让它更好地 sn-p 以提高答案的准确性吗?谢谢。
  • 您应该创建一个简约的 sn-p,其中一些 input 节点将由 xpath 表达式匹配。你试过我的答案吗?
  • 感谢 Zoom。您的解决方案已将节点范围缩小到 xml 中存在的两个复选框。 2个节点是:&lt;input type="checkbox" name="ext-comp-1185" id="ext-comp-1185" autocomplete="off" class=" x-form-checkbox x-form-field"&gt;&lt;input type="checkbox" name="ext-comp-1180" id="ext-comp-1180" autocomplete="off" class=" x-form-checkbox x-form-field"&gt;现在两个节点都没有任何唯一属性,除了动态@id,我需要求助于位置()吗?
  • 我不明白你需要实现什么。找到的两个input 标记已被选中,因为它们链接到另一个label 标记,该标记具有与input 标记的id 属性匹配的for 属性。是你想要的吗?
  • 感谢您的时间和帮助。实际上我只想要 input 附加到特定 label 具有文本 Add to Watchlist 的节点,所以我将 xpath 修改为://input[@type = 'checkbox' and (@id = ancestor::label/@for or @id = descendant::label/@for or @id = following-sibling::label/@for or @id = preceding-sibling::label/@for) and (following::label = 'Add to Watchlist')] 并且它有效:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-09-27
  • 2013-06-24
  • 1970-01-01
  • 2019-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多