2011-06-20 38 views
3

我使用WWW::MechanizeHTML::TokeParser解析網站的更新。我不能在網站上提供任何細節,因爲它需要登錄。該網站基本上有一個數據表。我只是簡單地解析html直到我到達表的第一行,檢查它是否是我最後一次刮的值,如果不發送郵件。當我在現有的表格條目上進行測試時,這非常有效,除非發生實際更新時,刮擦並不會停留在最後的刮擦處。它會一直髮送郵件,直到表格耗盡並無限期重複。我無法弄清楚發生了什麼事。我知道沒有任何人可以在沒有網站的情況下進行驗證,但無論如何我都會發布我的代碼。我會很欣賞可能出錯的想法。Perl:意外的行爲與網站刮

代碼:

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 ([email protected]); 

    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('[email protected]'); 
     $message->Cc('[email protected];[email protected]'); 
     my $hd = "$name - $info"; 
     $message->Subject($hd); 
     $message->Body(" "); 
     $message->Attach($link); 
     $message->send; 
    } 
} 
}  
+0

你可以包括你的while循環的代碼 - 在你檢查更新位。這可能是事情出錯的地方。 – Mike

+0

我會建議您添加應用程序日誌記錄,以便您可以從日誌中檢查更多內容。此外,郵件發送計數器和受控制的停止也是好的。我已經從這樣的程序發送了數百次的電子郵件,我知道這可能是多麼令人討厭。 – weismat

+0

我在循環中添加了代碼。郵件發送計數器的問題是,首先,我不知道有多少更新可以從刮擦到刮擦。其次,即使我做了限制,我每60秒運行一次腳本,所以下一次迭代開始重新發送郵件 – Aks

回答

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 
    } 
} 
2

有時,網站有變化。我經常使用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 
2

當你匹配你所尋找的東西時,退出while循環,否則它會一直循環。

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

實際上...當我到達最後一次刮擦時它會返回... – Aks

1

您將$comid傳遞給您的函數。在您的while循環中,首先設置$info,然後將其與$comid進行比較。如果兩個值匹配,則退出該功能。如果它們不匹配,則發送電子郵件。

一旦電子郵件已發送,循環繼續,並處理下一個標記。當你下一次比較$info$comid時,我想他們會有所不同,因爲你已經轉向下一個標籤。因此會發送另一封電子郵件。

我不知道這是否是預期行爲 - 您是打算爲表中的每個更新發送一封電子郵件,還是隻有一封電子郵件,如果表中有任何更新?如果您只需發送一封電子郵件,無論有多少次更新,只要在發送第一封電子郵件後退出循環 - 正如manu_v所建議的那樣。

我也會考慮重構你的代碼,使它更強大 - 所有的get_tag調用看起來有點脆弱。查看其他答案,瞭解如何執行此操作的建議。

+0

它的每次更新一封電子郵件。謝謝 – Aks

+1

如果我正確理解了你的代碼,你可以比較表('$ info')中的值和傳​​遞給你的函數('$ comid')的值。如果表中的值已更新,則需要記住該新值,以便您可以在下次掃描時與其進行比較。如果你不這樣做,你會一直在比較表值('$ info')和舊值('$ comid') - 它總是過期,因此電子郵件將被髮送。我看不到存儲更新值的位置或方式。您只返回'$ comid',這是您首先傳遞給函數的值。 – Mike

+0

如果您可以發佈正在掃描的表格的HTML,它也可能有所幫助。如果數據敏感,您可以用虛擬值替換實際值。 – Mike

1

聽起來,這是循環終止比使用TokeParser更多的問題。這聽起來像你的循環繼續迭代,即使你得到你正在尋找的價值。

您可能需要做這樣的事情:

While($x) { 

    . 
    . 
    . 
    last if ($foundWhatINeeded) 
}