2009-05-22 7 views
0

Je veux vérifier un site pour les liens, puis vérifier récursivement ces sites pour les liens. Mais je ne veux pas aller chercher la même page deux fois. J'ai des problèmes avec la logique. C'est le code Perl:Comment puis-je parcourir les liens de manière récursive sans devoir revoir les liens?

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

Mais cela ne semble pas réellement faire ce que je veux.

Aide ?!

EDIT: Je veux récupérer les URL du $starting_url, puis récupérer toutes les URL des extractions qui en résultent. Mais, si l'une des URL renvoie à $starting_url, je ne veux plus la récupérer.

+0

Il ne fait pas ce que vous voulez ... ok. Mais qu'est-ce que ça fait? – Zenshai

Répondre

2

Si vous avez une file d'attente de liens pour vérifier et que vous voulez ignorer les doublons, utilisez un hachage de noter ceux que vous avez déjà visités. Passer les liens qui sont dans ce hachage:

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

La situation est un peu plus compliquée avec les URL qui semblent un peu différentes, mais finissent à la même ressource, comme http://example.com, http://www.example.com, http://www.example.com/, et ainsi de suite. Si cela m'intéressait, j'ajouterais une étape de normalisation en créant un objet URI pour chacun puis retirer l'URL en tant que chaîne. Si c'était un plus gros problème, je regarderais aussi l'URL que les en-têtes de réponse prétendaient avoir (disons, par redirection, etc.) et marquerais que j'avais vu ceux-là aussi.

9

La chose la plus simple à faire serait de ne pas réinventer la roue et d'utiliser the CPAN.

0

Si vous voulez extraire tous les liens d'une page, je vous recommande d'utiliser LinkExtor par Gisle Aas, et une recherche rapide CPAN vous le montrera. Vous pouvez ensuite parcourir de manière récursive les liens trouvés en les poussant sur une liste, et en les faisant disparaître, en vérifiant d'abord avant de les traverser si vous les avez déjà visités, en utilisant le hash comme vous l'avez fait.

2

Je suppose que le problème est que

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

n'est pas récurrent dans la façon dont vous pensez qu'il est. Pour chaque URL que vous récupérez, vous devez appeler récursivement votre fonction une fois de plus, ce qui est très inefficace pour la mémoire.

Bien que vous écrivez un programme pour « récursive » crawl pages web, dans votre code vous devez utiliser itération, non récursivité:

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

Bien sûr, comme d'autres l'ont noté, il existe d'autres outils Cela peut automatiser cela pour vous.

0

peut-être que cela peut vous aider: blog.0x53a.de/where-do-my-links-go/ Il fait une première recherche à partir d'un site Web donné. Aussi le module HTML :: LinkExtractor utilisé peut être intéressant pour vous.

Cordialement, Manuel

Questions connexes