用XPath和PHP解析HTML
有没有办法(使用XPath和PHP)执行以下操作(无需外部XSLT文件)用XPath和PHP解析HTML,php,regex,xpath,html-parsing,Php,Regex,Xpath,Html Parsing,有没有办法(使用XPath和PHP)执行以下操作(无需外部XSLT文件) 删除所有表及其内容 删除第一个h1标签后的所有内容 只保留段落(包括其内部HTML(链接、列表等)) 我收到了一个XSLT答案,但我正在寻找不需要外部文件的XPATH查询 目前,我已通过以下方式将相关HTML加载到SimpleXmlElement中: $doc = @DOMDocument::loadHTML($xml); $data = simplexml_import_dom($doc); 现在我需要以下方面的帮
- 删除所有表及其内容
- 删除第一个h1标签后的所有内容
- 只保留段落(包括其内部HTML(链接、列表等))
$doc = @DOMDocument::loadHTML($xml);
$data = simplexml_import_dom($doc);
现在我需要以下方面的帮助:
$data = $data->xpath('??????');
我和这个人合作了好几天,都没有结果。我真的很感谢你的帮助
编辑:我并不特别关心段落中的内容,因为我可以使用strip_标签来消除我不想要的内容。我所需要做的就是将这些段落与源代码的其余部分隔离开来。我想更具体、更准确的要求是:
只返回表中不包含的段落(及其html内容),并且只返回第一个h1标记之前的段落
编辑2:
我想我大部分都是通过这个:$query=$xpath->query('//p[not(祖先::表)和not(前面的::h2)]代码>
唯一的问题是内部HTML的丢失。要只获取表中以外的所有p元素,并且只在第一个h1之前,您可以这样做
$xp = new DOMXPath($dom);
$expression = '//p[not(preceding::h1[1]) and not(ancestor::table)]';
foreach ($xp->query($expression) as $node) {
echo $dom->saveXml($node);
}
通常,如果您知道文档中第一个h1的位置,则使用该元素的直接路径比使用搜索文档中任何位置的/
查询更有效。例如,您也可以在下面的注释中使用Alejandro提供的XPath:
/descendant::h1[1]/preceding::p[not(ancestor::table)]
如果要从源文档中的节点创建新的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();
更多添加内容
在您的示例中,使用了错误抑制运算符。这是个坏习惯。如果要忽略来自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
使用DOM删除节点始终是相同的方法。找到要删除的节点。进入它的parentNode
并调用它的removeChild
,将要删除的节点作为参数
foreach ($dom->getElementsByTagName('foo') as $node) {
$node->parentNode->removeChild($node);
}
您还可以导航到没有XPath的同级节点(和子节点)。下面是如何删除第一个h1元素之后的所有同级元素
$firstH1 = $dom->getElementsByTagName('h1')->item(0);
while ($firstH1->nextSibling !== NULL) {
$firstH1->parentNode->removeChild($firstH1->nextSibling);
}
echo $dom->saveXml();
从DOMDocument
中删除节点将立即影响DOMDocument
。在上面的代码中,我们总是查询第一个h1的第一个兄弟。如果有,它将从DOM文档中删除nextSibling
然后将指向刚删除的同级(如果有)之后的同级
获取和打印所有段落同样容易。要获取outerXML,只需将要获取outerXML的节点传递给saveXML
方法
foreach ($dom->getElementsByTagName('p') as $paragraph)
{
echo $dom->saveXml($paragraph);
}
不管怎样,这应该能让你走了。我建议你。这并不难。您会发现,您将要做的大多数事情都是围绕DOMDocument
、DOMNode
和DOMElement
(这是DOMNode
的子类)中的属性和方法进行的。谢谢您,Gordon
解决方案:
$dom = @DOMDocument::loadHTML($xml);
$xpath = new DOMXPath($dom);
$query = $xpath->query('//p[
not(ancestor::table) and
not(preceding::h1[1])
]');
foreach ($query as $node){
$result .= $dom->saveXml($node);
}
echo $result;
根据你的假设,可以说你只需要,包括p
中的表格吗?(相关),但我不想要表格中的任何段落。+1回答得好。也许/genderant::h1[1]/previous::p[not(祖先::表)]
会更快(不测试每个p
的所有程序)@Alejandro谢谢。是的,那可能更快。我添加了它作为上述答案的替代方案