BAO 1

Parcourir toute l'arborescence et extraire les contenus textuels de tous les fils (classement des textes extraits par rubrique)


Quel est le but de cette boîte à outil?

Le Monde produit chaque jour des flux rss (des fichiers xml) pour ses 17 rubriques :



Un flux RSS est un fichier xml qui présente les articles récents de la rubrique.
Un extrait de ce fichier xml :
            
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Le Monde.fr - Actualités et Infos en France et dans le monde</title><description>Le Monde.fr - 1er site d’information. Les articles du journal et toute l’actualité en continu : International, France, Société, Economie, Culture, Environnement, Blogs ...</description><copyright>Le Monde - L’utilisation des flux RSS du Monde.fr est réservée à un usage strictement personnel, non professionnel et non collectif. Toute autre exploitation doit faire l’objet d’une autorisation et donner lieu au versement d’une rémunération. Contact : droitsdauteur@lemonde.fr</copyright><link>https://www.lemonde.fr/rss/une.xml</link><pubDate>Fri, 01 Jan 2021 17:56:34 +0000</pubDate><language>fr</language><atom:link href="https://www.lemonde.fr/rss/une.xml" rel="self" type="application/rss+xml"/><item><title><![CDATA[Covid-19 : pas de réouverture des théâtres, cinémas et musées le 7 janvier]]></title><pubDate>Fri, 01 Jan 2021 16:48:30 +0100</pubDate><description><![CDATA[En raison de l’avancée de l’épidémie, le gouvernement écarte la date avancée en décembre et réfléchit à de nouvelles mesures pour soutenir le secteur en pleine désillusion.]]></description><guid isPermaLink="true">https://www.lemonde.fr/culture/article/2021/01/01/culture-pas-de-reouverture-des-theatres-cinemas-et-musees-le-7-janvier_6064985_3246.html</guid><link>https://www.lemonde.fr/culture/article/2021/01/01/culture-pas-de-reouverture-des-theatres-cinemas-et-musees-le-7-janvier_6064985_3246.html</link><media:content url="https://img.lemde.fr/2021/01/01/449/0/5385/2692/644/322/60/0/722db00_429528356-260088.jpg" width="644" height="322"><media:description type="plain">La façade du cinéma parisien le Grand Rex, le 15 décembre 2020.</media:description><media:credit scheme="urn:ebu">ALAIN JOCARD / AFP</media:credit></media:content></item></channel></rss>
        
    

Dans cette boîte à outil, on souhaite extraire les titres et les descriptions de chaque article présenté dans chaque fichier xml de chaque rubrique choisie.
Comme les fils rss s'organisent en sous-parties de sous-parties de fichiers, on va donc devoir faire un programme qui soit récursif. C'est-à-dire qu'il va ouvrir des dossiers qui seront contenus dans d'autres dossiers qui seront contenus dans d'autres dossiers... On va ensuite devoir trier selon le nom de notre fichier car chaque rubrique a un xml avec des chiffres précis. De plus, on devra faire attention à ne sélectionner que les fichiers xml de ces derniers.

Perl


Le code ci-dessous est commenté. La version téléchargeable : ici
            
#/usr/bin/perl
# utilisation : perl bao1.pl ./2021 3208
# Il prend en arguments 2 éléments : (1) le nom de l'arborescence 2021 contenant les fils RSS de l'année 2021, (2) le nom de la rubrique à traiter (ici 3208 pour la rubrique A la une)
#-----------------------------------------------------------
use utf8;
use strict;
binmode(STDOUT, ":encoding(UTF-8)");
#-----------------------------------------------------------
if ($#ARGV != 1) {print "Il manque un argument à votre programme....\n";exit;} # on s'assure qu'il ne manque aucun argument
my $rep="$ARGV[0]"; #le répertoire 2021 sera le premier argument
my $RUBRIQUE="$ARGV[1]"; #la rubrique choisie est le deuxième argument
$rep=~ s/[\/]$//;# on s'assure que le nom du répertoire ne se termine pas par un "/"

#ouverture des fichiers#

open my $output, ">:encoding(UTF-8)","corpus-titre-description.txt"; #on créé un fichier txt qui recevra les titres et descriptions
open my $output2, ">:encoding(UTF-8)","corpus-titre-description.xml"; #on créé un fichier xml qui recevra les titres et descriptions
print $output2 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<corpus>\n"; #on écrit le début de notre fichier xml final
#----------------------------------------
&parcoursarborescencefichiers($rep); #on appelle notre fonction de récursivité
#----------------------------------------
print $output2 "</corpus>\n"; #on écrit la fin de notre fichier xml final

#fermeture des fichiers#

close $output;
close $output2;
#----------------------------------------------
exit;
#----------------------------------------------
#définition de la fonction#

sub parcoursarborescencefichiers {
    my $path = shift(@_); #on récupère le path, ici 2021 et on le supprime de la liste
    opendir(DIR, $path) or die "can't open $path: $!\n"; #on ouvre le dossier
    my @files = readdir(DIR); #on le lit
    closedir(DIR); #on le ferme
    foreach my $file (@files) { #pour chaque fichier
		next if $file =~ /^\.\.?$/; #on skip si le path du fichier est ../
		$file = $path."/".$file; #on ajoute le path récupéré au fichier
		if (-d $file) { #si le fichier est en fait un répertoire
			print "On entre dans le REPERTOIRE : $file \n"; #on écrit dans le terminal pour qu'on voit ce qu'il se passe
			&parcoursarborescencefichiers($file);	#on fait une récursivité
			print "On sort du REPERTOIRE :  $file \n"; #on écrit dans le terminal pour qu'on voit ce qu'il se passe
		}
		if (-f $file) { #si le fichier est vraiment un fichier
			if ($file =~ /$RUBRIQUE.+\.xml$/) { #si le fichier est la rubrique qui m'intéresse
				print "Traitement du fichier $file \n"; #on écrit dans le terminal pour qu'on voit ce qu'il se passe
				open my $input, "<:encoding(UTF-8)",$file; #on ouvre le fichier
				$/=undef; # par défaut cette variable contient \n donc on supprime le saut de ligne
				my $ligne=<$input> ; #on sauvegarde les lignes du fichier dans une variable
				close($input); #on ferme le fichier
				while ($ligne=~/<item><title>(.+?)<\/title>.+?<description>(.+?)<\/description>/gs) { #on va chercher cette expression jusqu'à ce qu'il n'y en ait plus dans le fichier (grâce à g), s sert à transformer les . en potentiel caractère non imprimable donc on ne peut rater aucun item avec cette expression
				#on va sauvegarder le texte contenu entre ()
					my $titre=&nettoyage($1); #on appelle la fonction nettoyage sur la première "variable" sauvegardé dans l'expression matchée
					my $description=&nettoyage($2); #on appelle la fonction nettoyage sur la deuxième "variable" sauvegardé dans l'expression matchée
					print $output "$titre \n"; #on écrit dans le fichier txt final le titre de l'article
					print $output "$description \n"; #on écrit dans le fichier txt final la description de l'article
					print $output2 "<item><titre>$titre</titre><description>$description</description></item>\n"; #on écrit dans le fichier xml final le titre et la description de l'article
				}
			}
		}
    }
}
#----------------------------------------------
#définition de la fonction qui va permettre de ne récupérer que le texte des titres et des descriptions#

sub nettoyage {
	my $texte=shift @_; #on récupère la valeur et on la supprime de la liste
	$texte=~s/(^<!\[CDATA\[)|(\]\]>$)//g; #on va supprimer les éléments qui ne sont pas du véritable texte
	$texte.=".";
	$texte=~s/\.+$/\./;
	return $texte; #on retourne la valeur textuelle finale
}
#----------------------------------------------
            
        

Lorsqu'on lance le programme voici ce qui s'affiche dans le terminal :



Voici les deux fichiers par rubrique en output :
- rubrique à la une : fichier txt et fichier xml.
- rubrique livres : fichier txt et fichier xml.
- rubrique cinema : fichier txt et fichier xml.

Un aperçu des résultats txt et xml :

txt :



xml :



Python


Le code ci-dessous est commenté. La version téléchargeable : ici et ici
            
#!/usr/bin/python3
import sys
from pathlib import Path
from bao1bis import extractionsur1fil

#utilisation : python3 bao1.py ./2021 corpus-titre-descriptionbao1py.xml corpus-titre-descriptionbao1py 3208

def parcours(dossier:Path,fichier_xml,fichier_txt,rubrique,doublons): #fonction de récursivité
	print(f"on traite {dossier}") #on affiche dans le terminal où on en est dans le traitement
	for fichier in sorted(dossier.iterdir()):
		if fichier.is_dir(): #si c'est pas un vrai fichier, on appelle la récursivité
				parcours(fichier,fichier_xml,fichier_txt,rubrique,doublons)
		if fichier.is_file() and fichier.name.endswith(".xml") and rubrique in fichier.name: #si le fichier est un véritable fichier, que c'est un fichier xml et qu'il est de la rubrique qu'on cherche
				extractionsur1fil(fichier,fichier_xml,fichier_txt,doublons) #on applique l'extracteur de titre/description
		
def main():
	dossier=Path(sys.argv[1]) #on récupère le path du dossier
	rubrique=sys.argv[4] #on récupère la rubrique
	doublons=[] #on met les titres et les descriptions dans cette liste pour éviter les doublons
	with open (sys.argv[2],'w') as fichier_xml: #on ouvre notre fichier xml final
		with open (sys.argv[3],'w') as fichier_txt: #on ouvre notre fichier txt final
			fichier_xml.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<corpus>\n") #on écrit le début de notre fichier xml
			parcours(dossier,fichier_xml,fichier_txt,rubrique,doublons) #on appelle la fonction de récursivité
			fichier_xml.write("</corpus>") #on écrit la fin de notre fichier xml

if __name__=="__main__": #manière un peu spéciale pour lancer le programme
	main() #on appelle la fonction main

#######################################################
# 2ème programme #
#######################################################

#!/usr/bin/python3

import sys 
import re

regex_item = re.compile("<item><title>(.*?)<\/title>.*?<description>(.*?)<\/description>") #avec () on récupère les titres et les descriptions

def nettoyage(texte): #fonction pour ne garder que le véritable texte
	texte_net = re.sub("<!\[CDATA\[(.*?)\]\]>","\\1",texte)
	texte_net = re.sub("&nbsp;","",texte_net)
	texte_net = re.sub("[a-z]&nbsp;","",texte_net)
	texte_net = re.sub("&","E",texte_net)
	texte_net = re.sub("&amp","E",texte_net)
	return texte_net

def extractionsur1fil(dossier,output_xml, output_txt,doublons): #fonction qui écrit les titres et les descriptions dans un fichier final
		with open(dossier, "r") as  input_rss: #on ouvre le fichier à traiter
					texte = "".join(input_rss.readlines()) #on garde toutes les lignes dans une variable
					#pour chaque élément qui dans le texte correspond à la regex définie (regex_item)
					for m in re.finditer(regex_item,texte): #on cherche les titres et descriptions
						if m.group(1) not in doublons: #s'il n'est pas déjà dans la liste des doublons
							doublons.append(m.group(1)) #on l'ajoute à la liste
							titre_net = nettoyage(m.group(1)) #on appelle la fonction nettoyage sur le titre qui est le 1er argument gardé par la regex
							description_net = nettoyage(m.group(2)) #on appelle la fonction nettoyage sur la description qui est le 2eme argument gardé par la regex
							output_txt.write(f"{titre_net}\n{description_net}\n") #on écrit le titre et sa description dans le fichier txt final
							output_xml.write(f"<item><titre>\n{titre_net}</titre><description>\n{description_net}</description></item>\n") #on  écrit le titre et sa description dans le fichier xml final
							
					
if __name__=="__main__": 
	dossier=sys.argv[1] #chemin du dossier
	fichier_xml=sys.argv[2] #fichier xml final
	fichier_txt=sys.argv[3] #fichier txt final
	extractionsur1fil(dossier, fichier_xml, fichier_txt,doublons) #on appelle la fonction d'extraction des titres et descriptions
            
        

Lorsqu'on lance le programme voici ce qui s'affiche dans le terminal :



Voici les deux fichiers par rubrique en output :
- rubrique à la une : fichier txt et fichier xml.
- rubrique livres : fichier txt et fichier xml.
- rubrique cinema : fichier txt et fichier xml.

Un aperçu des résultats txt et xml :

txt :



xml :