2

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

<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 我感兴趣的行。有人可以帮我解决这个问题吗?

4

3 回答 3

3

如果您改用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}});
于 2012-12-09T14:11:45.103 回答
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,但在将其存储在另一个数组中之前,为什么要对变量进行ing ,但这对我来说似乎没有意义。此外,这个另一个数组将包含与另一个数组完全相同的元素,只是重复。

$end = $#array1+1;

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

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

但是你可以一口气做到这一点:

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

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

close $html_fh;

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

于 2012-12-09T08:12:36.073 回答
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;
于 2012-12-09T16:01:03.313 回答