Boîte à outils 1
Extraction du contenu textuel
Objectif
L'objectif de cette première boîte à outils est de parcourir toute l'arborescence du fil RSS et d'extraire le contenu textuel des balises <title> et <description>.
Pour cela, nous avons utilisé deux méthodes :
- la première consiste à utiliser des expressions régulières,
- la deuxième repose sur la structuration logique du texte et des balises types d'une arborescence RSS, elle utilise la bibliothèque Perl XML::RSS.
Méthode 1 : expressions régulières
#/usr/bin/perl
<<DOC;
Nom -- Elise LINCKER
Date -- MARS 2021
Traitement -- parcours d une arborescence de fichiers (fil RSS)
extraction du contenu textuel des balises TITLE et DESCRIPTION avec les expressions régulières
nettoyage du contenu textuel
Utilisation du programme -- perl bao1_regexp.pl REPERTOIRE_A_PARCOURIR RUBRIQUE_A_TRAITER
Entrée -- le nom du répertoire-racine contenant les fichiers à traiter
et le nom de la rubrique à traiter parmi ces fichiers
Sortie -- un fichier au format txt
un fichier au format xml
DOC
#-----------------------------------------------------------
use strict;
use utf8;
use Timer::Simple;
# on instancie un timer commencant à 0.0s par défaut
my $t = Timer::Simple->new();
# on lance le timer
$t->start;
#-----------------------------------------------------------
# on récupère le nom du répertoire racine de l'arborescence à traiter
my $rep="$ARGV[0]";
# on récupère le nom de la rubrique à traiter
my $rubrique ="$ARGV[1]";
# on s'assure que le nom du répertoire ne se termine pas par un "/", sinon on enlève le "/"
$rep=~ s/[\/]$//;
# déclaration-initialisation des fichiers de sortie
open my $OUT,">:encoding(utf8)","BAO1_regexp_sortie_$rubrique.txt";
open my $OUTXML,">:encoding(utf8)","BAO1_regexp_sortieXML_$rubrique.xml";
# dans le fichier XML, écritute de l'en-tête XML et ouverture de la balise racine
print $OUTXML "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
print $OUTXML "<articles rubrique=\"$rubrique\">\n";
# initialisation de compteurs et d'une table de hachage
my %dico_des_titres=();
my $numberItem=0;
my $nbFile=0;
#----------------------------------------
# on déclenche la procédure parcoursarborescencefichiers (récursivité !) en prenant
# comme argument le nom du répertoire racine par lequel on doit commencer le parcours
&parcoursarborescencefichiers($rep);
#----------------------------------------
# dans le fichier XML, fermeture de la balise racine
print $OUTXML "</articles>\n";
# fermeture des fichiers de sortie
close $OUT;
close $OUTXML;
print "Nb item : $numberItem \n";
# affichage du temps écoulé depuis le lancement du programme
print "Time so far: ", $t->elapsed, " seconds\n";
# fin de l'exécution du programme
exit;
#----------------------------------------------
# procédure parcoursarborescencefichiers
# on accède au contenu du dossier dans lequel on se trouve
sub parcoursarborescencefichiers {
# on récupère l'argument passé à la procédure (le nom de dossier)
my $path = shift(@_);
# ouverture du répertoire avec la fonction perl opendir :
# elle prend comme arguments : le nom du répertoire à ouvrir
# + un descripteur, qui va être à partir de maintenant le moyen d'accéder au répertoire qu'on a ouvert
opendir(my $DIRhandle, $path) or die "can't open $path: $!\n";
# lecture du répertoire avec la fonction perl readdir :
# elle prend comme argument le descripteur du répertoire qu'on vient d'ouvrir
# et renvoie comme valeur la liste des fichiers contenus dans le répertoire
# on stocke cette liste dans @files
my @files = readdir($DIRhandle);
# fermeture du répertoire
closedir($DIRhandle);
# pour chaque élément contenu dans le répertoire (ficher ou répertoire)
foreach my $file (@files) {
# on passe au fichier suivant s'il s'agit d'un répertoire caché
next if $file =~ /^\.\.?$/;
# reconstruction du chemin relatif de l'élément traité $file
$file = $path."/".$file;
# "." est un opérateur de concaténation : "2020"."/"."01" => "2020/01"
# on teste le statut du fichier avec les opérateurs -d et - f
# -d ---> true si $file est un répertoire
if (-d $file) {
# on relance la procédure parcoursarborescencefichiers sur le répertoire $file
&parcoursarborescencefichiers($file); # recursivité !
}
# -f ---> true si $file est un fichier
if (-f $file) {
# on veut récupérer des fichiers XML RSS et une seule rubrique en particuler :
# si le nom du fichier correspond au nom de la RUBRIQUE qu'on veut traiter
# ET se termine par l'EXTENSION .xml ---> ON TRAITE LE FICHIER !
if ($file =~/$rubrique.+xml$/) {
# incrémentation du compteur de fichiers + message dans le terminal du fichier traité
print $nbFile++," Traitement de : ",$file,"\n";
# **************** TRAITEMENT DE LA BAO 1 : PERL REGEPX ****************
# **************** EXTRACTION DU CONTENU TEXTUEL ****************
# ouverture et lecture du fichier jusqu'à la fin en une seule fois
open my $fic,"<:encoding(utf8)",$file;
$/=undef; #ou $\=""
my $textelu=<$fic>;
# fermeture du fichier
close $fic;
# boucle qui extrait les titres et descriptions à l'aide des expressions régulières
while ($textelu=~/<item>.*?<title>(.+?)<\/title>.+?<description>(.+?)<\/description>/sg) {
# initialisation des variables $titre et $description pour traiter les titres et descriptions séparément
my $titre=$1;
my $description=$2;
# incrémentation du compteur d'items
$numberItem++;
# appel de la procédure de nettoyage
($titre,$description)=&nettoyage($titre,$description);
# si le titre n'existe pas dans la table de hachage $dico_des_titres,
if (!(exists $dico_des_titres{$titre})) {
# alors ce n'est pas un doublon : on ajoute le titre et sa description à la table de hachage
$dico_des_titres{$titre}=$description ;
# Ecriture des résultats dans les fichiers de sortie
print $OUT "$titre\n";
print $OUT "$description\n";
print $OUT "--------------------\n";
print $OUTXML "\t<item>\n";
print $OUTXML "\t\t<titre>$titre</titre>\n";
print $OUTXML "\t\t<description>$description</description>\n";
print $OUTXML "\t</item>\n";
}
# si le titre est déjà dans $dico_des_titres, on ne fait rien
}
}
}
}
}
#----------------------------------------------
# procédure nettoyage
sub nettoyage {
#my $titre=shift(@_); autre solution en vidant la liste des arguments du programmes...
#my $description=shift(@_);
my $titre = $_[0];
my $description = $_[1];
$titre=~s/^<!\[CDATA\[//;
$titre=~s/\]\]>$//;
$description=~s/^<!\[CDATA\[//;
$description=~s/\]\]>$//;
$description=~s/<.+?>//g;
$description=~s/'/'/g;
$description=~s/"/"/g;
$titre=~s/<.+?>//g;
$titre=~s/'/'/g;
$titre=~s/"/"/g;
$titre=~s/$/\./g;
$titre=~s/\.+$/\./g;
return $titre,$description;
}
Méthode 2 : bibliothèque XML::RSS
#/usr/bin/perl
<<DOC;
Nom -- Elise LINCKER
Date -- MARS 2021
Traitement -- parcours d une arborescence de fichiers (fil RSS)
extraction du contenu textuel des balises TITLE et DESCRIPTION avec la bibliothèque perl XML::RSS
nettoyage du contenu textuel
Utilisation du programme -- perl bao1_xmlrss.pl REPERTOIRE_A_PARCOURIR RUBRIQUE_A_TRAITER
Entrée -- le nom du répertoire-racine contenant les fichiers à traiter
et le nom de la rubrique à traiter parmi ces fichiers
Sortie -- un fichier au format txt
un fichier au format xml
DOC
#-----------------------------------------------------------
use strict;
use utf8;
# appel de la bibliothèque XML::RSS
use XML::RSS;
use Timer::Simple;
# on instancie un timer commencant à 0.0s par défaut
my $t = Timer::Simple->new();
# on lance le timer
$t->start;
#-----------------------------------------------------------
# utilisation de la bibliothèque : création de l'objet XML::RSS
my $rss=new XML::RSS;
# on récupère dans la variable $rep le nom du répertoire racine de l'arborescence à traiter
my $rep="$ARGV[0]";
# on récupère dans la variable $rubrique le nom de la rubrique à traiter
my $rubrique ="$ARGV[1]";
# on s'assure que le nom du répertoire ne se termine pas par un "/", sinon on enlève le "/"
$rep=~ s/[\/]$//;
# déclaration-initialisation des fichiers de sortie
open my $OUT,">:encoding(utf8)","BAO1_xmlrss_sortie_$rubrique.txt";
open my $OUTXML,">:encoding(utf8)","BAO1_xmlrss_sortieXML_$rubrique.xml";
# dans le fichier XML, écritute de l'en-tête XML et ouverture de la balise racine
print $OUTXML "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
print $OUTXML "<articles rubrique=\"$rubrique\">\n";
# initialisation de compteurs et d'une table de hachage
my %dico_des_titres=();
my $numberItem=0;
my $nbFile=0;
#----------------------------------------
# on déclenche la procédure parcoursarborescencefichiers (récursivité !) en prenant
# comme argument le nom du répertoire racine par lequel on doit commencer le parcours
&parcoursarborescencefichiers($rep);
#----------------------------------------
# dans le fichier XML, fermeture de la balise racine
print $OUTXML "</articles>\n";
# fermeture des fichiers de sortie
close $OUT;
close $OUTXML;
print "Nb item : $numberItem \n";
# affichage du temps écoulé depuis le lancement du programme
print "Time so far: ", $t->elapsed, " seconds\n";
exit;
#----------------------------------------------
# procédure parcoursarborescencefichiers
# on accède au contenu du dossier dans lequel on se trouve
sub parcoursarborescencefichiers {
# on récupère l'argument passé à la procédure (le nom de dossier)
my $path = shift(@_);
# ouverture du répertoire avec la fonction perl opendir :
# elle prend comme arguments : le nom du répertoire à ouvrir
# + un descripteur, qui va être à partir de maintenant le moyen d'accéder au répertoire qu'on a ouvert
opendir(my $DIRhandle, $path) or die "can't open $path: $!\n";
# lecture du répertoire avec la fonction perl readdir :
# elle prend comme argument le descripteur du répertoire qu'on vient d'ouvrir
# et renvoie comme valeur la liste des fichiers contenus dans le répertoire
# on stocke cette liste dans @files
my @files = readdir($DIRhandle);
# fermeture du répertoire
closedir($DIRhandle);
# pour chaque élément contenu dans le répertoire (ficher ou répertoire)
foreach my $file (@files) {
# on passe au fichier suivant s'il s'agit d'un répertoire caché
next if $file =~ /^\.\.?$/;
# reconstruction du chemin relatif de l'élément traité $file
$file = $path."/".$file;
# "." est un opérateur de concaténation : "2020"."/"."01" => "2020/01"
# on teste le statut du fichier avec les opérateurs -d et - f
# -d ---> true si $file est un répertoire
if (-d $file) {
# on relance la procédure parcoursarborescencefichiers sur le répertoire $file
&parcoursarborescencefichiers($file); # recursivité !
}
# -f ---> true si $file est un fichier
if (-f $file) {
# on veut récupérer des fichiers XML RSS et une seule rubrique en particuler :
# si le nom du fichier correspond au nom de la RUBRIQUE qu'on veut traiter
# ET se termine par l'EXTENSION .xml ---> ON TRAITE LE FICHIER !
if ($file =~/$rubrique.+xml$/) {
# incrémentation du compteur + message dans le terminal du fichier traité
print $nbFile++," Traitement de : ",$file,"\n";
# **************** TRAITEMENT DE LA BAO 1 : PERL XML::RSS ****************
# **************** EXTRACTION DU CONTENU TEXTUEL ****************
# on parse le fichier
eval {$rss->parsefile($file);};
# si il y a eu une erreur de parsing, on affiche un message d'erreur
if( $@ ){
$@ =~ s/at \/.*?$//s; # remove module line number
print STDERR "\nERROR in '$file':\n$@\n";
}
# sinon ---> extraction
else{
foreach my $item (@{$rss->{'items'}}){
# extraction du titre dans la variable $titre
my $titre=$item->{'title'};
# extraction de la description dans la variable $description
my $description=$item->{'description'};
# incrémentation du compteur d'items
$numberItem++;
# appel de la procédure de nettoyage
($titre, $description)=&nettoyage($titre, $description);
# si le titre n'existe pas dans la table de hachage $dico_des_titres,
if (!(exists $dico_des_titres{$titre})) {
# alors ce n'est pas un doublon : on ajoute le titre et sa description à la table de hachage
$dico_des_titres{$titre}=$description ;
# Ecriture des résultats en sorties
print $OUT "$titre\n";
print $OUT "$description\n";
print $OUT "--------------------\n";
print $OUTXML "\t<item>\n";
print $OUTXML "\t\t<titre>$titre</titre>\n";
print $OUTXML "\t\t<description>$description</description>\n";
print $OUTXML "\t</item>\n";
}
# si le titre est déjà dans $dico_des_titres, on ne fait rien
}
}
}
}
}
}
#----------------------------------------------
# procédure nettoyage
sub nettoyage {
#my $titre=shift(@_); autre solution en vidant la liste des arguments du programmes...
#my $description=shift(@_);
my $titre = $_[0];
my $description = $_[1];
$titre=~s/^<!\[CDATA\[//;
$titre=~s/\]\]>$//;
$description=~s/^<!\[CDATA\[//;
$description=~s/\]\]>$//;
$description=~s/<.+?>//g;
$description=~s/'/'/g;
$description=~s/"/"/g;
$description=~s/’/'/g;
$description=~s/\s\s+/ /g;
$titre=~s/<.+?>//g;
$titre=~s/'/'/g;
$titre=~s/"/"/g;
$titre=~s/’/'/g;
$titre=~s/\s\s+/ /g;
$titre=~s/$/\./g;
$titre=~s/\.+$/\./g;
return $titre,$description;
}
Résultats
Les sorties obtenues par ces deux programmes sont idendiques. Les deux méthodes sont performantes et produisent des résultats très satisfaisants. Toutefois, nous remarquons une différence au niveau de la vitesse d'exécution des programmes : le programme utilisant la méthode des expressions régulières est en moyenne plus de 10 fois plus rapide que celui faisant appel à la bibliothèque XML::RSS. Pour les boîtes à outils suivantes, nous privilégierons donc la première méthode.
RUBRIQUE | METHODE | |
---|---|---|
REGEXP | XML::RSS | |
Europe - 3214 | 0.314611 s | 4.034765 s |
Planète - 3244 | 0.387406 s | 3.86934 s |
Culture - 3246 | 0.295774 s | 4.002958 s |
Cinéma - 3476 | 0.216899 s | 3.872754 s |
Vous pouvez visualiser et télécharger les fichiers de sortie depuis le tableau ci-dessous :
RUBRIQUE | METHODE REGEXP | METHODE XML::RSS | ||
---|---|---|---|---|
SORTIE XML | SORTIE TXT | SORTIE XML | SORTIE TXT | |
Europe - 3214 | ||||
Planète - 3244 | ||||
Culture - 3246 | ||||
Cinéma - 3476 | ||||
Tout |