2012-12-09 44 views
2

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

<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,很多数组元素只是重复句子。我怎样才能避免这种重复?是否有其他有效的方式来表达我感兴趣的线条。有人可以帮我解决这个问题吗?

回答

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; 

这是另一个中间变量,也可以简化。该$#印记会给你最后一个元素的索引,但阵列本身在标量上下文会给你它的大小:

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

但你可以在一个去做到这一点:

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

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

close $html_fh; 

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

+0

非常感谢...我会尝试修复我的代码中的错误。 – user128956

+0

@ user128956如果您觉得这回答了您的问题,您可以通过单击复选标记接受答案。 – TLP

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

这里是另一种方式来获得所有内容之间<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;