Extraire une liste d’une page Wikipédia. Exemple des stations de radio locales

Dans le cadre du projet RELOC, Réseaux sociaux et localisation du financement participatif (financement DEPS/Ministère de la Culture et de la Communication), la recherche des équipements culturels sur un territoire m’a amenée à récupérer différentes bases de données et des informations sur l’encyclopédie collaborative Wikipédia.

Extraire la liste des radios françaises de Wikipédia dans un tableur

Notre objectif est d’obtenir le nombre de radios par ville en France.

Il existe différentes manières de le faire d’après de nombreux blogs sur le web, avec google spreadsheet ou le projet BDpediaFR.

J’ai essayé à partir d’un article très bien fait sur data.hypotheses.org, celui d’Alexandre Hobeika et Étienne Ollion mais leur billet se base sur une extraction à partir d’un tableau, ce qui n’est pas le cas ici.

On va donc détailler l’intérêt de maîtriser quelques commandes basiques héritées du monde Unix pour traiter des fichiers textes. Ces commandes sont valides pour un environnement GNU/Linux (type Debian Jessie). Pour MacOS, elles peuvent légèrement varier dans leur forme ou leurs options.

Extraire le code source HTML

Enregistrer le contenu du code source html (de la page Wikipédia archivée et non modifiée par le navigateur) dans un fichier encodé en UTF-8 nommé par exemple sources dans votre répertoire personnel. Ici les extensions de fichier sont optionnelles.

On notera que le fichier obtenu est très bien formaté puisque chaque station est sur une ligne distincte. Cela va nous permettre de sauter une étape de sélection des données par xpath et de nous concentrer sur l’utilisation de méthodes de sélection et d’édition de flux qui travaillent ligne par ligne.

sources vu dans atom

sources vu dans Atom

Vous allez devoir taper des commandes dans votre Terminal. Par exemple pour accéder à votre fichier vous devez taper la commande suivante (lien si vous êtes déjà perdu) :

  • pwd : où je suis
  • ls: liste
  • cd: change directory, exemple: cd/home/personel/…

Pour cette extraction, voici les principales commandes à connaître:

  • man <command> : affiche l’aide sur une commande, alternative à Google
  • grep <pattern> [<file>] : sélectionne les lignes correspondant à un motif
  • sed <command>: c’est très réducteur, mais dans notre cas, on va dire que cette commande sert à transformer les lignes
  • le symbole pipe | sert à rediriger la sortie de la commande à gauche en tant qu’entrée de la commande de droite. Bonne chance pour le trouver sur un clavier Mac. Bon allez on est sympa on vous donne la combinaison : ALT+SHIFT+L (et après on nous répète à l’envie qu MacOS est ergonomique…)
  • while et for sont des instructions de boucle commune à beaucoup de langages de programmation impératifs (tant que vrai faire …., pour chaque élément dans … faire … )

Un bonne connaissance des expressions régulières/rationnelles est un gros plus. Si vous aimez les maths, la théorie des langages et des automates, tant mieux, sinon, une référence sur le sujet : www.rexegg.com.

Restreindre les lignes correspondantes aux stations et villes

Les lignes qui nous intéressent sont les lignes qui commencent par <li> et, de manière étonnante, elles ont toutes au moins un groupe entre parenthèses. On veut également conserver les titres intermédiaires correspondants aux départements.
On ne prend que ces lignes (commande grep) et on ne conserve que ce qui se trouve entre la première parenthèse ouvrante et la dernière parenthèse fermante de la ligne (commande sed). Le fichier créé sera nommé frequences.
# Cette commande se présente sur deux lignes grâce au back slash \ pour
# être un peu plus lisible. Elle se lit de cette manière:
# Pour chaque ligne dans le fichier sources correspondant au motif
#   0 ou plusieurs blancs (espace, tabulation) suivis de <li> suivi d'au
#   moins un caractère suivi d'au moins un caractère entre parenthèses;
# transformer cette ligne pour ne conserver que le groupe entre ( )
# et envoie tous les résultats dans un nouveau fichier nommé frequences
grep -E '^\s*(<li>.+\(.+\)|<h3><span class="mw-headline")' sources \
  | sed -E 's@[^(]+(\(.+\))[^)]+@\1@' > frequences
frequences vu dans atom

frequences vu dans Atom

On retire tous les liens sur les villes pour ne garder que les noms de communes, cela évite d’avoir des noms avec du Percent Encoding.

# Le sed transforme les lignes du fichier frequences pour retirer les
# éléments html des liens et ne conserver que le texte de la balise a
sed -i -E 's@<a href[^>]+>([^<]+)</a>@\1@g' frequences

# sous mac os, ne pas mettre -i 
# et diriger vers un nouveau fichier en ajoutant > frequences2
sed -E 's@<a href[^>]+>([^<]+)</a>@\1@g' frequences > frequences2

Extraire les noms de communes

Pour chaque ligne du fichier villes que l’on va créer, on ne garde que le contenu du dernier groupe entre parenthèses. Il faut également supprimer les lignes correspondantes aux radios émettant dans une langue régionale situées en fin de fichier. Ces stations sont déjà répertoriées dans la liste par département.

# Transforme les lignes du fichier frequences pour ne conserver que le
# dernier groupe entre ( ).
# Le résultat de cette commande est envoyé dans fichier villes
sed -E 's@^.+\(([^\)]+)\)$@\1@g' frequences > villes

Malheureusement, certaines lignes avaient un commentaire en fin de ligne entre parenthèses et non pas les noms de communes. Pour ces rares exceptions (une quinzaine), une correction manuelle s’impose. Savoir utiliser un comparateur de texte aide énormément pour cela, comme par exemple vimdiff ou kdiff3.

vimdiff frequences villes
# Pour chaque ligne où le dernier groupe n'était pas la/les commune/s
# 1. restaurer la ligne initiale avec 'dp'
# 2. corriger manuellement (CTRL-W →, mode insertion et corriger)
# 3. enregistrer les modifications (:w, CTRL-W ←, :diffupdate)

OU, Perso, sous mac, j’ai installé Diffmerge, et une ligne exceptée, ça fonctionne en mode graphique.

Cas particuliers: plusieurs villes sur la même ligne

Certaines lignes ont plusieurs villes séparées par des virgules. Là c’est plus délicat, on va lire le fichier ligne par ligne, découper chaque ligne sur le caractère virgule et pour chaque champ le réécrire dans un nouveau fichier:

# La variable IFS indique à la commande for quel est le caractère qui
# sépare les éléments, on va y placer le symbole virgule.
# Le && permet de chainer la deuxième commande si la première a réussi.
IFS="," && while read line; do for city in $(echo "$line"); do \
  printf "%s\n" "$city" >> villes_split; done; done < villes

Il reste le cas des relocalisations, Ville A puis Ville B, où on ne souhaite conserver que la dernière ville, là où est actuellement située la radio.

# Transforme les lignes du fichier villes_split correspondantes au motif
#   N'importe quoi suivi de puis d'un espace et du reste de la ligne,
#   capturé sous forme d'un groupe
# pour ne conserver que le premier groupe capturé
sed -i -E 's@^.+puis (.+)$@\1@g' villes_split

# rappel, sous mac, pas de -i et ajouter > villes_split2

Nettoyer le fichier

À ce stade le fichier villes ne contient plus qu’une commune par ligne, certaines entre parenthèses, d’autre non. On nettoie les parenthèses inutiles

# Transforme les lignes du fichier villes_split correspondantes au motif
#   Une parenthèse ouvrante suivi de blancs optionnels puis de caractères
#   qu'on capture tant qu'on ne trouve pas de parenthèse fermante
# pour ne conserver que le premier groupe capturé
sed -E 's@\(*\s*([^\)]+)\)*@\1@g' villes_split > villes_clean

# sous mac, il faut matcher la ligne complète pour pouvoir en extraire
# une partie
sed -E 's@ *\(*\s*([^\)]+)\)* *@\1@g' villes_split2 > villes_clean

Regrouper par commune

Certaines communes ayant des homonymes dans d’autres départements, on va adjoindre à chaque ville le numéro de département de sa rubrique.

while read line; do is_dpt=$(echo "$line" | grep -cE '^[0-9][0-9AB]$'); \
  if [[ $is_dpt -gt 0 ]]; then dpt="$line"; \
  else echo "$line $dpt" >> villes_dpt; fi; done < villes_clean

Maintenant pour traiter plus rapidement le fichier, on va le trier

sort villes_dpt > villes_sort

La commande uniq avec le flag -c permet de compter les doublons et peut servir à faire l’équivalent d’un SELECT count(1), ville GROUP BY ville en SQL dans ce cas précis.

uniq -c villes_sort > villes_final

Et on obtient un fichier avec <nombre de stations><espaces><ville>.

Remarque:

Une petite vérification manuelle du fichier obtenu laisse apparaître quelques erreurs qu’on corrige en se référant à la page Wikipedia.

Parmi les possibles erreurs on notera:

– L’énumérations de plusieurs villes pour une station, dont l’une des villes ne se trouve pas dans le même département: Grenoble, Annecy etc… On corrige le code département

– La duplication d’information (une même station notée deux fois sur la même ville mais dans deux départements différents, aire d’écoute?): Armentières, Lannion, Perros-Guirec, Embrun, Les Mureaux etc… On supprime le doublon

– Des stations ayant déménagées ou des stations non locales n’ayant pas de ville bien définie: Saône-et-Loire, Normandie, etc… On supprime la ligne

On effectue les corrections sur le fichier villes_sort et on relance la dernière commande.

On fera attention à ne pas créer de nouvelles erreurs, par exemple il existe bien deux Valence en France avec des radios locales, dans la Drôme (26) et dans le Tarn-et-Garonne (82).

nombre de radio / ville_dept

nombre de radio / ville_dept

Simple à importer dans n’importe quel logiciel de traitement de données ou dans ma petite base en cours sur les équipements culturels 😉

Bon test!

Cette extraction a été faite le 5 mai 2016 avec l’aide de Pierre Barnoux, informaticien.


Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *