Pet*_*ter 2 php regex xpath html-parsing
有没有办法(使用XPath和PHP)执行以下操作(没有外部XSLT文件)?
我在这里收到了一个XSLT答案,但我正在寻找不需要外部文件的XPATH查询.
目前,我通过以下方式将有问题的HTML加载到SimpleXmlElement中:
$doc = @DOMDocument::loadHTML($xml);
$data = simplexml_import_dom($doc);
Run Code Online (Sandbox Code Playgroud)
现在我需要帮助:
$data = $data->xpath('??????');
Run Code Online (Sandbox Code Playgroud)
一直在使用这个几天无济于事.我非常感谢你的帮助.
编辑:我并不特别在意段落内的内容,因为我可以使用strip_tags来消除我不想要的内容.我需要做的就是将段落与其他来源隔离开来.我想更具体,更准确的要求是:
仅返回未包含在表中且仅在第一个h1标记之前的段落(及其html内容)
编辑2:
我想我已经完成了大部分工作:
$query = $xpath->query('//p[not(ancestor::table) and not(preceding::h2)]');
唯一的问题是内部HTML的丢失.
要获得不在表中且仅在第一个h1之前的所有P元素,您可以这样做
$xp = new DOMXPath($dom);
$expression = '//p[not(preceding::h1[1]) and not(ancestor::table)]';
foreach ($xp->query($expression) as $node) {
echo $dom->saveXml($node);
}
Run Code Online (Sandbox Code Playgroud)
通常,如果您知道文档中第一个h1的位置,则使用该元素的直接路径更为高效,而不是//
在文档中的任何位置搜索的查询.例如,作为替代方案,您还可以在下面的注释中使用Alejandro提供的XPath:
/descendant::h1[1]/preceding::p[not(ancestor::table)]
Run Code Online (Sandbox Code Playgroud)
如果要从源文档中的节点创建新的DOM文档,则必须将节点导入新文档.
// src document
$dom = new DOMDocument;
$dom->loadXML($xml);
// dest document
$new = new DOMDocument;
$new->formatOutput = TRUE;
// xpath setup
$xp = new DOMXPath($dom);
$expr = '//p[not(preceding::h1[1]) and not(ancestor::table)]';
// importing nodes into dest document
foreach ($xp->query($expr) as $node) {
$new->appendChild($new->importNode($node, TRUE));
}
// output dest document
echo $new->saveXML();
Run Code Online (Sandbox Code Playgroud)
还有一些补充
在您的示例中,您使用了错误抑制运算符.这是不好的做法.如果要忽略DOM中的任何解析错误,请使用
libxml_use_internal_errors(TRUE); // catch any DOM errors with libxml
$dom = new DOMDocument; // remove the @ as it is bad practise
$dom->loadXML($xhtml); // use loadHTML if it's not valid XHTML
libxml_clear_errors(); // disregards any DOM related errors
Run Code Online (Sandbox Code Playgroud)
使用DOM删除节点始终是相同的方法.找到要删除的节点.得到它parentNode
并removeChild
使用要删除的节点作为参数调用它.
foreach ($dom->getElementsByTagName('foo') as $node) {
$node->parentNode->removeChild($node);
}
Run Code Online (Sandbox Code Playgroud)
您还可以在没有XPath的情况下导航到兄弟节点(和子节点).以下是在第一个h1元素之后删除所有后续兄弟节点的方法
$firstH1 = $dom->getElementsByTagName('h1')->item(0);
while ($firstH1->nextSibling !== NULL) {
$firstH1->parentNode->removeChild($firstH1->nextSibling);
}
echo $dom->saveXml();
Run Code Online (Sandbox Code Playgroud)
从中删除节点DOMDocument
会DOMDocument
立即影响.在上面的代码中,我们总是查询第一个h1的第一个兄弟.如果有,则将其从中删除DOMDocument
.nextSibling
然后指向刚删除的兄弟(如果有的话)之后的兄弟姐妹.
获取和打印所有段落同样容易.要获取outerXML,只需将您想要outerXML的节点传递给该saveXML
方法.
foreach ($dom->getElementsByTagName('p') as $paragraph)
{
echo $dom->saveXml($paragraph);
}
Run Code Online (Sandbox Code Playgroud)
无论如何,这应该让你去.我建议你熟悉DOM API.这并不困难.你会发现你要做的大部分事情都围绕着属性和方法DOMDocument
,DOMNode
并且DOMElement
(它是它的子类DOMNode
).