Как я могу рекурсивно посещать ссылки без повторного просмотра ссылок?

Я хочу проверить сайт на наличие ссылок, а затем рекурсивно проверить эти сайты на наличие ссылок. Но я не хочу получать одну и ту же страницу дважды. У меня проблемы с логикой. Это код 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);
    }
}

Но, похоже, это не то, что я хочу.

Помогите?!

РЕДАКТИРОВАТЬ: я хочу получить URL-адреса из $starting_url, а затем получить все URL-адреса из полученных выборок. Но если один из URL-адресов ссылается на $starting_urlЯ не хочу получать это снова.

5 ответов

Решение

Если у вас есть очередь ссылок для проверки и вы хотите пропустить дубликаты, используйте хэш, чтобы отметить те, которые вы уже посетили. Пропустите ссылки, которые находятся в этом хэше:

my @need_to_check = (...); # как бы вы ни составляли этот список
мой %ready_checked = ();

while (моя $link = shift @need_to_check)
    {
    следующий, если существует $ уже_проверенный {$link};
    ...;
    $ Already_checked{$ ссылку}++;
    }

Ситуация несколько сложнее с URL-адресами, которые выглядят немного по-другому, но в конечном итоге находятся на одном и том же ресурсе, например, http://example.com/, http://www.example.com/, http://www.example.com/, и так далее. Если бы я заботился об этом, я бы добавил шаг нормализации, создав для каждого объект URI, а затем снова вытащил URL-адрес в виде строки. Если бы это была более серьезная проблема, я бы также посмотрел URL-адрес, который, как утверждали заголовки ответа, я получил (например, с помощью перенаправления и т. Д.) И отметил, что я их тоже видел.

Проще всего было бы не изобретать велосипед и использовать CPAN.

Я предполагаю, что проблема в том, что

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

Конечно, как отмечали другие авторы, есть и другие инструменты, которые могут автоматизировать это для вас.

Может быть, это поможет вам: blog.0x53a.de/where-do-my-links-go/ Выполняет поиск в ширину, начиная с заданного веб-сайта. Также вам может быть интересен используемый модуль HTML::LinkExtractor.

С уважением, Мануэль

Если вы хотите извлечь все ссылки со страницы, я рекомендую использовать LinkExtor от Gisle Aas, и быстрый поиск CPAN покажет вам это. Затем вы можете рекурсивно проходить по найденным ссылкам, помещая их в список и выталкивая их, сначала проверяя, прежде чем переходить по ним, если вы уже посетили их, используя хеш, как вы это сделали.

Другие вопросы по тегам