php XPath子内容

xeufq47z  于 2023-04-28  发布在  PHP
关注(0)|答案(2)|浏览(96)

我正在尝试获取以下html的段落内容:

<h4 class="m-b-0 text-dark synopsis"><p>This is the text I want.</p></h4>

有几个h4,但只有一个类为synopsis
我可以用print_r($xpath->query("//h4[contains(@class, 'synopsis')]"));获取h4元素,但无法获取子段落内容。
我做错了什么?

lymnna71

lymnna711#

h4不能包含p。PHP DOMDocument将尝试修复HTML:

$document = new DOMDocument();
$document->loadHTML(
  '<h4 class="m-b-0 text-dark synopsis"><p>This is the text I want.</p></h4>',
);
echo $document->saveHTML();
Warning: DOMDocument::loadHTML(): Unexpected end tag : h4 in Entity, line: 1 in /in/BTVAZ on line 4
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><h4 class="m-b-0 text-dark synopsis"></h4><p>This is the text I want.</p></body></html>

这可以通过一些加载标志来避免:

$document = new DOMDocument();
$document->loadHTML(
  '<h4 class="m-b-0 text-dark synopsis"><p>This is the text I want.</p></h4>',
  LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NOERROR
);
echo $document->saveHTML();
<h4 class="m-b-0 text-dark synopsis"><p>This is the text I want.</p></h4>

class属性值由用空格分隔的标记组成。一个简单的contains()将匹配字符串,如果它是另一个类名的一部分。
将它们与Xpath 1匹配。0,使用normalize-space()concat()。我们的想法是将属性值转换为{space}classOne{space}classTwo{space},并将它们与{space}classOne{space}进行匹配。

  • 将所有白色序列替换为单个空格并修剪该值。

normalize-space(@class)

  • 在开始/结束处添加空格:

concat(' ', normalize-space(@class), ' ')

  • 查找由空格包围的类名:

[contains(concat(' ', normalize-space(@class), ' '), ' synopsis ')]

  • 将任何元素节点与类匹配:

//*[contains(concat(' ', normalize-space(@class), ' '), ' synopsis ')]

  • 将第一个节点转换为字符串:

string(//*[contains(concat(' ', normalize-space(@class), ' '), ' synopsis ')])

$document = new DOMDocument();
$document->loadHTML(
  '<h4 class="m-b-0 text-dark synopsis"><p>This is the text I want.</p></h4>',
  LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NOERROR
);
$xpath = new DOMXpath($document);

$expression = "string(//*[contains(concat(' ', normalize-space(@class), ' '), ' synopsis ')])";

var_dump($xpath->evaluate($expression));

输出:

string(24) "This is the text I want."

如果尝试获取多个节点,请删除Xpath中的字符串转换。表达式将返回一个节点列表。迭代节点并读取$textContent属性。它将包含所有子代文本节点的内容。

$expression = "//*[contains(concat(' ', normalize-space(@class), ' '), ' synopsis ')]";

foreach ($xpath->evaluate($expression) as $synopsis) {
    var_dump($synopsis->textContent);
}
xtupzzrd

xtupzzrd2#

  • 这个答案在XML级别上是正确的,但是OP的真实的问题是由于php/libxml试图修复有效性约束冲突,该冲突不允许h4中的p元素。更多详情请参阅@ThW的回答(+1)。*

如果

//h4[contains(@class, 'synopsis')]

选择所需的h4元素,然后

//h4[contains(@class, 'synopsis')]/p

将选择所需h4元素的子p元素,并且

//h4[contains(@class, 'synopsis')]/p/text()

将选择这些p元素的文本节点子节点。
可以通过string()获取节点的字符串值:

string(//h4[contains(@class, 'synopsis')]/p)

请注意,上面假设XPath 1。0(或者只有一个这样的p),其中将返回//h4/p选择的节点集合的第一个节点的字符串值。将节点序列传递给string()XPath 2中是错误的。0和更高的,这里应该用途:

string((//h4[contains(@class, 'synopsis')]/p)[0])

如果可能有一个以上这样p,或者

//h4[contains(@class, 'synopsis')]/p/string()

如果您希望返回所有此类p元素的字符串值。

HTML示例

<!doctype html>
<html>
<head>
  <title>p is not allowed in h4...</title>
</head>
<body>
  <h4><p>...but can still be selected via XPath</p></h4>
</body>
</html>

XPath选择示例

$x("//h4/p")
[p]
$x("string(//h4/p)")
'...but can still be selected via XPath'

相关问题