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.
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 :
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.
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.
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
En première année de Master Traitement Automatique des Langues