Free HTML5 Bootstrap Template by FreeHTML5.co

Boîte à Outils 1 - Extraire le texte du Fil RSS du Monde

Le corpus de travail est une arborescence de fils RSS du journal Le Monde. Le format RSS permet de décrire de façon synthétique le contenu d'un site web en insérant son titre et sa description dans un fichier XML, comme expliqué sur le site comment ça marche.

Le script PERL

Notre script perl prend deux arguments : le nom de l'arborescence ("2021" cette année) et le nom de la rubrique à traiter ("3208" pour la rubrique "À la une" par exemple).

if ($#ARGV != 1) {print "Il manque un argument à votre programme....\n";exit;}
my $REPERTOIRE="$ARGV[0]";
my $RUBRIQUE="$ARGV[1]";
# on s'assure que le nom du répertoire ne se termine pas par un "/"
$REPERTOIRE=~ s/[\/]$//;
open my $output_txt, ">:encoding(UTF-8)","corpus-titre-description.txt";
open my $output_xml, ">:encoding(UTF-8)","corpus-titre-description.xml";
print $output_xml "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<corpus>\n";

&parcoursarborescencefichiers($rep);
#----------------------------------------
print $output2 "\n";
close $output;
close $output2;
#----------------------------------------------
exit;

Il faut d'abord vérifier que tous les arguments aient été donnés par l'utilisateur. Pour ce faire on regarde si la variable #ARGV est différente de 1. Ainsi, s'il n'y a pas assez ou trop d'argument, le programme ne se lancera pas.

On récupère ensuite les arguments entrés par l'utilisateur que l'on nomme "REPERTOIRE" et "RUBRIQUE". Perl permet d'utiliser très facilement les expressions régulière pour nattoyer un texte. On utilise cette fonction pour enlever un éventuel "/" à la fin de "rep"

On peut maintenant ouvrir nos fichiers de travail dans lesquels seront stockés les titres et descriptions nettoyés sous format xml et sous format txt.

Le premier print est réalisé uniquement dans le fichier xml pour lui ajouter l'entête caractéristique des fichiers XML et ouvrir un noeud <corpus>.

Après avoir fait ces premiers traitement sur les fichers de sorties, on va parcourir les fichiers grâce à la fonction &parcoursarborescencefichiers qui va fonctionner de façon récursive.

Pour commencer, il faut récupérer l'argument de la fonction, c'est à dire "REPERTOIRE". On veut ensuite récupérer le fichier désigné par le path qui est récupéré. Si le fichier ne peut pas êter ouvert, le terminal affiche can't open et la commande s'arrête. On inscrit ensuite dans la variable liste files l'ensemble des fichiers contenus dans le répertoire qui a été ouvert.

Avec la commande foreach on entame une boucle qui traite tous les fichiers contenus dans la liste files. Pour éviter les fichiers cachés, on élimine les fichiers commançant par ... Pour pouvoir entrer des les dossiers contenus dans d'autres dossier, on utilise la récursivité en appelant à nouveau la fonction parcoursarborescencefichiers à l'intérieure de celle-ci si le "fichier" désigné par le path est un répertoire. On a auparavent récupéré le path associé au dossier pour pouvoir l'entrer en paramètre de ce nouvel appel.

Dans le cas où le "fichier" désigné par le path est de type file on regarde si le nom du fichier contient la variable RUBRIQUE. Si c'est le cas, on ouvre le fichier pour le traiter. On cherche le pattern qui nous intéresse avec les regex :

  • d'abord le titre de la forme : <title> suivi de mots quelconques </title>
  • Puis les descriptions de la forme :<description> des mots quelconques </description>
Les titres et les descriptions peuvent aussi être séparés par des mots quelconques. Pour récupérer ces séquences inconnues, on utilise la regex .+? qui a le même effet que .* mais sans être gourmand ce qui lui permet de s'arrêter dès qu'il rencontre une balise titre ou description.

En ayant récupéré les titre et les descriptions grâce au groupe de capture des expressions régulières, on peut maitnenant réinsérer ce contenu nettoyé dans nos fichiers de sortie output_txt et output_xml

sub parcoursarborescencefichiers {
	my $path = shift(@_);
	opendir(DIR, $path) or die "can't open $path: $!\n";
	my @files = readdir(DIR);
	closedir(DIR);
	foreach my $file (@files) {
		next if $file =~ /^\.\.?$/;
		$file = $path."/".$file;
		if (-d $file) {
			print "On entre dans le REPERTOIRE : $file \n";
			&parcoursarborescencefichiers($file);	#recurse!
			print "On sort du REPERTOIRE :  $file \n";
		}
		if (-f $file) {
			if ($file =~ /$RUBRIQUE.+\.xml$/) {
				print "Traitement du fichier $file \n";
				open my $input, "<:encoding(UTF-8)",$file;
				$/=undef; # par défaut cette variable contient \n
				my $ligne=<$input> ;
				close($input);
				while ($ligne=~/<item><title>(.+?)<\/title>.+?<description>(.+?)<\/description>/gs) {
					my $titre=&nettoyage($1);
					my $description=&nettoyage($2);
					print $output_txt "$titre \n";
					print $output_txt "$description \n";
					print $output_txt "----------------------------\n";
					print $output_xml "<item><titre>$titre</titre><description>$description</description></item>\n";
				}
			}
		}
	}
}
sub nettoyage {
my $texte=shift @_;
$texte=~s/(^<!\[CDATA\[)|(\]\]>$)//g;
$texte.=".";
$texte=~s/\.+$/\./;
return $texte;

La fonction de nettoyage prend en argument une suite de chaîne de caractères (l'ensemble d'un titre ou d'une description). Elle enlève ensuite tout les <![CDATA[ et les ]]> et remplace les suite de points en fin de texte par un seul point.

Le script Python

Notre script python prend les même arguments que le script perl : le nom de l'arborescence ("2021" cette année) et le nom de la rubrique à traiter ("3208" pour la rubrique "À la une" par exemple). Nous avons organisé notre script de la même manière que le script perl réalisé en cours la seule différence étant la place des fonctions que nous avons placées au début du script en python.

import os,re,sys

if len(sys.argv) != 3:
	print("Il manque un argument à votre programme...\n")
	exit
repertoire=sys.argv[1]
rubrique=sys.argv[2]
# on s'assure que le nom du répertoire ne se termine pas par un "/"
repertoire=re.sub("\/$","",repertoire)
output_txt=open("corpus-titre-description_versionPython.txt", "w")
output_xml=open("corpus-titre-description_versionPython.xml", "w")
output_xml.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<corpus>\n")

parcoursarborescencefichiers(repertoire)

output_xml.write("</corpus>")
output_txt.close()
output_xml.close()       

Contrairement à Perl, il faut récupérer en Python le nombre d'argument avec la fonction len() qui permet de connaître la longueur de la liste des arguments. On récupère les arguments grâce à la bibliothèque sys. Le premier argument de sys.argv est le nom de la fonction. On veut donc récupérer sys.argv[2] et sys.argv[3] (sys.argv[0] donne l'ensemble de l'entrée ,c'est à dire le nom de la fonction et les arguments).

On ouvre ensuite les fichiers le sorties et on ecrit l'entête du fichier XML dans la sortie xml.

Le gros de l'algorithme est réalisé dans la fonction parcoursarborescencefichiers que nous explicitons plus bas.

En python, on a pas besoin de récupérer l'argument de la fonction qui est ue variable locale à l'échelle de la fonction. On utilise la bibliothèque os et sa méthode os.scandir pour parcourir le dossier. On utilise ensuite la méthode os.path.join pour concatener le nom du dossier et le nom du fichier ou du dossier et avoir le path relatif à l'endroit de l'execution du script python.

On peut vérifier la classe de l'objet désigné par le path avec les méthodes os.path.isfile et os.path.isdir et ainsi déterminer si on a affaire à un dossier ou a un fichier. Dans le cas d'un dossier, on entame une récurssion en appelant la fonction parcoursarborescencefichiers. Sinon, on vérifie que le nom du fichier correspond au nom de la rubrique que l'on veut traiter avec une regex.

On traite le fichier s'il correspond au nom de la rubrique à traiter comme on le faisait avec Perl. Toutes les regex sont effectuées grâce à la bibliothèque Python re.

On récupère le texte avec la méthode group() qui fonctionne comme argv, c'est à dire que l'index 0 renvoie l'ensemble de la liste. On écrit le texte nettoyé dans les fichiers le sortie xml et txt. On utile l'opérateur + en Python pour concatener les balises xml avec les variables.

On notera que contrairement à Perl, on a pas besoin de traiter le cas ou le fichier commence par un point car ces fichiers ne sont pas pris en compte par la méthode os.scandir

def parcoursarborescencefichiers(rep):
with os.scandir(rep) as files :
	for file in files:
		file_name=os.path.join(rep,file.name)
		if os.path.isdir(file_name):
			print("On entre dans le REPERTOIRE : ",file_name , "\n")
			parcoursarborescencefichiers(file_name)
			print("On sort du REPERTOIRE : ", file_name, "\n")
		if os.path.isfile(file_name):
			name_match="{0}.+\.xml$".format(rubrique)
			if re.search(name_match,file_name) :
				print("Traitement du fichier", file_name, "\n")
				input=open(file_name, "r")
				lignes=input.readlines()
				input.close()
				for ligne in lignes :
					print(ligne)
					m = re.search(r"<item><title>(.+?)<\/title>.+?<description>(.+?)<\/description>",ligne)
					if m :
						titre=nettoyage(m.group(1))                            
						description=nettoyage(m.group(2))                            
						output_txt.write(titre+"\n")
						output_txt.write(description+"\n")
						output_txt.write("_______________________________")
						output_xml.write("<item><titre>"+titre+"</titre><description>"+description+"</description></item>\n")
def nettoyage(texte):
	texte=re.sub(r"(^<!\[CDATA\[)|(\]\]>$)","",texte)
	texte += "."
	texte=re.sub("\.+$","\.",texte)
	return texte

Pour la fonction de nettoyage, le code est très proche du code Perl. On utilise simplement re à la place des regex natives de Perl.

Le résultat






On obtient en résultat le titre et la description de chaque article contenu dans le fil RSS traité ("A la Une" pour nous) dans un fichier XML et dans un fichier txt











Projet réalisé par Eve Sauvage

En première année de Master Traitement Automatique des Langues