Problème avec le module TreeBuilder

Fermé
wobot - Modifié le 7 juin 2020 à 05:01
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 - 29 juil. 2020 à 19:36
Bonjour,
je tente de parcourir, via PERL, des pages du site IMDB (les films d'un acteur donné plus particulièrement) en utilisant le module Mechanize et ensuite extraire leurs textes bruts (le genre des films) avec HTML::TreeBuilder et ensuite en faire un tableau d'hachage.

Mon code compile mais le dernier print n'affiche rien donc je pense qu'il y a un problème concernant le tableau d'hashage (%index) ou la façon dont j'ai utilisé TreeBuilder. Quelqu'un pourrait-il m'aider? :)

use LWP::Simple;
use PerlIO::locale;
use WWW::Mechanize::Link;
use HTML::TreeBuilder;
use WWW::Mechanize;
binmode STDOUT, ':locale';
use strict;
use warnings;

print "Entrez le nom du premier acteur: ";
my $acteur1 = <STDIN>;
print "Nous allons analyser la filmographie de l'acteur $acteur1 en fonction du genre\n";

my $lien1 = "https://www.imdb.com/find?s=nm&q=$acteur1";
my $mech = WWW::Mechanize->new();
$mech->get($lien1);
$mech->follow_link( url_regex => qr/nm0/i );
my @url_links= $mech->find_all_links( url_regex => qr/title\/tt/i );
my $nb_links = @url_links;

my $tree = HTML::TreeBuilder->new();
my %index;
my $mech1 = WWW::Mechanize::Link->new();

my @genres = ();
for ( my $i = 1 ; $i <= $nb_links ; $i++ ) {
$mech1-> url($url_links[$i]);
$tree->parse($mech1);
@genres = $tree->look_down (
'class', 'see-more inline canwrap',
sub {
my $link = $_[0]->look_down('_tag','a');
return 1 if $link->attr('href') =~ m{genres=};
return 0;
}
);
}

foreach my $e (@genres){
my $genre = $e->as_text;
$index{$genre}++;
}

$tree->delete;

print "Les genres de films que $acteur1 a faits (en pourcentage)\n";
foreach my $cle (sort{$index{$b} <=> $index{$a}} keys %index){
print "($index{$cle}/$nb_links) de sa filmographie a comme genre: $cle\n";
}

2 réponses

Up, please :'(
J'suis en train d'avancer sur mon problème et il semble clairement se situer dans le foreach de @genres...
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
Modifié le 8 juin 2020 à 09:41
Salut wobot,

Peux-tu nous donner un exemple de ce qu'il y a dans le hash
%index
et de ce que tu veux afficher par rapport à cet exemple dans ton dernier print ?


Dal

N.B. : quand tu postes du code n'oublie pas de spécifier le langage Perl dans les balises code en cliquant sur la flèche basse à droite de l'icône code. Cela fera la coloration syntaxique, préservera l'indentation et numérotera tes lignes.
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
8 juin 2020 à 19:02
ou alors, donnes nous un exemple de ce que tu as dans le tableau
@genres
, afin qu'on puisse reproduire ton problème (utilise Dumper dessus).
0
wobot1340 Messages postés 6 Date d'inscription lundi 6 juillet 2020 Statut Membre Dernière intervention 15 août 2020
6 juil. 2020 à 23:10
Salut Dal,
voici un exemple d'un tableau d'hash (sauf qu'à la place de mots d'un texte, j'aimerai la liste de tous les genres cinématographiques apparus dans la filmo de l'acteur ainsi que leur fréquence) et la deuxième image c''est ce que contient @genres dans mon code actuellement.


Voici une meilleure mise en forme de mon code
use LWP::Simple;
use PerlIO::locale;
use WWW::Mechanize::Link;
use HTML::TreeBuilder;
use WWW::Mechanize;
binmode STDOUT, ':locale';
use strict;
use warnings;

print "Entrez le nom du premier acteur: ";
my $acteur1 = <STDIN>;  
print "Nous allons analyser la filmographie de l'acteur $acteur1 par genre\n";


my $lien1 = "https://www.imdb.com/find?s=nm&q=$acteur1";
my $mech = WWW::Mechanize->new();
$mech->get($lien1); 
$mech->follow_link( url_regex => qr/nm0/i ); 
my @url_links= $mech->find_all_links( url_regex => qr/title\/tt/i ); 
my $nb_links = @url_links; 

my $tree = HTML::TreeBuilder->new(); 
my %index; 
my $mech1 = WWW::Mechanize::Link->new(); 

my @genres = (); 
for ( my $i = 1 ; $i <= $nb_links ; $i++ ) { 
    $mech1-> url_abs($url_links[$i]); 
	$tree->parse($mech1); 
	@genres = $tree->look_down ('class', 'see-more inline canwrap', 
	sub {
	    my $link = $_[$i]->look_down('_tag','a'); 
	    $_[$i] if $link->attr('href') =~ m{genres}; 
    }
	);
}	

my @genres1 = ();
foreach my $e (@genres){
    my $genre = $e->as_text;
	@genres1 = split(/[à| ]/,$genre);
}

foreach my $e (@genres1){
    if ($e ne ("Genres:" or "") ) {
	    $index{$e}++;
	}
}

$tree->delete;

foreach my $cle (sort{$index{$b} <=> $index{$a}} keys %index){
    print "$cle : $index{$cle}\n";
}


Merci d'avance pour ton aide! :)
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083 > wobot1340 Messages postés 6 Date d'inscription lundi 6 juillet 2020 Statut Membre Dernière intervention 15 août 2020
8 juil. 2020 à 11:05
dans ton code :
  • en ligne 17, tu récupères le résultat de l'interrogation
  • en ligne 18, tu suis le lien du premier résultat
  • en ligne 19, tu récupères les liens vers les titres de la filmographie sous la forme d'un tableau de tableaux


après, je ne comprends pas ce que tu fais, ni comment tu obtiens ce que tu montres en capture ...

Tu utilises HTML::TreeBuilder mais tu lui donnes à parser un tableau au lieu du contenu d'une page html (vérifie avec Dumper).

Autrement, as-tu l'autorisation d'IMDB d'utiliser leur site de cette façon ?

https://forums.commentcamarche.net/forum/affich-36690314-la-fonction-follow-link#8

Dal
0
wobot1340 Messages postés 6 Date d'inscription lundi 6 juillet 2020 Statut Membre Dernière intervention 15 août 2020 > [Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024
11 juil. 2020 à 01:19
Je t'envoie le code avec des commentaires pour que ce soit plus compréhensible.

Je parse pas du tout un tableau car je l'explore d'abord (ligne 26) avant de parser le contenu du lien donné (lignes 29 et 30).

Et oui, IMDB donne bien accès à son site de cette façon, sauf qu'on a droit d'exploiter qu'à une partie du code source.

use LWP::Simple;
use PerlIO::locale;
use HTML::TreeBuilder;
use WWW::Mechanize;
binmode STDOUT, ':locale';
use strict;
use warnings;


print "Entrez le nom du premier acteur: ";
my $acteur1 = <STDIN>;  #l'utilisateur entre le nom de l'acteur
print "Nous allons analyser la filmographie de l'acteur $acteur1 par genre\n";

#on mets le lien avec l'acteur donné en variable Mechanize afin de parcourir les liens internet
my $lien1 = "https://www.imdb.com/find?s=nm&q=$acteur1";
my $mech = WWW::Mechanize->new();
$mech->get($lien1); #on accède à la page de recherche avec la fonction get
$mech->follow_link( url_regex => qr/nm0/i ); #on accède au premier résultat grace à la fonction follow_link et à l'expression régulière nm0 qui est dans l'URL
my @url_links= $mech->find_all_links( url_regex => qr/title\/tt/i ); #on insère dans un tableau tous les liens ayant comme expression régulière "title" dans leur URL 
my $nb_links = @url_links; #on enregistre le nombre de liens qu'il y a dans la liste dans cette variable

my $tree = HTML::TreeBuilder->new(); #on crée le module TreeBuilder pour accéder à un texte précis de la page via les balises
my %index; #on crée un tableau d'haschage

my @genres = (); #on crée la liste genres pour insérer tous les genres rencontrés
foreach my $e (@url_links) { #on fait une boucle pour parcourir tous les liens enregistrés
    my $html = $e->url(); #on prend l'url du lien
	my $html1 = get("https://www.imdb.com$html"); #on complète le lien
	my $htm11 = $mech->content; #on prend le contenu de la page
	$tree->parse($htm11); #on accède à l'url et on utilise l'arbre pour trouver les chaines de caractères qui nous intéressent
	my $tgenre = $tree->look_down ('class', 'see-more inline canwrap', #On a comme critère que class="see-more.." (selon le code source) pour que le mot soit intégré dans la liste @genres
	sub {
        my $link = $_[0]->look_down('_tag','a'); #2ème condition: balises <a>
	    return if $link->attr('href') =~ m{genres}; #on retourne le(s) mot(s) entre les balises si 3ème condition: le mot "genres" doit être dans l'URL
    }
    );
    push(@genres,$tgenre);	#on ajoute le mot, qui s'avère donc être le genre, dans la liste des genres
    }	

$tree->delete; #on supprime l'arbre comme on en a plus besoin

my @genres1 = (); #on crée une nouvelle liste pour filtrer les mots trouvés (les genres de films)
foreach my $e (@genres){ #on crée une boucle pour parcourir la liste
    my $genre = $e->as_text;  #le texte de l'élément de la liste est inséré dans la variable
	@genres1 = split(/[à| ]/,$genre); #on enlève les caractères inutiles que sont les espaces, "à" et "|" qui permettent de garder que les termes de genre ciné
}

foreach my $e (@genres1){ #une autre boucle pour filtrer les erreurs de listing ("Genres:" etc..) et ajouter les mots corrects dans le tableau d'hachage
    if ($e ne ("Genres:" or "") ) {
	    $index{$e}++;
	}
}

foreach my $cle (sort{$index{$b} <=> $index{$a}} keys %index){
    print "$cle : $index{$cle}\n"; #on affiche le tableau d'hachage avec les genres et le nombre de fois qui apparaissent dans la filmographie de l'acteur donné
}
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083 > wobot1340 Messages postés 6 Date d'inscription lundi 6 juillet 2020 Statut Membre Dernière intervention 15 août 2020
13 juil. 2020 à 17:57
Ton code ci-dessus est différent de celui auquel je réagissais, et passerait effectivement une chaîne contenant l'intégralité du code html de la page à
parse
en ligne 30 si en ligne 28 tu assignais à
$html1
le résultat de la méthode
get()
de l'objet
$mech
.

Même comme cela, ton code échoue et ce que tu fais en lignes 31 à 35 ne renvoie pas les "genres" selon mes tests.

A supposer que tu débogues ton code (je ne vais pas le faire pour toi car ce code est hors sujet si tu es autorisé à utiliser la base de données d'IMDb de façon automatisée) :
  • les pages de IMDb comprennent, sur certains résultats de recherche, des liens mis en avant à titre publicitaire matchant la regexp
    /title\/tt/
    , mais sans rapport avec la recherche
  • IMDb contient des liens vers une même URL en double, triple ou quadruple, selon la raison pour laquelle une personne est créditée


Si tu es autorisé à utiliser la base de données d'IMDb de façon automatisée, cela signifie que tu as une licence d'accès à leur API et que tu n'as pas à faire ce type de bricolages à la merci d'un changement d'interface Web.

Utilise donc leur API.
0