Problème avec le module TreeBuilder

Signaler
-
Messages postés
5407
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
24 septembre 2020
-
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...
Messages postés
5407
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
24 septembre 2020
898
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.
Messages postés
6
Date d'inscription
lundi 6 juillet 2020
Statut
Membre
Dernière intervention
15 août 2020
>
Messages postés
5407
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
24 septembre 2020

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é
}
Messages postés
5407
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
24 septembre 2020
898 >
Messages postés
6
Date d'inscription
lundi 6 juillet 2020
Statut
Membre
Dernière intervention
15 août 2020

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.
Messages postés
6
Date d'inscription
lundi 6 juillet 2020
Statut
Membre
Dernière intervention
15 août 2020
>
Messages postés
5407
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
24 septembre 2020

Ah, on s'est mal compris, j'ai pas de licence d'accès à l'API. En fait, via l'invite de commande, j'ai accès à une (petite) partie du code source des pages IMDB (d'où le fait que j'étais limité sur la regexp à utiliser).
Messages postés
6
Date d'inscription
lundi 6 juillet 2020
Statut
Membre
Dernière intervention
15 août 2020
>
Messages postés
5407
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
24 septembre 2020

Sinon, je reçois ça comme erreur quand je compile le code et je ne comprends pas:
"Can't call method "attr" on an undefined value"
A la ligne 34
Messages postés
5407
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
24 septembre 2020
898 >
Messages postés
6
Date d'inscription
lundi 6 juillet 2020
Statut
Membre
Dernière intervention
15 août 2020

Ce message est renvoyé lorsque l'on tente d'appeler une méthode sur quelque chose qui n'est pas un objet et qui a une valeur "indéfinie". Ta variable
$link
ne contient pas l'objet que tu veux utiliser,... elle ne contient rien.