22

我正在尝试使用 DOMDocument 解析 HTML 代码,对其进行更改之类的操作,然后将其组装回我发送到输出的字符串。

但是有一些关于解析的问题,这意味着我发送给 DOMDocument 的内容并不总是以相同的形式返回:)

这是一个列表:

  1. 使用->loadHTML

    • 无论preserveWhitespaceformatOutput设置如何格式化我的文档(在预格式化文本上丢失空格)
    • 当我有 html5 标签时给我错误<header><footer>等等。但它们可以被抑制,所以我可以忍受这个。
    • 产生不一致的标记 - 例如,如果我添加一个<link ... />元素(带有自关闭标签),在解析/保存 HTML 之后,输出将是<link .. >
  2. 使用->loadXML

    • >对 from<style><script>tags之类的实体进行编码:body > div变为body &gt; div
    • 所有标签都以相同的方式关闭,例如<meta ... />变为<meta...></meta>; 但这可以用正则表达式修复。

我没有尝试 HTML5lib,但出于性能原因,我更喜欢 DOMDocument 而不是自定义解析器


更新:

因此,就像提到的 Honeymonster 一样,使用 CDATA 解决了 loadXML 的主要问题。

有没有什么办法可以防止除了某个集合之外的所有空 HTML 标签的自动关闭,而不使用正则表达式?

现在我有:

$html = $dom->saveXML($node);

$html = preg_replace_callback('#<(\w+)([^>]*)\s*/>#s', function($matches){

       // ignore only these tags
       $xhtml_tags = array('br', 'hr', 'input', 'frame', 'img', 'area', 'link', 'col', 'base', 'basefont', 'param' ,'meta');

       // if a element that is not in the above list is empty,
       // it should close like   `<element></element>` (for eg. empty `<title>`)
       return in_array($matches[1], $xhtml_tags) ? "<{$matches[1]}{$matches[2]} />" : "<{$matches[1]}{$matches[2]}></{$matches[1]}>";
}, $html);

哪个有效,但它也会在 CDATA 内容中进行替换,这是我不想要的......

4

6 回答 6

14

使用html5lib。它可以解析 html5 并生成 DOMDocument。例子:

require_once '/path/to/HTML5/Parser.php';
$dom = HTML5_Parser::parse('<html><body>...');

文档

于 2012-05-23T04:57:38.407 回答
10

如果你想支持 HTML5,就不要碰 DOMDocument。

目前最好的选择似乎是https://github.com/Masterminds/html5-php

以前最好的选择是https://github.com/html5lib/html5lib-php但正如描述所说,它“当前未维护”。这是自 2011 年 10 月以来的状态,所以我不再屏住呼吸了。

我没有html5-php在生产中使用过,所以我无法提供任何关于它的真实世界经验。我html5lib-php在生产中使用过,我会说它可以正确解析格式正确的文档,但它有一些简单的语法错误和意外错误。另一方面,它似乎正确地实现了采用代理算法和其他一些奇怪的极端情况。如果html5lib-php仍然保持,我仍然更喜欢它。但是,就目前的情况而言,我更喜欢使用html5-php并可能帮助修复那里的剩余错误。

于 2016-04-26T12:41:03.580 回答
7

不幸的是,或者可能幸运的是,domdocument 旨在不尝试保留原始文档的格式。这是为了通过保持所有元素相同的样式来使解析器的内部状态更易于管理。Afaik 大多数解析器将在内存中创建树表示,并且在用户请求之前不用担心文本格式。这就是为什么您的自我封闭标签与单独的结束标签一起输出的原因。好消息是这无关紧要。

至于样式标签和脚本标签被<>转换为&lt;&gt;,您可以通过使用推荐的 cdata 标签包围相关元素的内容来避免转换:

<style>
  /*<![CDATA[*/
    body > div {
      width: 50%;
    }
  /*]]>*/
</style>

围绕 cdata 声明的注释/* */是为了允许不了解 cdata 部分的损坏客户端,而是将声明视为 CSS 代码。如果您仅在内部使用该文档,那么您可以省略/* */注释包围并仅使用 cdata 声明。如果您操作文档然后将其发送到浏览器而不检查以确保/* */保留评论,您可能会遇到上述损坏的客户端的问题;我不确定 domdocument 是否会保留这些。

于 2012-05-28T19:04:36.733 回答
3

我尝试了 html5lib 和 html5php,但都没有使用我提供的 HTML。能够解析 HTML 的替代方法是:https ://github.com/ivopetkov/html5-dom-document-php

主类扩展了 PHP 的原生 DomDocument。

于 2017-01-07T18:03:58.437 回答
3

10 年过去了,但 PHP DOMDocument 上的问题仍然存在,我找到了两种解决问题的方法。

解决方案 1

作为选项添加LIBXML_NOERROR到 loadHTML 方法,如下所示:

<?php

$dom = new DOMDocument();

$dom->loadHTML('<header data-attribute="foo">bar<', LIBXML_NOERROR);

echo $dom->saveHTML();
// outputs the html with valid closing tag without any error
?>

解决方案 2

libxml_use_internal_errors(true)在加载 HTML 之前添加

<?php

$dom = new DOMDocument();

libxml_use_internal_errors(true);

$dom->loadHTML('<header data-attribute="foo">bar<');

echo $dom->saveHTML();
// outputs the html with valid closing tag without any error
?>
于 2021-12-10T10:35:16.267 回答
-7

初始化 domDocument 时,请执行以下操作:

$dom = new DOMDocument(5, 'UTF-8');
于 2015-08-26T00:48:37.070 回答