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