如何在不重新访问链接的情况下递归访问链接?

时间:2009-05-22 19:04:04

标签: perl recursion

我想检查一个站点的链接,然后递归检查这些站点的链接。但我不想两次获取同一页面。我遇到了逻辑问题。这是Perl代码:

my %urls_to_check = ();
my %checked_urls = ();

&fetch_and_parse($starting_url);

use Data::Dumper; die Dumper(\%checked_urls, \%urls_to_check);

sub fetch_and_parse {
    my ($url) = @_;

    if ($checked_urls{$url} > 1) { return 0; }
    warn "Fetching 'me' links from $url";

    my $p = HTML::TreeBuilder->new;

    my $req = HTTP::Request->new(GET => $url);
    my $res = $ua->request($req, sub { $p->parse($_[0])});
    $p->eof();

    my $base = $res->base;

    my @tags = $p->look_down(
        "_tag", "a",
    );

    foreach my $e (@tags) {
        my $full = url($e->attr('href'), $base)->abs;
        $urls_to_check{$full} = 1 if (!defined($checked_urls{$full}));
    }

    foreach my $url (keys %urls_to_check) {
        delete $urls_to_check{$url};
        $checked_urls{$url}++;
        &fetch_and_parse($url);
    }
}

但这似乎并没有真正做到我想要的。

帮助?!

编辑:我想要从$starting_url获取网址,然后从结果提取中获取所有网址。但是,如果其中一个网址链接回$starting_url,我不想再次抓取它。

5 个答案:

答案 0 :(得分:9)

最简单的做法是不重新发明轮子并使用the CPAN

答案 1 :(得分:2)

我猜这个问题是

foreach my $url (keys %urls_to_check) {...}

不会以您认为的方式重复出现。对于您恢复的每个URL,您必须再次递归调用您的函数,这是非常低效的内存。

虽然您正在编写一个“递归”抓取网页的程序,但在您的代码中,您需要使用迭代,而不是递归:

sub fetch_and_parse {
    my ($url) = @_;
    $urls_to_check{$url} = 1;
    while(%urls_to_check) {
        // Grab a URL and process it, putting any new URLs you find into urls_to_check
    }
  }

当然,正如其他海报所述,还有其他工具可以为您自动化。

答案 2 :(得分:2)

如果您有要检查的链接队列并且想要跳过重复项,请使用哈希来记录您已访问过的哈希值。跳过该哈希中的链接:

my @need_to_check   = ( ... ); # however you make that list
my %already_checked = ();

while( my $link = shift @need_to_check )
    {
    next if exists $already_checked{$link};
    ...;
    $already_checked{$link}++;
    }

情况稍微复杂一些,网址看起来略有不同但最终位于同一资源,例如http://example.comhttp://www.example.comhttp://www.example.com/等。如果我关心这些,我会通过为每个创建一个URI对象来添加一个规范化步骤,然后再将该URL作为字符串拉出。如果这是一个更大的问题,我还会查看响应标头声称我获得的URL(例如,通过重定向等),并标记我也看到了这些。

答案 3 :(得分:0)

如果您想从页面中提取所有链接,我建议使用Gisle Aas的LinkExtor,快速的CPAN搜索会向您显示。然后,您可以通过将它们推送到列表上来递归遍历找到的链接,然后将它们弹出,在遍历它们之前首先检查它们(如果您已经访问过它们),使用哈希就像您一样。

答案 4 :(得分:0)

也许这可以帮到你:blog.0x53a.de/where-do-my-links-go/ 它从给定的网站开始进行广度优先搜索。使用的模块HTML :: LinkExtractor也可能对您有用。

此致 曼努埃尔