使用perl的HTML解析器

时间:2012-12-09 07:56:27

标签: html perl parsing

我正在尝试使用perl脚本解析html文件。我正在尝试用html标签p grep所有文本。如果我查看源代码,数据将以这种格式写入。

  

<p>指标都是特定于虚拟化的,并按优先顺序排列并按如下方式分组:</p>

以下是代码。

use HTML::TagParser();

use URI::Fetch;

//my @list = $html->getElementsByTagName( "p" );

    foreach my $elem ( @list ) {
        my $tagname = $elem->tagName;
        my $attr = $elem->attributes;
        my $text = $elem->innerText;

        push (@array,"$text");

        foreach $_  (@array) {
           # print "$_\n"; 
           print $html_fh "$_\n";   
          chomp ($_);        
           push (@array1, "$_");
         }
       } 
    }

$end = $#array1+1;

print "Elements in the array: $end\n";

close $html_fh;

我面临的问题是生成的输出是4.60 Mb,很多数组元素只是重复句子。我怎样才能避免这种重复?有没有其他有效的方法来grep我感兴趣的行。有人可以帮我解决这个问题吗?

3 个答案:

答案 0 :(得分:3)

您看到重复行的原因是您要为其中的每个元素打印整个数组一次。

foreach my $elem ( @list ) {
    my $tagname = $elem->tagName;
    my $attr = $elem->attributes;
    my $text = $elem->innerText;

    push (@array,"$text");      # this array is printed below

    foreach $_  (@array) {      # This is inside the other loop
       # print "$_\n"; 
       print $html_fh "$_\n";   # here comes the print
      chomp ($_);        
       push (@array1, "$_");
     }
   } 

例如,如果您有一个数组"foo", "bar", "baz",它将打印:

foo   # first iteration
foo   # second
bar
foo   # third
bar
baz

因此,要修复重复错误,请将第二个循环移到第一个循环之外。

其他一些说明:

您应该始终使用这两个编译指示:

use strict;
use warnings;

他们将提供比您可以做的任何其他单一事物更多的帮助。与修复错误相关的简短学习曲线可以弥补大大减少调试时间。

//my @list = $html->getElementsByTagName( "p" );

perl中的注释以#开头。不确定这是否是拼写错误,因为你在下面使用这个数组。

foreach my $elem ( @list ) {

除非需要数组,否则无需将标记实际存储到数组中。仅在这种情况下,这是一个中间变量。您只需执行以下操作(请注意forforeach完全相同):

for my $elem ($html->getElementsByTagName("p")) {

这些变量也是中间变量,其中两个未使用。

my $tagname = $elem->tagName;
my $attr = $elem->attributes;
my $text = $elem->innerText;
push (@array,"$text");

另请注意,您永远不必以这种方式引用变量。你可以这样做:

push @array, $elem->innerText;

foreach $_  (@array) {

默认情况下使用$_变量,无需明确指定。

print $html_fh "$_\n";   
chomp ($_);        
push (@array1, "$_");

我不确定为什么你在打印之后chomp变量,但在将它存储在另一个数组之前,但它似乎没有意义对我来说。此外,该另一个数组将包含与另一个数组完全相同的元素,仅重复。

$end = $#array1+1;

这是另一个中间变量,也可以简化。 $# sigil将为您提供最后一个元素的索引,但是标量上下文中的数组本身将为您提供它的大小:

$end = @array1;   # size = last index + 1

但你可以一次性做到这一点:

print "Elements in the array: " . @array1 . "\n";

请注意,在此使用连接运算符.会强制执行数组上的标量上下文。如果您使用了逗号运算符,,它将具有列表上下文,并且该数组将已扩展为其元素列表。这是通过上下文进行操作的典型方法。

close $html_fh;

不需要显式关闭文件句柄,因为它会在脚本结束时自动关闭。

答案 1 :(得分:2)

如果您使用Web::Scraper代替,您的代码会变得更简单和清晰(只要您能够构建CSS选择器或XPath查询):

#!/usr/bin/env perl
use strict;
use warnings qw(all);

use URI;
use Web::Scraper;

my $result = scraper {
    process 'p',
        'paragraph[]' => 'text';
}->scrape(URI->new('http://www.perl.org/'));

for my $test (@{$result->{paragraph}}) {
    print "$test\n";
}

print "Elements in the array: " . (scalar @{$result->{paragraph}});

答案 2 :(得分:2)

以下是另一种获取<p>代码之间所有内容的方法,这次使用Mojo::DOM项目的Mojolicious部分。

#!/usr/bin/env perl

use strict;
use warnings;
use v5.10; # say

use Mojo::DOM;

my $html = <<'END';
<p>Paragraph 1</p>
<p>Paragraph 2</p>
<div>Should not find this</div>
<p>Paragraph 3</p>
END

my $dom = Mojo::DOM->new($html);
my @paragraphs = $dom->find('p')->pluck('text')->each;

say for @paragraphs;