Documents Structurés

Site réalisé dans le cadre du cours de Documents Structurés par Kelly MASCLEF et Julie SAUVAGE.

Visiter le site

Projet Encadré 1

Programmation et Projet Encadré 1 du Semestre 1 par Kelly MASCLEF et Julie SAUVAGE (et avec Julie BELIAO).

Visiter le site

Projet Encadré 1

Programmation et Projet Encadré 1 du Semestre 1 par Ilaria TIDDI (avec Marcelo MATOSO et Camille DOUDANE).

Visiter le site

TAL

L5T53 / L5T55 (BASH/Emacs/Python) par Kelly MASCLEF

Visiter le site

Environnement-TAL

L5T53 / L5T55 (BASH/Emacs/Python) par Julie SAUVAGE

Visiter le site

Programmation pour le TAL avec PERL

L6T54 / L6T55 (PERL) par Julie SAUVAGE

Visiter le site

Bienvenue chez Perl

L6T54 / L6T55 (PERL) par Kelly MASCLEF

Visiter le site

Boîte à Outils 1

L'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:

  • une méthode "brute" utilisant les commandes de Perl,
  • une seconde méthode avec les commandes de Perl mais "plus ordonnée",
  • une méthode avec les bibliothèques XML::XPath et XML::LibXML,
  • une autre avec la bibliothèque XML::RSS
  • et une dernière et cinquième méthode avec la bibliothèque XML::LibXML.

  • Script "à la loyale"

    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.

  • Récupération du fichier xml qui nous intéresse avec une expression régulière et ouverture de la commande OPEN(FILE):
  •  
    			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);

  • Lecture du fichier avec la commande <FILE>:
  •        	while (my $ligne=<FILE>) {
           

  • Extraction de l'encodage grâce à une expression régulière:
  • 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";
    			}

  • De la même façon nous extrayons les dates et effaçons les balises en trop:
  • 	
    			#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;
    			}

  • Extraction des titres et des résumés:
  • 	
     			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

  • Une deuxième fonction donnée au départ par les profs nous permet de nettoyer les textes à travers des expressions régulières:
  • 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/&#39;/'/g;
    	$texte =~ s/&/&/g;
    	$texte =~ s/&#39;/'/g;
    	$texte =~ s/'/'/g;
    	$texte =~ s/&#34;/"/g;
    	$texte =~ s/&#34;/"/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


    [Haut de Page]

    Script "à la loyale" avec variantes

    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:

  • Nous avons testé la commande given(<objet>)…when(<option>)…default(<option_de_default>) comme alternative à la structure if…elsif…elsif…. Le résultat est le même, la commande demande juste l'utilisation d'une version de perl supérieure à 5.10 et l'importation du module "switch":
  • 	
     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";}
    }

  • Dans la fonction parcourarborescencefichier nous parcourons les dossiers à la recherche du bon fichier xml. Ensuite, nous appelons la fonction du traitement du fichier:
  • 		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);		
    		}

  • Dans la procédure traiterFichier nous extrayons les infos que nous cherchons. La date est extraite de la même façon que le premier script, l'encodage est extrait en appelant une troisième procédure extractEncoding:
  • 		#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;
    }

  • Nous avons créé une procédure (appelée: &filtrageTexte($texte);) de filtrage du texte pour extraire les contenus, toujours avec des expressions régulières:
  • 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;
    		}
    	}
    }

  • Nous avons une procédure aussi pour le choix de la rubrique, que nous appelons au début du script &tabloCouleurs;:
  • 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


    [Haut de Page]

    Script avec XML::XPath et XML::LibXML

    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.

  • Il ne faut pas oublier d'importer les bibliothèques nécessaires:
  • 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";

  • Ensuite, nous avons créé notre sortie xml avec Xml::LibXml:
  • 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

  • L'extraction de l'encodage aussi s'effectue via Xml::LibXml:
  • 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;
    }

  • Nous initialisons un objet XPath dans lequel nous stockerons la liste des noeuds que nous allons traiter:
  • #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 )

  • Ici nous créons la structure du fichier XML à l'aide de XML::LibXml::Document. La procédure adoptée est la suivante: nous créons l'élément(<createElement>), nous prenons le contenu récupéré (<appendTextnode()>) et nous le confions aux fils créés, éventuellement nous créons un attribut (<setAttribute()>) et finalement nous "attachons" le fils au père (<$père->addChild($fils)>)…pour qu'ils soient une famille heureuse. Voici le code:
  •        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);
    			}

  • On a extrait les contenus des noeuds avec Xpath:
  •  
        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;

  • Une fois effectué tous les traitements, le fichier XML est écrit est fermé:
  • #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


    [Haut de Page]

    Script avec la bibliothèque XML::RSS

    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:

  • Le script s'appuie sur le script de base, mais c'est dans le filtrage du texte que nous voyons les premières différences. Nous initialisons l'objet et le "parsons":
  • #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;

  • De chaque fichier, nous extrayons la date avec la bonne méthode:

  • 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'};

  • Ensuite nous récupérons les contenus des balises titre et description, fils de la balise item:
  • 	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


    [Haut de Page]

    Script avec la bibliothèque XML::LibXML

    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:

  • L'instanciation de l'objet:
  • my $parser = XML::LibXML->new();
    #Les résultats de parse_file sont stockés dans $xp

  • La recherche du noeud <channel> et <pubDate>:
  • #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;

  • La recherche de <titre> et <description>:
  • 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

    [Haut de Page]