xpath匹配第一个和最后一个孩子

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了xpath匹配第一个和最后一个孩子相关的知识,希望对你有一定的参考价值。

试图确定任何P标签文本是否完全在strong / B标签内

// Match (unacceptable, flag to user):
<p><strong>Any text and <span>maybe</span> other <em>tags</em></strong></p>
// Don't match (acceptable):
<p>Any text and <strong>maybe</strong> other <em>tags</em></p>
答案
  • 任何p ...... //p
  • 至少有一个strong后代节点...... //p[.//strong]
  • 有一些文本内容,只有空格... //p[.//strong[normalize-space(.) != ""]]
  • 并且没有没有strong祖先节点的内容的文本节点后代: //p[ .//strong[normalize-space(.) != ""] and not(.//text()[normalize-space(.) != "" and not(ancestor::strong)]) ]

这将检查两个条件。首先,该段落有一些实际内容在strong内部,并且没有实际内容没有在strong内 - 换句话说,内容格式不同。

例:

$html = <<<'HTML'
<p><strong>Any text and <span>maybe</span> other <em>tags</em></strong></p>
<p>Any text and <strong>maybe</strong> other <em>tags</em></p>
<p><strong>Builder's</strong> <strong>tea</strong></p>
<p><em><strong>Builder's</strong> <strong> tea</strong></em></p>
HTML;

$document = new DOMDocument();
$document->loadHTML($html);
$xpath = new DOMXpath($document);

$expression = 
  '//p[
      .//strong[normalize-space(.) != ""] and 
      not(.//text()[normalize-space(.) != "" and not(ancestor::strong)])
    ]';

foreach ($xpath->evaluate($expression) as $p) {
  var_dump(
    $document->saveXml($p)
  );
}

输出:

string(75) "<p><strong>Any text and <span>maybe</span> other <em>tags</em></strong></p>" 
string(54) "<p><strong>Builder's</strong> <strong>tea</strong></p>" 
string(64) "<p><em><strong>Builder's</strong> <strong> tea</strong></em></p>"

表达式可以扩展到覆盖b

//p[
   (
     .//strong[normalize-space(.) != ""] or
     .//b[normalize-space(.) != ""]
   ) and 
   not(
     .//text()[
       normalize-space(.) != "" and 
       not(ancestor::*[self::strong or self::b])
     ]
   )
]
另一答案

这是一种方式,部分基于@gangabass的建议。它计算仅包含单个<p>元素的<strong>元素,这些元素可选地仅由空白文本包围。

$unacceptableNodesCount = $xpath->evaluate( 'count(//p[count(*) = 1 and name(*) = "strong" and normalize-space() = string(strong)])' );

var_dump( $unacceptableNodesCount );

说实话,如果目标是阻止用户仅使用粗体文本并确定用户,他们可能会找到一种方法。例如,通过用Unicode空格字符或类似的东西包围<strong>元素。

另一答案

您的问题描述表明您也想抓住

<p><strong>Builder's</strong><strong> tea</strong></p>

也许也是

<p><strong>Builder's</strong> <strong>tea</strong></p>

一些建议的解决方案没有抓住这些解决方案。

但目前尚不清楚你是否也想抓住

<p><emph><strong>Builder's</strong> <strong> tea</strong></emph></p>

我认为XPath 2.0中最接近“任何P标签文本完全在强/ B标签内”是

//p[empty(.//text()[normalize-space()] except .//strong//text()])]

它选择所有不具有非白色后代文本节点的p元素,该节点不是p中强元素的后代。

我无法立即在XPath 1.0中看到这样做的方法,但我的XPath 1.0非常生疏。

另一答案

以下代码检查P标记在任何Strong标记之前和之后是否包含任何文本或其他HTML标记,确定P标记完全是粗体(强)。

$false_headings = $xpath->query("//p/strong");

foreach ($false_headings as $heading) {
    if ($heading->previousSibling === null and $heading->nextSibling === null) {
        // Report to user 
        break;
    }
}

以上是关于xpath匹配第一个和最后一个孩子的主要内容,如果未能解决你的问题,请参考以下文章

xpath语法

xpath语法

IE8 的第一个孩子和最后一个孩子

算法笔记_113:算法集训之代码填空题集一(Java)

XPath:如何从一个孩子导航到同一个父母的另一个孩子

xpath