【问题标题】:Encoding broken after using HTML::TreeBuilder as_HTML使用 HTML::TreeBuilder as_HTML 后编码中断
【发布时间】:2014-10-11 10:07:53
【问题描述】:

假设,我们有以下文件:

test.html

<!DOCTYPE html>
<html>
  <head>
    <title>Евгений Онегин</title>
    <meta charset="utf-8">
  </head>
  <body>
    <p><cite>Евгений Онегин</cite></p>
    <pre>
      Не мысля гордый свет забавить,
      Вниманье дружбы возлюбя,
      Хотел бы я тебе представить
      Залог достойнее тебя,
    </pre>
</body>
</html>

我想用解析器获取 HTML 格式的 body 标签的内容:

<p><cite>Евгений Онегин</cite></p>
<pre>
  Не мысля гордый свет забавить,
  Вниманье дружбы возлюбя,
  Хотел бы я тебе представить
  Залог достойнее тебя,
</pre>

parser.pl

#!/usr/bin/env perl

use strict;
use warnings;
use 5.010;
use utf8;

use HTML::TreeBuilder;

my $root = HTML::TreeBuilder->new;
$root->parse_file('test.html');

my $body = $root->find('body');
print $body->as_HTML;

当我将输出保存到 HTML 文件并在浏览器中以 Unicode 格式观看时,编码被破坏:而不是“Евгений Онегин”,我得到的是“Евгений Онегин”。

正确的工作

当 HTML 存储在 Perl 文件中时,它可以正常工作:

#!/usr/bin/env perl

use strict;
use warnings;
use 5.010;
use utf8;

use Data::Dumper;
use HTML::TreeBuilder;

my $root = HTML::TreeBuilder->new;
$root->parse_file(\*DATA);

my $body = $root->find('body');
print $body->as_HTML;

__END__
<!DOCTYPE html>
<html>
  <head>
    <title>Евгений Онегин</title>
    <meta charset="utf-8">
  </head>
  <body>
    <p><cite>Евгений Онегин</cite></p>
    <pre>
      Не мысля гордый свет забавить,
      Вниманье дружбы возлюбя,
      Хотел бы я тебе представить
      Залог достойнее тебя,
    </pre>
</body>
</html>

因此,当 HTML::TreeBuilder 从文件中读取时,就会发生错误。

问题:

  1. 如何修复编码?
  2. 该模块将每个俄语字符编码为一个实体:&amp;#x415;。是否可以保存为字符Е

【问题讨论】:

    标签: perl encoding html-parsing


    【解决方案1】:

    parse_file 方法将采用文件名或文件句柄,因此最简单的解决方案是使用open 调用以:utf8 作为模式打开文件,然后将文件句柄传递给已解析。

    看起来像这样。我使用new_from_file 构造函数只是因为它保存了一个语句。效果和你自己的代码完全一样。

    #!/usr/bin/env perl
    
    use strict;
    use warnings;
    use 5.010;
    use utf8;
    
    use HTML::TreeBuilder;
    
    my $file = 'test.html';
    
    open my $fh, '<:utf8', $file or die qq{Unable to open "$file" for parsing: $!};
    my $root = HTML::TreeBuilder->new_from_file($fh);
    
    my $body = $root->find('body');
    print $body->as_HTML;
    

    至于将实体更改为字母,我不清楚您的意思。您是否只想删除所有十六进制实体并用等效字符替换它们?您可能会从HTML::Entities 模块中获得一些好处。

    【讨论】:

    • 有没有办法解决关于实体的第二个问题?
    • @user4035:也许吧。我不清楚你在问什么。我已添加到我的答案中。
    • 我添加了 decode_entities($body->as_HTML);现在一切都很棒。
    • @user4035:我很高兴听到这个消息。不过要小心,因为如果文本中有任何&amp;lt;&amp;gt;,那么decode_entities 会将它们转换为&lt;&gt;,这会使HTML 无效。
    【解决方案2】:

    您可以使用man HTML::TreeBuilder 中记录的字符集自动检测。

    当您将文件名传递给parse_file 时,HTML::Parser 以二进制模式打开它,这意味着它被解释为 Latin-1 (ISO-8859-1)。如果文件是另一种编码,例如UTF-8UTF-16,这将不会做正确的事情。

    一种解决方案是使用正确的:encoding 层自己打开文件,并将文件句柄传递给parse_file。您可以使用html_file in IO::HTML 自动执行此过程,它将使用HTML5 编码嗅探算法自动确定正确的:encoding 层并应用它。

    HTML-Tree 的下一个主要版本中,我计划让它自动使用IO::HTML。如果你真的希望你的文件以二进制模式打开,你应该自己打开它并将文件句柄传递给parse_file

    因此,使用IO::HTML 来自动检测已打开文件的字符集。

    use HTML::TreeBuilder;
    use IO::HTML;  # exports html_file by default
    
    my $root = HTML::TreeBuilder->new;
    $root->parse_file(html_file('test.html'));
    

    https://stackoverflow.com/a/24577042/2139766

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-16
      • 1970-01-01
      • 1970-01-01
      • 2012-07-14
      • 2012-01-23
      相关资源
      最近更新 更多