如何使用 Nokogiri 正确修复未闭合的 HTML 标签

How to correctly fix unclosed HTML tags with Nokogiri

我很难让网站生成 HTML。 HTML 包含一些未关闭的标签。

例如:

<div>
  <li>
    <div>
      <div>
        test
      </div>

  <li>
     <div>
       test 
     </div>

正在解析 HTML:

html = Nokogiri::HTML(open('origin.html'))

结果:

或者,在 HTML 中:

  <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">

    <html><body>
      <div>

        <li>
          <div>
            <div>
              test
            </div>

        <li>
          <div>
            test 
          </div>

    </li>
    </div>
    </li>
    </div>
    </body>
    </html>

我认为正确的做法应该是这样的:

  <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
   <body>
    <div>
      <li>
        <div>
          <div>
            test
          </div>
        </div>
      </li>

      <li>
        <div>
           test 
        </div>
      </li>
    </div>
  </body>
</html>

知道如何解决这个问题吗?换另一个gem?在解析之前使用正则表达式更改 HTML?

您认为 <li> 不应该嵌套?让我们试着理解为什么 Nokogiri 会这样解析它:

<div>
  <li>
    <div>  <!-- unclosed div -->
      <div>
        test
      </div>    
      <li>
        <div>
           test 
        </div>

首先,您的 HTML 无效,因为(除了缺少结束标记)没有 <ul><ol> 标记,所以 Nokogiri 立即从第一个 <li> 标签。

接下来,让我们看看关于省略结束标记的规范:<li> may omit its end tag, but <div> may not omit它。

您嵌套 <li> 标签的方式,Nokogiri 尝试找到第二个 <div> 的结束标签(参见上面代码清单中的 HTML 注释)并选择较小的邪恶,虽然没有嵌套 <ul> 标签。

您可以考虑使用 Nokogumbo which attaches Googles’ Gumbo HTML5 parser 到 Nokogiri。然后,这将在解析格式错误的 HTML 时使用 HTML5 纠错算法,而不是执行我的 Nokogiri 和 libxml 的默认解析,并将导致解析后的 HTML 更接近您期望的结果从浏览器查看。

这是一个示例 irb 会话,显示它如何处理您的示例 HTML 并产生您想要的结果。注意方法名称是 HTML5,它仍然在 Nokogiri 模块上被调用。

>> require 'nokogumbo'
=> true
>> s = <<EOT
<div>
  <li>
    <div>
      <div>
        test
      </div>

  <li>
     <div>
       test
     </div>
EOT
=> "<div>\n  <li>\n    <div>\n      <div>\n        test\n      </div>\n\n  <li>\n     <div>\n       test \n     </div>\n"
>> puts Nokogiri.HTML5(s).to_html
<html>
<head></head>
<body><div>
  <li>
    <div>
      <div>
        test
      </div>

  </div>
</li>
<li>
     <div>
       test
     </div>
</li>
</div></body>
</html>
=> nil

如何修复 unterminated/unclosed 标签取决于您的目标。 @Matt 的建议是合理的,但是如果原始 HTML 在病理学上是错误的,它仍然会导致错误的 HTML,并且在这一点上你必须介入并在让任何其他人之前进行修复解析器试图理解它。

是否需要使用正则表达式或简单的字符串操作或提取特定行并将其解析为片段取决于具体情况。我不得不做一些非常丑陋的事情,只是为了能够在严重损坏的 HTML 上多次使用解析器,而且每次都是不同的过程。