Boîte à Outils 1

La première boîte à outils consiste en l'extraction de données pertinentes à partir des fichiers XML contenant les flux RSS provenant du journal Le Monde.

Ces flux RSS du Monde sont générés quotidiennement et concentrent les articles qui ont été publiés le jour-même. Le journal Le Monde organise ses flux RSS par rubrique.

La boîte à outils n°1 vise à extraire le titre et la description de chaque flux RSS dans deux fichiers différents : un fichier texte et un fichier XML. Ces deux fichiers serviront ultérieurement pour l'élaboration des boîtes à outils supplémentaires pour mettre à bien notre projet.

Deux méthodes permettent d'extraire le titre et la description de chaque article des flux RSS du journal Le Monde. La première consiste à utiliser les expressions régulières et la seconde à s'appuyer sur la bibliothèque Perl XML::RSS. Dans les deux cas, l'algotrithme est simple : avec une fonction récursive, on parcourt le dossier où se trouvent les flux RSS. Si un fichier se trouve être un flux RSS alors, on extrait le titre et la description pour les mettre dans deux fichiers de résultat : un fichier XML et un fichier texte brut.

Dans les deux cas, nous avons recours à une fonction qui parcourt l'arborescence du dossier qui contient les fichiers RSS afin de déterminer quels sont les fichiers exploitables. Cette fonction est résursive à partir du moment où elle ne rencontre pas un fichier XML qui commence par 0,2.

De même, le script vérifie qu'on ne prend pas en compte plusieurs fois le même article. Charque titre et description des articles sont stockés dans un tableau multidimensionnel. S'il y a doublons au sein des fichiers RSS, seul le premier article stocké sera traité.

En utilisant les expressions régulières

Après étude des fichiers XML produits par le site, nous pouvons remarquer une structure constante qui va nous permettre de localiser aisément le titre et la description de chacun des articles du journal. En effet, nous pouvons constater que la balise <title> et <description> contiennent respectivement le titre et la description des articles. Il nous faut alors créer une boucle qui permettra de parcourir les fichiers XML à la recherche de ces balises afin d'en extraire le contenu avec l'aide des expressions régulières.

#!/usr/bin/perl
# Importation des bibliothèques
use utf8;
use HTML::Entities;
# Initialisation des variables
my $rep = "$ARGV[0]";
$rep =~ s/[\/]$//;				# On s'assure que le nom du répertoire ne se termine pas par un "/"
my %tableCategories = ();		# Table de hachage - Rubriques
my %tableArticles = ();			# Table de hachage - Articles
my $i = 1;						# Compteur d'articles lus pour debug
# Fonction de parcours d'arborescense de fichiers
sub scanFiles {
	my $path = shift(@_);
	opendir(dir, $path) or die "Je n'arrive pas à ouvrir $path : $!\n";
	my @files = readdir(dir);
	closedir(dir);
	foreach my $file (@files) {
		next if $file =~ /^\.\.?$/;
		$file = $path . "/" . $file;
		# Si file est un répertoire, la fonction est alors récursive
		if (-d $file) {
			scanFiles($file);		# Récursion
		}
		# Si file est un fichier, on traite le fichier
		if (-f $file) {
			if ($file =~ /0,2.+\.xml$/) {					
				print "\n\nTraitement de " . $file . ".\n\n";
				# On recupère l'encodage
				open(FILE, $file);
				$line1 = <FILE>;
				close(FILE);
				$line1 =~ /encoding=['"]([^\'\"]+)['"]/;
				$encodage = $1;
				# On recupère le titre du flux selon l'encodage pour avoir la catégorie
				open(fileItem, "<:encoding($encoding)", $file);
				my $category0 =~ /<channel>\s*<title>([^<]*)<\/title>/; 
				my $category = $1;
				# On nettoie la catégorie
				$category = lc($category);
				$category =~ s/le monde.fr : //g;
				$category =~ s/toute l'actualité sur le monde.fr//g;
				$category =~ s/\s//g;
				$category =~ s/[,.;:!]//g;
				$category =~ s/[éêëè]/e/g;
				$category =~ s/[âäà]/a/g;
				$category =~ s/[îï]/i/g;
				$category =~ s/[ôö]/o/g;
				$category =~ s/[ûüù]/u/g;
				$category =~ s/[ÿ]/y/g;
				# On initialise les noms des fichiers de sortie
				my $outputXml = "sortie-" . $category . ".xml";
				my $outputTxt = "sortie-" . $category . ".txt";	
				# On verifie que la categorie n'existe pas
				if (!(exists($tableCategories{$category}))) {
						$tableCategories{$category}++;
						# On crée un fichier XML vide
						open(fileOutXml, ">:encoding(utf-8)", $outputXml) or die "Je n'ai pas réussi à ouvrir le fichier $outputXml !";				
						print fileOutXml "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
						print fileOutXml "<PARCOURS>\n";
						close(fileOutXml);		
						# On crée un fichier txt vide
						open(fileOutTxt, ">:encoding(utf-8)", $outputTxt) or die "Je n'ai pas réussi à ouvrir le fichier $outputTxt !";
						close(fileOutTxt);							
				}
				# On récupère le contenu désiré des articles
				while($string =~ /<item><title>([^<]*)<\/title>.*?<description>([^<]*)<\/description>/g) {		
					$tmpTitle = $1;
					$tmpDesc = $2;
					# On ne garde que le nécessaire
					$tmpDesc =~ s/<p.*?\/>//;
					$tmpDesc =~ s/<a href[^>]+>//g;
					$tmpDesc =~ s/<img[^>]+>//g;
					$tmpDesc =~ s/<\/a>//g;
					$tmpDesc =~ s/<[^>]+>//g;
					$tmpDesc =~ s/ - Lisez l'intégralité de l'article pour plus d'information.//g;
					# On nettoie le titre et la description
					$cleanTitle = decode_entities($tmpTitle);
					$cleanDesc = decode_entities($tmpDesc);
					if (uc($encodage) ne "UTF-8") {
						utf8::decode($cleanTitle);
						utf8::decode($cleanDesc);
					}	
					print $i . " - " . $cleanTitle . " - " . $cleanDesc . "\n";
					$i++;
				}
				# On verifie que l'article n'existe pas déjà
				if ($tableArticles{$cleanTitle} ne $cleanDesc) {
					# S'il n'existe pas, on l'ajoute
					$tableArticles{$cleanTitle} .= $cleanDesc;	
					# On ajoute le titre et la description dans le fichier XML...
					open(fileOutXml, ">>:encoding(utf-8)", $outputXml) or die "Je n'ai pas réussi à ouvrir le fichier $outputXml !";
					print fileOutXml "<item>\n\t<titre>$cleanTitle</titre>\n\t<description>$cleanDesc</description>\n</item>\n";
					close(fileOutXml);					
					# ...ainsi que dans le fichier TXT
					open(fileOutTxt, ">>:encoding(utf-8)", $outputTxt) or die "Je n'ai pas réussi à ouvrir le fichier $outputTxt !";						
					print fileOutTxt "§ " . $cleanTitle . "\n" . $cleanDesc ."\n\n";
					close(fileOutTxt);
				}
				close(fileItem);
			}
		}
	}
}
# On parcourt les fichiers
scanFiles($rep);
# Fermetures des balises des fichiers de sortie
foreach my $category (%tableCategories) {
	my $outputXml = "sortie-" . $category . ".xml";
	if (!open (fileOutXml,">>:encoding(utf-8)", $outputXml)) { die "Problème à l'ouverture du fichier $outputXml."};
	print fileOutXml "</PARCOURS>\n";
	close(fileOutXml);
}
exit;

En utilisant la bibliothèque Perl XML::RSS

La bibliothèque XML::RSS est fort pratique : en effet, elle permet un hashage du fichier XML et il est ainsi beaucoup plus aisé d'extraire les informations désirées de la table de hashage pour ensuite les traiter. Une table de hashage n'est rien d'autre qu'un tableau multidimensionnel. Les boîtes à outils suivantes sont élaborées en utilisant la bibliothèque XML::RSS et non les expressions régulières.

#!/usr/bin/perl
# Importation des bibliothèques
use XML::RSS;
use utf8;
use HTML::Entities;
# Initialisation des variables
my $rss = new XML::RSS;
my $rep = "$ARGV[0]";
$rep =~ s/[\/]$//;				# On s'assure que le nom du répertoire ne se termine pas par un "/"
my %tableCategories = ();		# Table de hachage - Rubriques
my %tableArticles = ();			# Table de hachage - Articles
my $i = 1;						# Compteur d'articles lus pour debug
# Fonction de parcours d'arborescense de fichiers
sub scanFiles {
	my $path = shift(@_);
	opendir(dir, $path) or die "Je n'arrive pas à ouvrir $path : $!\n";
	my @files = readdir(dir);
	closedir(dir);
	foreach my $file (@files) {
		next if $file =~ /^\.\.?$/;
		$file = $path . "/" . $file;
		# Si file est un répertoire, la fonction est alors récursive
		if (-d $file) {
			scanFiles($file);		# Récursion
		}
		# Si file est un fichier, on traite le fichier
		if (-f $file) {
			if ($file =~ /0,2.+\.xml$/) {
				# Vérification de la validité du fichier d'entrée
				eval {
					$rss -> parsefile($file);
				};
				if ($@) {
					$@ =~ s/at \/.*?$//s;		# On enlève le numéro de la ligne en cas d'erreur
					print STDERR "\nErreur dans '$file' :\n$@\n";
				}
				# Le fichier est bel et bien un fichier RSS, on poursuit !
				else {
					print "\n\nTraitement de " . $file . ".\n\n";
					# On recupère l'encodage
					open(fileItem, $file);
					my $encoding = $rss -> {'encoding'};
					close(fileItem);
					# On recupère le titre du flux selon l'encodage pour avoir la catégorie
					open(fileItem, "<:encoding($encoding)", $file);
					my $category = $rss -> {'channel'} -> {'title'};
					# On nettoie la catégorie
					$category = lc($category);
					$category =~ s/le monde.fr : //g;
					$category =~ s/toute l'actualité sur le monde.fr//g;
					$category =~ s/\s//g;
					$category =~ s/[,.;:!]//g;
					$category =~ s/[éêëè]/e/g;
					$category =~ s/[âäà]/a/g;
					$category =~ s/[îï]/i/g;
					$category =~ s/[ôö]/o/g;
					$category =~ s/[ûüù]/u/g;
					$category =~ s/[ÿ]/y/g;
					# On initialise les noms des fichiers de sortie
					my $outputXml = "sortie-" . $category . ".xml";
					my $outputTxt = "sortie-" . $category . ".txt";	
					# On verifie que la categorie n'existe pas
					if (!(exists($tableCategories{$category}))) {
							$tableCategories{$category}++;
							# On crée un fichier XML vide
							open(fileOutXml, ">:encoding(utf-8)", $outputXml) or die "Je n'ai pas réussi à ouvrir le fichier $outputXml !";				
							print fileOutXml "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
							print fileOutXml "<PARCOURS>\n";
							close(fileOutXml);
							# On crée un fichier txt vide
							open(fileOutTxt, ">:encoding(utf-8)", $outputTxt) or die "Je n'ai pas réussi à ouvrir le fichier $outputTxt !";
							close(fileOutTxt);							
					}
					# On récupère le contenu désiré des articles
					foreach my $item (@{$rss -> {'items'}}) {
						my $tmpTitle = $item -> {'title'};
						my $tmpDesc = $item -> {'description'};
						# On ne garde que le nécessaire
						$tmpDesc =~ s/<p.*?\/>//;
						$tmpDesc =~ s/<a href[^>]+>//g;
						$tmpDesc =~ s/<img[^>]+>//g;
						$tmpDesc =~ s/<\/a>//g;
						$tmpDesc =~ s/<[^>]+>//g;
						$tmpDesc =~ s/ - Lisez l'intégralité de l'article pour plus d'information.//g;
						# On nettoie le titre et la description
						$cleanTitle = decode_entities($tmpTitle);
						$cleanDesc = decode_entities($tmpDesc);
						print $i . " - " . $cleanTitle . " - " . $cleanDesc . "\n";
						$i++;
					}
					# On verifie que l'article n'existe pas déjà
					if ($tableArticles{$cleanTitle} ne $cleanDesc) {
						# S'il n'existe pas, on l'ajoute
						$tableArticles{$cleanTitle} .= $cleanDesc;
						# On ajoute le titre et la description dans le fichier XML...
						open(fileOutXml, ">>:encoding(utf-8)", $outputXml) or die "Je n'ai pas réussi à ouvrir le fichier $outputXml !";
						print fileOutXml "<item>\n\t<titre>$cleanTitle</titre>\n\t<description>$cleanDesc</description>\n</item>\n";
						close(fileOutXml);
						# ...ainsi que dans le fichier TXT
						open(fileOutTxt, ">>:encoding(utf-8)", $outputTxt) or die "Je n'ai pas réussi à ouvrir le fichier $outputTxt !";						
						print fileOutTxt "§ " . $cleanTitle . "\n" . $cleanDesc ."\n\n";
						close(fileOutTxt);
					}
					close(fileItem);
				}
			}
		}
	}
}
# On parcourt les fichiers
scanFiles($rep);
# Fermetures des balises des fichiers de sortie
foreach my $category (%tableCategories) {
	my $outputXml = "sortie-" . $category . ".xml";
	if (!open (fileOutXml,">>:encoding(utf-8)", $outputXml)) { die "Problème à l'ouverture du fichier $outputXml."};
	print fileOutXml "</PARCOURS>\n";
	close(fileOutXml);
}
exit;

Les boîtes à outils suivantes seront réalisées à partir du code qui utilise la bibliothèque XML::RSS car je trouve que c'est bien plus pratique à utiliser.