使用HTML :: TreeBuilder as_HTML后编码损坏

时间:2014-08-18 13:14:39

标签: perl encoding html-parsing

假设我们有以下文件:

的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. 该模块将每个俄语字符编码为一个实体:&#x415;。是否可以将其保存为角色Е

2 个答案:

答案 0 :(得分:4)

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模块中获得一些里程数。

答案 1 :(得分:3)

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

  

当您将文件名传递给parse_file时,HTML::Parser会以二进制模式打开它,这意味着它被解释为Latin-1(ISO-8859-1)。如果文件采用其他编码方式,例如UTF-8UTF-16,则无法做到正确。

     

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

     

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

因此,使用https://stackoverflow.com/a/24577042/2139766自动检测已打开文件的字符集。

use HTML::TreeBuilder;
use IO::HTML;  # exports html_file by default

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

{{3}}