Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/regex/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
用XPath和PHP解析HTML_Php_Regex_Xpath_Html Parsing - Fatal编程技术网

用XPath和PHP解析HTML

用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); 现在我需要以下方面的帮

有没有办法(使用XPath和PHP)执行以下操作(无需外部XSLT文件)

  • 删除所有表及其内容
  • 删除第一个h1标签后的所有内容
  • 只保留段落(包括其内部HTML(链接、列表等))
我收到了一个XSLT答案,但我正在寻找不需要外部文件的XPATH查询

目前,我已通过以下方式将相关HTML加载到SimpleXmlElement中:

$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谢谢。是的,那可能更快。我添加了它作为上述答案的替代方案