Perl:网站抓取时出现意外行为

时间:2011-06-20 07:31:32

标签: perl www-mechanize

我正在使用WWW::MechanizeHTML::TokeParser来解析网站以获取更新。我无法在网站上提供任何详细信息,因为它需要登录。该网站基本上有一个数据表。我只是解析html直到我到达表的第一行,检查它是否是我最后一次刮的值,如果没有发送邮件。当我在现有表条目上测试它时,这非常有效,除非在实际更新发生时,刮擦不会停留在我的最后一次刮擦。它一直发送邮件,直到桌子耗尽,并无限期地重复。我无法弄清楚发生了什么。我知道没有任何人可以在没有网站的情况下进行验证,但我仍然会发布我的代码。我很欣赏可能出现问题的想法。

代码:

sub func{
    my ($comid, $mechlink) = @_;

    my $mechanize = WWW::Mechanize->new(
        noproxy  => 0,
        stack_depth => 5,
        autocheck => 1
    );

    $mechanize->proxy( https => undef );
    eval{
            my $me = $mechanize->get($mechlink);
            $me->is_success or die $me->status_line;
    };
    return $comid if ($@);  

    my $stream = HTML::TokeParser->new( \$mechanize->{content} ) or die $!;

    while ( $tag = $stream->get_tag('td') ) {
    if( $tag->[1]{class} eq 'dateStamp' ) {
        $dt = $stream->get_trimmed_text('/td');
        $tag = $stream->get_tag;
        $tag = $stream->get_tag;
        $name = $stream->get_trimmed_text('/td') if( $tag->[1]{class} eq 'Name' );
        return $comid unless( $tag->[1]{class} eq 'Name' );
        $tag = $stream->get_tag;
        $tag = $stream->get_tag;
        $tag = $stream->get_tag;
        $tag = $stream->get_tag;
        $info = $stream->get_trimmed_text('/td');
        print "$name?\n";
        return $retval if($info eq $comid);
        print "You've Got Mail! $info $comid\n";
        $tcount++;
        $retval = $info if($tcount == 1);
        $tag = $stream->get_tag;
        $tag = $stream->get_tag;
        $tag = $stream->get_tag;
        $link = "http://www.abc.com".$tag->[1]{href} if ($tag->[0] eq 'a' );
        my $outlook = new Mail::Outlook();
        my $message = $outlook->create();
        $message->To('abc@def.com');
        $message->Cc('abc@def.com;abc@def.com');
        my $hd = "$name - $info";  
        $message->Subject($hd);
        $message->Body(" ");
        $message->Attach($link);
        $message->send;
    }
}
}    

5 个答案:

答案 0 :(得分:6)

对于这类任务,我更喜欢使用HTML::TableExtract。它非常易于使用:

use HTML::TableExtract;
$te = HTML::TableExtract->new( headers => [qw(header1 header2)]);
$te->parse($html);
foreach $ts ($te->tables) {
    foreach $row ($ts->rows) {
        my ($field1, $field2) = @$row;
        # Your code here
    }
}

答案 1 :(得分:2)

有时,网站会有变化。我经常使用Web :: Scraper。可以使用XPath写入get元素。

use Web::Scraper;
use URI;

my $uri = URI->new("http://....");
my $entries = scraper {
    process 'id("content")/div[@class="section"]', 'news[]' => scraper {
        process 'h2', title => 'TEXT';
        process 'p', body => 'TEXT';
    };
};

# if you have instance of WWW::Mechanize, set like following.
# $entries->user_agent($mech);

my $res = $entries->scrape( $uri );
for my $entry (@{$res->{news}}) {
    # use $entry->title or $entry->body
}
# language: lang-perl

答案 2 :(得分:2)

当你匹配你想要的东西时,从while循环退出,否则它会一直循环。

 while ( $tag = $stream->get_tag('td') ) {
    if( $tag->[1]{class} eq 'dateStamp' ) {
        $dt = $stream->get_trimmed_text('/td');
                    ...
                    ... 
        last;
    }
}

答案 3 :(得分:1)

您将$comid传递给您的函数。在while循环中,首先设置$info,然后将其与$comid进行比较。如果两个值匹配,则退出该函数。如果不匹配,请发送电子邮件。

电子邮件发送完毕后,循环继续,并处理下一个标记。当您下次比较$info$comid时,我猜他们将会有所不同,因为您已转到下一个标记。因此,将发送另一封电子邮件。

我不知道这是否是预期的行为 - 您是打算为表中的每个更新发送一封电子邮件,还是只有一封电子邮件,如果表中有任何更新?如果您只需要发送一封电子邮件,无论有多少更新,只需在发送第一封电子邮件后退出循环 - 按照manu_v的建议。

我还会考虑重构您的代码以使其更强大 - 所有get_tag调用看起来都有点脆弱。查看其他答案以获取有关如何执行此操作的建议。

答案 4 :(得分:1)

听起来我觉得循环终止比使用TokeParser更具问题。听起来你的循环即使在你得到你正在寻找的值之后也会继续迭代。

您可能希望执行以下操作:

While($x) {

  .
  .
  .
  last if ($foundWhatINeeded)
}