【问题标题】:Parse HTML to trim it解析 HTML 以修剪它
【发布时间】:2014-02-26 17:54:28
【问题描述】:

假设我有以下代码:

<div class="content">
    <h3>Test</h3>
    <img src="#" alt="" />
    <p>Lorem ipsum</p>
    <p>dolor sit</p>
    <p><!-- pagebreak --></p>
    <p>amet</p>
</div>

我想在分页后修剪我的代码并删除所有内容,但保留 html 逻辑 (&lt;/div&gt;):

<div class="content">
    <h3>Test</h3>
    <img src="#" alt="" />
    <p>Lorem ipsum</p>
    <p>dolor sit</p>
</div>

工具:Zend,常规 PHP。

有什么想法吗?

【问题讨论】:

  • 你试过DOMDocument吗?
  • &lt;!-- pagebreak --&gt; 实际上是文字 SGML 注释还是会有一些 HTML 实体定义它的位置?
  • 你能确定分页符出现在哪里?分页符是否总是在具有“内容”类的 div 中?
  • 它将由 WYSIWYG 编辑器定义,所以“基本上”它会在 div.content>p 中,但我不能保证。

标签: php html parsing zend-framework


【解决方案1】:

您可以像这样使用 xpath 访问这些节点:

$doc = new DOMDocument;
$doc->loadHTML($html); // your content with a between '<html></html>' pair
$xpath = new DOMXpath($doc);
$elements = $xpath->query('//p[contains(comment(), "pagebreak")]/following-sibling::*|//p[contains(comment(), "pagebreak")]');
foreach ($elements as $elem) {
    $elem->parentNode->removeChild($elem);
}   

print $doc->saveHTML();

xpath 的一点小故障:

  • //p 每个 P 元素。
  • //p[] 过滤 P 的列表,就像它是一个数组一样。
  • //p[contains(comment(), "pagebreak")] 过滤器是他们的comment() 值是否包含“pagebreak”字符串。
  • //p[contains(comment(), "pagebreak")]/following-sibling::* 已选择 P 的所有后续兄弟。
  • 另一部分只是一个联合 (|),具有相同的模式,但没有 following-sibling::* 部分。

编辑:

一个可能不那么复杂的 xpath 是:

//p[contains(comment(), "pagebreak")]/preceding-sibling::*[1]/following-sibling::*

这可以通过在兄弟列表中后退一个来解决问题(此时针对带有注释的那个之前的&lt;p&gt;)并拥有该节点的following-siblings::*

【讨论】:

  • 天哪,这是一个复杂的 XPath。不过似乎效果很好,+1。
  • 可能写得不那么冗长,但我似乎无法弄清楚如何将self 包含到联合的第一部分(-:
  • 添加了一些细分和一个可能不那么愚蠢的版本。
【解决方案2】:

我做到了!我把它当作个人挑战,我终于用正则表达式做到了!
这段代码可能没有优化,那么请不要犹豫告诉我如何改进它(但使用正则表达式,而不是 DOM),但它仍然有效! :)

$str = '<div>
    <h3>Test</h3>
    <img/>
    <p>Lorem ipsum</p>
    <p>dolor sit</p>
    <p><-- pagebreak --></p>
    <p>amet</p>
    <blockquote>
        <p>
            lol
        <p>
    </blockquote>
</div>';


$pagebreak_str = '-- pagebreak --';
$weird_replacement = '§~@';

$parsed = array();
$is_below_break = false;

while (preg_match ("#<([^/]+)>#isU",$str)) {
    if ($is_below_break) {
        $str = preg_replace ("#<" .$pagebreak_str. ">#isU", "", $str);
        $str = preg_replace ("#<[^/>]+>(.+)</[^/>]+>#isU", "", $str);
        $str = preg_replace ("#<[^/>]+/>#isU", "", $str);
    }
    else {
        $get = preg_replace ("#^(.*)<([^/>]+)>(.*)$#isU","$2",$str,1);

        if ($get == $pagebreak_str)
            $is_below_break = true;
        if (!$is_below_break)
            $str = preg_replace ("#<([^/>]+)>#isU","$weird_replacement$1>",$str,1);
        if (preg_match ("#</([^/>]+)>(.+)(<$pagebreak_str>)#isU", $str))
            $str = preg_replace ("#</([^/>]+)>#isU", "$weird_replacement/$1>", $str, 1);
    }
}
$str = preg_replace ("#$weird_replacement#isU", "<", $str);

echo $str;

此代码假设您没有任何 html 编码错误,例如交叉标记 (

),但它绝对可以使用正确的编码样式。
试试看,享受吧!

【讨论】:

  • 那些用 PHP 干预的正则表达式看起来像经过较长时间的尝试和错误之后,您发现了一些最终在这种特定情况下起作用的东西。但我很确定它与 HTML 的语法不兼容。最好使用现有的解析器或知道如何使用正则表达式解析 HTML(在 PHP 中不容易实现),这里有一个示例:cs.sfu.ca/~cameron/REX.html,如果您想探索更多选项。
  • 没有说这是理想的做法。当然,DOM 元素更容易操作,也更可靠。当我有时间时,我会阅读所有这些文档。但我真的把它作为个人挑战比其他任何事情都重要。不过,感谢您的回答。
猜你喜欢
  • 1970-01-01
  • 2013-01-28
  • 2017-04-26
  • 2010-12-21
  • 2012-11-21
  • 2011-10-09
  • 1970-01-01
  • 2021-10-21
  • 2017-03-19
相关资源
最近更新 更多