Dans la boîte à outils 1, on va trouver un programme qui prend en entrée les flux RSS, parcourt l'arborescence des fils RSS et fait les opérations suivantes : un encodage, un filtrage, une extraction et un nettoyage de textes. Pour ce faire, on vous propose les deux solutions ci-dessous: une solution avec la bibliothèque Unicode::String et une solution avec la bibliothèque XML::rss.
Pour télécharger le script en Perl de la première solution, cliquez ici.
Pour télécharger le script en Perl de la deuxième solution, cliquez ici.
La première solution : la bibliothèque Unicode::String
D'abord, nous avons déclaré l'utilisation de la bibliothèque Unicode::String (avec la fonction utf-8). Le symbole "qw" en anglais signifie "quote word" : cette fonction permet d'extraire de la chaine l'argument entre parenthèses.
#/usr/bin/perluse Unicode::String qw(utf8);
Le programme suivant prend en entrée le nom du répertoire contenant les fichiers RSS à traiter et construit en sortie un fichier structuré contenant sur chaque ligne le nom du fichier et le résultat du filtrage :
Première phase : l'encodage. Le programme va recupérer l'encodage de chaque fichier RSS et il force l'encodage du fichier de sortie en UTF-8 :
#Definition de variable de telle sorte qu'elle contient l'information "utf-8" :my $encodagesortie="utf-8";
#On va recuperer l'encodage dans chaque fichier RSSmy $encodage=`file -i $ARGV[0] | cut -d= -f2`;
print"ENCODAGE : $encodage \n";
#Une commande qui travaille sur les chaînes de caractères : #On va aussi supprimer les eventuels retours à la ligne :
chomp($encodage);
#On ouvre un fichier en lecture, en specifiant l'encodage du fichier :
open(FILE,"<:encoding($encodage)", $ARGV[0]);
#On ouvre des fichiers de sorties et on les stocke dans deux fichiers.#On force l'encodage de sortie (utf-8) :
open(OUT1,">:encoding($encodagesortie)","SORTIE.txt");
open(OUT2,">:encoding($encodagesortie)","SORTIE.xml");
print OUT2 "<?xml version=\"1.0\" encoding=\"$encodagesortie\" ?>\n";
print OUT2 "<file>\n";
print OUT2 "<name>$ARGV[0]</name>\n";
Avant la phase de filtrage du texte, on a fait un pré-traitement - quelque soit le fichier d'entrée - de telle sorte que le contenu textuel soit sur une seule ligne pour avoir un format unique et pouvoir en suite faire un traitement générique :
#On va créer une variable "vide" au départ :my $texte="";
#On parcourt les fichiers ouverts en lecture ligne par ligne :while (my $ligne=<FILE>) {
#On supprime les retours à la ligne (on aurait pu utiliser "chomp" ou "$/") :
$ligne =~ s/\n//g;
#On concatène chaque nouvelle ligne dans $texte :
$texte .= $ligne;
}
#On supprime les blancs entre les balises #dans les lignes stockées dans la variable $texte :
$texte =~ s/> *</></g;
#On recupère la date du fichier :
$texte=~/<pubDate>([^>]+)<\/pubDate>/;
#On a besoin d'avoir cette date en sortie :my $date=$1;
#Si l'encodage original n'est pas en utf-8 ou #Si la valeur de la chaîne de caractère dans $encodage transcode en majuscule (ne) #on transcode {...} en utf-8 :if (uc($encodage) ne"UTF-8") {utf8($date);}
print OUT2 "<date>".$date."</date>\n";
print OUT2 "<items>\n";
Deuxième phase : le filtrage. Grâce à la boucle créée par la commande while, le programme va recupérer (et nettoyer) le titre et le résumé de chaque fichier RSS :
#Tant qu'on trouve un fichier avec les balises suivants...#...on va faire les choses suivantes (.+?), sur toutes les occurrences : while ($texte =~/<item><title>(.+?)<\/title>.+?<description>(.+?)<\/description>/g) {
#On stocke le titre et le résumé de l'item :my $titre=$1;
my $resume=$2;
if (uc($encodage) ne"UTF-8") {
utf8($titre);
utf8($resume);
}
#On va nettoyer le titre et le résumé :
$titre = &nettoietexte($titre);
$resume = &nettoietexte($resume);
print OUT1 "Titre : $titre \n";
print OUT1 "Resume : $resume \n";;
print OUT2 "<item><title>$titre</title><abstract>$resume</abstract></item>\n";
}
print OUT2 "</items>\n</file>\n";
close(OUT1);
close(OUT2);
close(FILE);
exit;
Troisième phase : le nettoyage. La fonction utilisée ci-dessus "nettoietexte" nettoie le texte et remplace certains élements par d'autres. La commande Shift prend la première valeur de la liste, l'enlève et retourne une valeur de la liste moins la première valeur. On se balade comme ça dans le texte, à l'aide des expressions régulières :
Quatrième phase : l'extraction. Le programme utilise la commande my (on l'utilise la première fois qu'on declare une variable) et va stocker les lignes extraites dans des fichiers de sortie en .txt et en .xml :
#La variable est $rep qui prendra la valeur de l'argument :my $rep="$ARGV[0]";
#On s'assure que le nom du répertoire ne se termine pas par un "/" :
$rep=~ s/[\/]$//;
#Declaration des hash :my %dico1=();
my %dico2=();
#Initialisation des variables pour stocker les lignes extraites :#(1 pour les .txt, 2 pour les .xml)my $DUMPFULL1="";
my $DUMPFULL2="";
#On initialise une variable contenant le flux de sortie pour le fichier .txt :my $output1="SORTIE.txt";
#S'il ne s'ouvre pas, on arrête le programme :if (!open (FILEOUT,">$output1")) { die "Pb a l'ouverture du fichier $output1"};
#On initialise une variable contenant le flux de sortie pour le fichier .xml :my $output2="SORTIE.xml";
#S'il ne s'ouvre pas, on arrête le programme :if (!open (FILEOUT,">$output2")) { die "Pb a l'ouverture du fichier $output2"};
Le traitement doit être récursif (pour tous les fichiers), donc on met en œuvre des procédures pour parcourir toute l'arborescence, puis on spécifie le format de sortie pour les fichiers :
#----------------------------------------
&parcoursarborescencefichiers($rep); #recurse!#----------------------------------------#On précise le format de sortie à partir des infos suivantes : print FILEOUT "<?xml version=\"1.0\" encoding=\"$encodagesortie\" ?>\n";
print FILEOUT "<PARCOURS>\n";
print FILEOUT "<NOM>ERVAS, KHALSA, MORAINE</NOM>\n";
print FILEOUT "<FILTRAGE>".$DUMPFULL1."</FILTRAGE>\n";
print FILEOUT "</PARCOURS>\n";
close(FILEOUT);
exit;
L'expréssion parcoursarborescencefichiers qu'on a mis en ouvre. c'est une fonction du script qui permet de parcourir l'arborescence :
sub parcoursarborescencefichiers {
my $path = shift(@_);
#On ouvre le repertoire, si on arrive pas les ouvrir on arrête le programme :
opendir(DIR, $path) or die "can't open $path: $!\n";
#On ouvre les fichier pour les lire :my @files = readdir(DIR);
closedir(DIR);
#Pour chaque fichier on a une liste (array) :foreachmy $file (@files) {
#Ensuite, si...nextif $file =~ /^\.\.?$/;
$file = $path."/".$file;
if (-d $file) {
&parcoursarborescencefichiers($file); #recurse!
}
if (-f $file) {
Si le fichier a une extension .xml, on va mettre en place une traitement spécial pour les balises :
if ($file =~file/\.xml$/)
{
#On ouvre le fichier pour le lire :
open (FILEIN, $file);
#Tant qu'il y a des lignes, on continue la boucle :while (my $ligne=<FILEIN>){
#Si on lit "item" on l'extrait...#on le met dans la variable DUMPFULL1 après <article>#et on retourne à la ligne:if($ligne=~m/<item>/) {
$DUMPFULL1=$DUMPFULL1 . "<article>" . "\n";
}
#Si on lit "fin item" on fait pareil...#...mais on écrit la balise après "fin article" :if($ligne=~m/<\/item>/) {
$DUMPFULL1=$DUMPFULL1 . "\n</article>" . "\n";
}
#Si on lit "title" puis n'importe quoi suivi de "fin title":if($ligne=~m/<title>([^<]*)<\/title>/) {
my $texte = $1;
#S'il n'existe pas dans la variable $dico{$texte} :if (!(exists ($dico1{$texte}))) {
$dico1{$texte}=1;
$texte=&nettoietexte($texte);
$DUMPFULL2=$DUMPFULL2 . $texte . "\n";
$DUMPFULL1=$DUMPFULL1 . "<titre>" .$texte. "</titre>\n";
}
else {
$dico1{$texte}++;
$DUMPFULL1 = $DUMPFULL1 . "<title>""</title>\n";
}
}
if($ligne=~ m/<description>([^<]*)<\/description>/) {
my $texte = $1;
if (!(exists ($dico2{$texte}))) {
$dico2{$texte}=1;
$texte=&nettoietexte($texte);
$DUMPFULL2=$DUMPFULL2 . $texte . "\n";
$DUMPFULL1=$DUMPFULL1 . "<abstract>" .$texte. "</abstract>\n";
}
else {
$dico1{$texte}++;
$DUMPFULL1 = $DUMPFULL1 . "<abstract></abstract>\n";
}
}
}
close (FILEIN);
}
print $i++,"\n";
La deuxième solution : la bibliothèque XML::RSS
Dans ce script, on a utilisé une bibliothèque qui nous permet de récupérer des éléments d'un fichier RSS. En sortie le programme suivant produit : 1) un fichier au format .txt contenant les flux RSS avec, sur chaque ligne, le titre et le résumé de l'article, 2) un fichier au format .xml contenant les flux RSS avec un numéro attribué à l'article en ordre croissant, le nom du fichier, la date, le titre de l'article et le résumé de l'article. Les fichiers de sorties sont organisés par leur rubrique. Pour ce faire, on a d'abord declaré l'utilisation de la bibliothèque XML::RSS :
#!/usr/bin/perluse Unicode::String qw(utf8);
use XML::RSS;
Dans ce script, on a ajouté un compteur pour attribuer un numéro à chaque fichier (my $i=0) et, par rapport à la première solution, on a mis en place un traitement différent pour les fichiers qui ont extension .xml avec la commande parsefile :
#On va recuperer l'encodage dans chaque fichier RSS #(avec l'option -I sur mac, -i sur windows)my $encodage=`file -I $file | cut -d= -f2`;
#Une commande qui travaille sur les chaînes de caractères : #On va aussi supprimer les eventuels retours à la ligne :
chomp($encodage);
#Creation d'une variable pour instancier un objet RSSmy $rss=new XML::RSS;
#On stocke les résultats de "parsefile" dans la variable $rss
$rss->parsefile($file);
my $date=$rss->{'channel'}->{'pubDate'};
if (uc($encodage) ne"UTF-8") {
utf8($date);
};
Le programme écrit dans le fichier de sortie .xml des informations, comme par exemple le nom du fichier et la date de l'article, et il utilise un compteur pour attribuer également un numéro à chaque article (my $j=0). Ensuite, par rapport à la première solution, on a mis en place un traitement différent pour les fichiers qui ont extension .xml avec la commande parsefile :
Puis, le programme utilise la boucle foreach pour récupérer d'autres informations sur l'article, comme par exemple le titre et le résumé :
foreachmy $item (@{$rss->{'items'}}) {
my $titre=$item->{'title'};
my $resume=$item->{'description'};
if (uc($encodage) ne"UTF-8") {
utf8($titre);
utf8($resume);
}
#On va nettoyer le titre et le résumé :
$titre = &nettoietexte($titre);
$resume = &nettoietexte($resume);
#S'il y a un titre dans le premier flux dans dico1 #qui n'est pas le même que le deuxième flux, on traite :if ((!(exists($dico1{$titre}))) && ($titre ne"")) {
#Incrémentation du compteur :
$j++;
Puis, le programme concatène les résultats dans une sortie .txt et dans une sortie .xml :
#On concatène dans dumpfull dans le fichier .txt :
$dico1{$titre}=1;
$DUMPFULL1.="$titre";
$DUMPFULL2.="<ARTICLE num=\"$j\"><TITRE>$titre</TITRE>";
}
#Sinon il n'y a pas de titre, on écrit rien :else {
$titre="";
}
#S'il n'y a pas déjà le même résumé dans dico2 #et le résumé qu'on traite n'est pas vide, on continue :if (!(exists($dico2{$resume})) && ($resume ne"")) {
$dico2{$resume}=1;
$DUMPFULL1.="$resume";
$DUMPFULL2.="<RESUME>$resume</RESUME></ARTICLE>\n";
}
else {
$resume="";
}
#On concatène dans le fichier .xml les informations :#if (!exists($titre ne "") && ($resume ne "")) {#$DUMPFULL2.=""#}#else{#$DUMPFULL2.="<ARTICLE num=\"$j\"><TITRE>$titre</TITRE><RESUME>
$resume</RESUME></ARTICLE>\n"; #} } #On ferme les dernières balises dans le fichier de sortie : $DUMPFULL2.="</ARTICLES>\n</FICHIER>\n;
Pour les parties d'encodage et nettoyage du texte, cfr. la première solution ci-dessus.