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