【问题标题】:Perl : Unexpected behavior with website scrapingPerl:网站抓取的意外行为
【发布时间】:2011-09-18 11:32:40
【问题描述】:

我正在使用 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;
    }
}
}    

【问题讨论】:

  • 您能否包含您的 while 循环的代码 - 您检查更新的位。这很可能是出了问题的地方。
  • 我建议您添加应用程序日志记录,以便您可以从日志中查看更多信息。此外,邮件发送计数器和受控停止也很好。我已经从这样的程序发送了好几次 100 封电子邮件,我知道这有多烦人。
  • 我在循环中添加了代码。邮件发送计数器的问题在于,首先,我不知道从一次抓取到抓取多少次更新。其次,即使我设置了限制,我也会每 60 秒运行一次相同的脚本,所以下一次迭代开始重新发送邮件

标签: perl www-mechanize


【解决方案1】:

在我看来,这更多是循环终止的问题,而不是 TokeParser 的问题。听起来您的循环即使在您获得所需的值之后仍在继续迭代。

您可能想要执行以下操作:

While($x) {

  .
  .
  .
  last if ($foundWhatINeeded)
}

【讨论】:

    【解决方案2】:

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

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

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

    我还会考虑重构您的代码,使其更加健壮 - 所有get_tag 调用似乎都有些脆弱。查看其他答案以获取有关如何执行此操作的建议。

    【讨论】:

    • 如果我正确理解了您的代码,请将表中的值 ($info) 与传递给您的函数的值 ($comid) 进行比较。如果表中的值已更新,您需要记住该新值,以便在下次扫描时与它进行比较。如果您不这样做,您将始终将表值 ($info) 与旧值 ($comid) 进行比较 - 它总是过时的,因此将发送电子邮件。我看不到您存储更新值的位置或方式。你只返回$comid,这是你首先传递给函数的值。
    • 如果您可以发布您正在扫描的表格的 HTML,它也可能会有所帮助。如果数据敏感,您可以将实际值替换为虚拟值。
    • 好的。我会尝试发布html。我将$retval 分配给该特定刮擦的最顶部条目,当您到达先前迭代的最顶部刮擦时,将返回$retval$comid 接收 this 作为函数的返回值并传递给下一次迭代
    • 我要么使用调试器逐步执行代码,要么添加大量打印语句,以确保返回正确的值并且您正在比较正确的值。
    • 当我对现有条目进行测试时,该代码运行良好。但是在更新发生的那一刻,一些奇怪的事情正在发生。我将使用描述的其他一些方法并尝试。感谢您的帮助
    【解决方案3】:

    当你匹配到你要找的东西时退出while循环,否则它会继续循环。

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

    【讨论】:

      【解决方案4】:

      有时,网站会发生变化。我经常使用 Web::Scraper。可以通过 XPath 编写获取元素。

      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
      

      【讨论】:

        【解决方案5】:

        对于这类任务,我更喜欢使用 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
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-04-10
          • 1970-01-01
          • 2012-09-04
          • 2020-07-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多