Documents Structurés
Site réalisé dans le cadre du cours de Documents Structurés par Kelly MASCLEF et Julie SAUVAGE.
Visiter le siteL'objectif de cette boîte à outils est d'appliquer un filtrage automatique sur un arbre de fils RSS. Pour cela, nous avons eu à écrire des scripts Perl qui parcourent l'arborescence des répertoires et fichiers contenant les fils RSS 2009 et 2010 de lemonde.fr récupérés chaque jour à 19h.
Nous avons donc extrait et filtré le texte contenu entre les balises <TITLE> </TITLE> et <RESUME> </RESUME> afin d'avoir le titre et le résumé de chaque article. Nous avons aussi extrait la date et la rubrique correspondantes.
Pour cela, nous avons utilisé différentes méthodes:
Ce script utilise les commandes Perl et extrait les titres et les contextes avec des expressions régulières. Il s'agit de la première version que nous avons écrite, donc une version "brute" (d'où "à la loyale") parce qu'il s'agit d'une seule fonction appelée parcoursarborescencefichiers qui contient toutes les instructions de notre programme. Nous indiquons ici les caractéristiques principales.
if ((-f $file) && ($file =~ /0,2-$numRub,1-0,0\.xml$/)) { #On affiche le fichier + compteur sur la console print "$file : \e[1;31;40m",$i++,"\e[0m\n"; #On ouvre le fichier traité qui pointe sur FILE open(FILE,$file);
while (my $ligne=<FILE>) {
if ($ligne =~ /encoding=["|'](.*?)['|"]\?/){ #On pourrait se servir de la fonction file -i mais #celle-ci ne fonctionne pas à tous les coups #L'encodage se trouve entre "" ou ' ' $encodage=$1; #On affiche l'encodage print "\e[1;34;40m$encodage\e[0m\n"; }
#On récupère la date $texte=~/<pubDate>([^<]+)<\/pubDate>/; #On stocke la date dans une variable $date my $date=$1; #On supprome les balises CDATA $texte =~ s///g; }
while ($texte =~ /<item><title>(.+?)<\/title>.+?<description>(.+?)<\/description>/g) { #On récupère le titre et le résumé de l'item my $titre=$1; #indique la première extraction (.+?) my $resume=$2; #la deuxième
sub nettoietexte { my $texte=shift; $texte =~ s/</</g; #On remplace < par l'élément correspondant $texte =~ s/>/>/g; $texte =~ s/<a href[^>]+>//g; $texte =~ s/<img[^>]+>//g; $texte =~ s/<\/a>//g; $texte =~ s/'/'/g; $texte =~ s/&/&/g; $texte =~ s/'/'/g; $texte =~ s/'/'/g; $texte =~ s/"/"/g; $texte =~ s/"/"/g; $texte =~ s/"/"/g; $texte =~ s/[/[/g; $texte =~ s/]/]/g; #$texte =~ s/í/'/g; $texte =~ s/<[^>]+>//g; #On supprime les balises fermantes $texte =~ s/ //g; #On supprime les blancs return $texte; }
Elle est appelée de la façon suivante:
$titre = &nettoietexte($titre); $resume = &nettoietexte($resume);Sur la variable entre parenthèses nous appliquons la fonction nettoietexte et nous remplaçons la vieille variable par le nouveau résultat.
Pour télécharger ce script, cliquez ici
Pour visualiser ce script, cliquez ici
Ce script utilise encore la méthode des expressions régulières, mais nous avons décidé de l'organiser de façon à ce qu'il soit plus "ordonné". Nous avons donc créé plusieurs fonctions (le résultat étant toujours le même) qui sont appelées au fur et à mesure dans l'avancement du programme. Le résultat est un script "propre" qui permet de retrouver facilement ce que nous cherchons et de remplacer juste la fonction lorsque nous allons changer de méthode. C'est l'intérêt du bon programmeur de ne pas se perdre dans ses milliers de codes! Il est donc recommandé de s'organiser de façon ordonnée. Nous indiquons ici les nouveautés:
given ($rub) { when ($rub eq 1) {$numRub="3208";$nomRub="A_la_une";} when ($rub eq 2) {$numRub="3210";$nomRub="International";} when ($rub eq 3) {$numRub="3214";$nomRub="Europe";} when ($rub eq 5) {$numRub="3224";$nomRub="Opinions";} when ($rub eq 4) {$numRub="3232";$nomRub="Societe";} when ($rub eq 6) {$numRub="3236";$nomRub="Medias";} when ($rub eq 7) {$numRub="3238";$nomRub="Rendez-vous";} when ($rub eq 8) {$numRub="3242";$nomRub="Sports";} when ($rub eq 9) {$numRub="3244";$nomRub="Planete";} when ($rub eq 10) {$numRub="3246";$nomRub="Culture";} when ($rub eq 11) {$numRub="3260";$nomRub="Livres";} when ($rub eq 12) {$numRub="3404";$nomRub="Examens";} when ($rub eq 13) {$numRub="3476";$nomRub="Cinema";} when ($rub eq 14) {$numRub="3546";$nomRub="Voyage";} when ($rub eq 15) {$numRub="651865";$nomRub="Technologies";} default {print "Je n'ai pas compris votre requête\n";} }
if ((-f $file) && ($file =~ /0,2-$numRub,1-0,0\.xml$/)) { print "Traitement en cours...\n"; #on lance la proc. num.2 --> le traitement des fichiers &traiterFichier($file); }
#recuperation de l'encodage avec la procédure num. 3 $encodage=&extractEncoding($file);
Cette procédure est expliquée plus en bas. Elle récupère l'encodage avec une expression régulière, et RETOURNE sa valeur. C'est important de faire un return à la fin puisqu'il va être stocké dans la variable $encodage, comme on vient de le montrer:
sub extractEncoding { $file=shift; #On ouvre le fichier traité qui pointe sur FILE open(FILE,$file); #--------on récupère l'encodage du fichier------- while (my $ligne=<FILE>) { if ($ligne =~ /encoding=["|'](.*?)['|"]\?/){ $encodage=$1; #On affiche l'encodage print "\e[1;34;40m$encodage\e[0m\n"; } } #On ferme le fichier close(FILE); return $encodage; }
sub filtrageTexte { #my $texte=shift; #print texte; while ($texte =~ /<item><title>(.+?)<\/title>.+?<description>(.+?)<\/description>/g) { #On récupère 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 nettoie le titre et le résumé grâce à la procédure nettoietexte $titre = &nettoietexte($titre); $resume = &nettoietexte($resume); #Si la clef du titre n'existe pas et si le titre ne contient rien {INSTRUCTIONS} if ((!(exists($LISTEXML{$titre})))&&($titre ne "")) { #On incrémente le compteur $i++; #On écrit les sorties nécessaires dans les fichiers print FILEOUTTXT "TITRE : $titre \n"; print FILEOUTTXT "RESUME : $resume \n\n"; print FILEOUTXML "<ARTICLE num=\"$i\"><TITRE>$titre</TITRE><RESUME>$resume</RESUME></ARTICLE>\n"; print OUTXML "$resume"; #On définit à 1 la valeur de la clef de $titre $LISTEXML{$titre}=1; } } }
sub tabloCouleurs { print "Quelle rubrique voulez-vous traiter?\n Tapez sur le numéro de la rubrique désirée:\n \e[1;32;40m1.\e[0m\t\e[1;33;40mA la une\e[0m\t \e[1;32;40m6.\e[0m\t\e[1;33;40mMedias\e[0m\t\t \e[1;32;40m11.\e[0m\t\e[1;33;40mLivres\e[0m\n \e[1;32;40m2.\e[0m\t\e[1;33;40mInternational\e[0m\t \e[1;32;40m7.\e[0m\t\e[1;33;40mRendez-vous\e[0m\t \e[1;32;40m12.\e[0m\t\e[1;33;40mExamens\e[0m\n \e[1;32;40m3.\e[0m\t\e[1;33;40mEurope\e[0m\t\t \e[1;32;40m8.\e[0m\t\e[1;33;40mSports\e[0m\t\t \e[1;32;40m13.\e[0m\t\e[1;33;40mCinema\e[0m\n \e[1;32;40m4.\e[0m\t\e[1;33;40mSociété\e[0m\t\t \e[1;32;40m9.\e[0m\t\e[1;33;40mPlanete\e[0m\t\t \e[1;32;40m14.\e[0m\t\e[1;33;40mVoyages\e[0m\n \e[1;32;40m5.\e[0m\t\e[1;33;40mOpinions\e[0m\t \e[1;32;40m10.\e[0m\t\e[1;33;40mCulture\e[0m\t\t \e[1;32;40m15.\e[0m\t\e[1;33;40mTechnologies\e[0m\n"; }
Pour télécharger ce script, cliquez ici
Pour visualiser ce script, cliquez ici
X-Path est un langage d'interrogation qui permet de manipuler et de se déplacer dans les fichiers XML. Dans ce script, notre objectif est de remplacer les méthodes de recherche par expression régulière par des requêtes X-Path. Nous avons décidé aussi d'utiliser X-Path en le combinant avec la librairie Perl XML::LibXml, une librairie de parsing et manipulation XML qui intègre dans ses dernières versions X-Path et d'autres fonctionnalités. Cela nous a donc permis de créer les fichiers, d'y écrire dedans, d'extraire l'encodage et les informations qui nous intéressaient grâce à des nouvelles requêtes. Voyons ensemble.
use XML::XPath; use XML::LibXML; #On utilise une bibliothèque permettant de #convertir à l'encodage désiré use Unicode::String qw(utf8); use feature "switch";
my $XML="SORTIE-$nomRub.xml"; my $DocXML = XML::LibXML::Document->new( '1.0', 'utf-8' ) or {die "Problème à l'ouverture du fichier $XML"}; #Document est une méthode de libxml pour traiter les fichiers $racine = $DocXML->createElement ('FICHIERS'); #dans le document, nous créons un élément… $DocXML->setDocumentElement ($racine); #…que nous établissons comme élément racine
sub getFileEncoding{ $dom = XML::LibXML->load_xml(location => $file ); #$dom est de type Document #il possède la méthode encoding my $encodage=$dom->encoding; print "$encodage / "; #on vérifie l'encodage print $dom->actualEncoding."\n"; #renvoi de l'encodage return $encodage; }
#initialisation de l'objet XPath $xp = XML::XPath->new( filename => $file ) or die "PROBLÈME"; #On stocke dans la varibale le motif de recherche my $search_path1.="//channel"; #ici le chemin Xpath #On crée une boucle sur les noeuds foreach my $noeud1 ( $xp->find($search_path1)->get_nodelist )
my $fic=$DocXML->createElement("FICHIER"); $racine->addChild($fic); #le nom du fichier (fils de la balise "FICHIER"): my $nom=$DocXML->createElement("NOM"); $nom->appendTextNode($file); $fic->addChild($nom); #la date (fils de la balise "FICHIER"): my $date=$noeud1->find('//pubDate')->string_value; my $dat=$DocXML->createElement("DATE"); $dat->appendTextNode($date); $fic->addChild($dat); #la balise ARTICLES (fils de la balise "FICHIER"): $Rub=$DocXML->createElement("ARTICLES"); $Rub->setAttribute("nom","$nomRub"); $fic->addChild($Rub); }
my $search_path.="//item"; # chemin X-Path foreach my $noeud ( $xp->find($search_path)->get_nodelist ) { #On récupère le titre et le résumé de l'item my $titre=$noeud->find('title')->string_value; my $resume=$noeud->find('description')->string_value;
#on lance la procédure num. 3 &filtrerTexte($file); #écriture finale du fichier XML $DocXML->toFile($XML,1);
Pour télécharger ce script, cliquez ici
Pour visualiser ce script, cliquez ici
XML::RSS est une librairie Perl qui permet de traiter les flux RSS (RDF Side Summary). Les méthodes permettent la manipulation des flux. Normalement il permet de créer des fichiers RSS, mais nous allons juste exploiter ses fonctionnalités pour l'extraction des données qui nous intérèssent:
#On crée une variable pour instancier un objet RSS my $rss=new XML::RSS; #Les résultats du parsing sont stockés dans $rss $rss->parsefile($file); #On initialise un compteur qui va nous permettre #d'attribuer un numéro pour chaque article $i=0;
foreach my $item (@{$rss->{'pubDate'}}) { #On récupère le titre et le résumé de l'item #en cherchant en fonction des balises du document rss my $date=$item->{'pubDate'};
foreach my $item (@{$rss->{'items'}}) { #On récupère le titre et le résumé de l'item #en cherchant en fonction des balises du document rss my $titre=$item->{'title'}; my $resume=$item->{'description'};
Pour télécharger ce script, cliquez ici
Pour visualiser ce script, cliquez ici
Nous avons précédemment introduit la bibliothèque XML::LibXML. Nous avons ici un script "version simplifiée", dont nous montrons les différences principales par rapport aux scripts précédents:
my $parser = XML::LibXML->new(); #Les résultats de parse_file sont stockés dans $xp
#On crée une boucle sur les noeuds channel foreach my $noeud1 ( $xp->findnodes('//channel')->get_nodelist ) { #On récupère le titre et le résumé de l'item #en cherchant en fonction des balises des noeuds my $date=$noeud1->findnodes('//pubDate')->string_value;
foreach my $noeud ( $xp->findnodes('//item')->get_nodelist ) { my $titre=$noeud->findnodes('title')->string_value; my $resume=$noeud->findnodes('description')->string_value;
Pour télécharger ce script, cliquez ici
Pour visualiser ce script, cliquez ici