This is the draft website for Programming Historian lessons under review. Do not link to these pages.

For the published site, go to https://programminghistorian.org

##

Objectif de la leçon

Avant d’aller plus loin, nous avons besoin de « normaliser » la liste que nous avons créée dans la leçon Du html à une liste de mots (2). La normalisation des données est une étape importante qui consiste à préparer les données pour le traitement automatique que l’on veut leur appliquer, en leur donnant une forme que nous pourrons manipuler facilement (par exemple, normaliser des données textuelles peut nécessiter de convertir tous les caractères en minuscules ou de retirer des caractères spéciaux qui ne nous intéressent pas pour la suite). Pour cela, nous allons appliquer des méthodes de traitement des chaines de caractères, ainsi que des expressions régulières de Python. Une fois normalisées, nos données pourront être analysées plus facilement.

Fichiers nécessaires pour cette leçon

  • html-to-list-1.py
  • obo.py

Si vous n’avez pas les fichiers de la leçon précédente cités ci-dessus, vous pouvez télécharger le fichier python-lessons3.zip ici.

Nettoyer notre liste

Dans la leçon Du html à une liste de mots (2), nous avons rédigé un programme Python, html-to-list-1.py. Ce programme télécharge le contenu d’une page web, extrait le formatage et les métadonnées, puis produit une liste de « mots », comme celle ci-dessous. En réalité, ces entités sont appelées des « tokens » (jetons), plutôt que « mots ». En effet, certains de ces éléments ne sont pas du tout des « mots » à proprement parler (par exemple, l’abréviation « &c. » pour « et cetera »). D’autres peuvent aussi être considérés comme des groupes de plusieurs mots. Dans la liste qui suit, la forme possessive « Akerman’s » (en anglais) par exemple est parfois analysée par les linguistes comme deux mots : « Akerman » accompagné d’un marqueur possessif. En français, on pourrait trouver de la même façon des formes analysables comme deux mots mais récupérées comme un token unique par le programme Python (des verbes pronominaux par exemple, comme « s’élancer »).

Reprenez votre programme html-to-list-1.py et vérifiez qu’il renvoie bien quelque chose comme suit :

['324.', '\xc2\xa0', 'BENJAMIN', 'BOWSEY', '(a', 'blackmoor', ')', 'was',
'indicted', 'for', 'that', 'he', 'together', 'with', 'five', 'hundred',
'other', 'persons', 'and', 'more,', 'did,', 'unlawfully,', 'riotously,',
'and', 'tumultuously', 'assemble', 'on', 'the', '6th', 'of', 'June', 'to',
'the', 'disturbance', 'of', 'the', 'public', 'peace', 'and', 'did', 'begin',
'to', 'demolish', 'and', 'pull', 'down', 'the', 'dwelling', 'house', 'of',
'\xc2\xa0', 'Richard', 'Akerman', ',', 'against', 'the', 'form', 'of',
'the', 'statute,', '&c.', '\xc2\xa0', 'ROSE', 'JENNINGS', ',', 'Esq.',
'sworn.', 'Had', 'you', 'any', 'occasion', 'to', 'be', 'in', 'this', 'part',
'of', 'the', 'town,', 'on', 'the', '6th', 'of', 'June', 'in', 'the',
'evening?', '-', 'I', 'dined', 'with', 'my', 'brother', 'who', 'lives',
'opposite', 'Mr.', "Akerman's", 'house.', 'They', 'attacked', 'Mr.',
"Akerman's", 'house', 'precisely', 'at', 'seven', "o'clock;", 'they',
'were', 'preceded', 'by', 'a', 'man', 'better', 'dressed', 'than', 'the',
'rest,', 'who']

En soi, séparer ainsi le texte en mots n’est pas très utile, mais c’est une étape nécessaire. Nous pouvons maintenant utiliser le texte pour réaliser des mesures qui ne sont normalement pas faisables sans logiciels spécifiques. Nous allons commencer par calculer les fréquences des tokens et d’autres unités linguistiques, ce qui se fait fréquemment dans le cadre d’une analyse textuelle.

La liste aura besoin d’être nettoyée avant d’être utilisée pour mesurer des fréquences. À l’aide des méthodes vues dans la leçon précédente Du html à une liste de mots (1), essayons dans un premier temps de décrire notre algorithme avec des phrases en français. Ce que nous voulons, c’est savoir combien de fois les mots sémantiquement importants apparaissent dans notre texte. Les étapes à réaliser devraient donc ressembler à cela :

  • Convertir tous les mots en minuscules, pour que BENJAMIN et benjamin soient comptés comme un seul token
  • Retirer tout caractère qui ne ferait pas partie des caractères qui nous intéressent (les emojis ou les signes diacritiques (accents, cédilles) par exemple)
  • Compter, pour chaque mot, le nombre de fois où il apparait (son nombre d’occurrences)
  • Retirer les mots outils (stopwords), des mots à faible poids sémantique mais très courants, comme it, the, and, etc. (en français, le, et, un, etc.)

Convertir en minuscules

Généralement, les tokens sont convertis en minuscules pour faire des mesures de fréquences. C’est ce que nous allons faire en appliquant la méthode lower() à chaque token. Il s’agit d’une méthode applicable à des chaines de caractères et qui a déjà été introduite dans la leçon Manipuler des chaines de caractères en Python. Nous allons donc devoir l’appliquer à la chaine de caractères qui est renvoyée par la fonction stripTags(html) du module obo.py, dans le programme html-to-list1.py.

En effet, la fonction stripTags() du module obo.py retourne une chaine de caractère à partir des données extraites (contenues dans la variable html), que nous convertissons en minuscules avec la fonction lower(). En appliquant ainsi les deux fonctions sur une même ligne, nous gardons un code assez court tout en apportant des modifications majeures à notre programme.

Modifier html-to-list1.py pour y appliquer la méthode lower() à obo.stripTags(html) :

#html-to-list1.py
import urllib.request, urllib.error, urllib.parse, obo

url = 'http://www.oldbaileyonline.org/browse.jsp?id=t17800628-33&div=t17800628-33'

response = urllib.request.urlopen(url)
html = str(response.read().decode('UTF-8'))
text = obo.stripTags(html).lower() # ajouter la méthode applicable à une chaine de caractères ici.
wordlist = text.split()

print(wordlist)

Normalement, vous devriez obtenir la même liste de mots que précédemment, mais cette fois avec tous les caractères en minuscules.

Comme nous l’avons déjà vu, Python permet de faire beaucoup, facilement et avec peu de code !

À partir de là, nous pourrions parcourir un grand nombre d’autres entrées de Old Bailey Online et de nouvelles sources pour être sûrs qu’il n’y ait pas d’autres caractères spéciaux qui pourraient nous poser problème plus tard. Nous pourrions également anticiper toutes les situations où nous voudrions conserver la ponctuation (par exemple, pour distinguer des quantités monétaires, comme « 1300$ » ou « 1865£ », des dates, ou reconnaitre la différence entre « 1629-40 » et « 1629 40 »). C’est le travail des programmeurs professionnels : essayer de penser à tout ce qui pourrait clocher et traiter le problème en amont.

Nous allons utiliser une autre approche. Notre objectif est de développer des techniques utilisables par un historien ou une historienne en activité durant le processus de recherche. Cela signifie que nous favoriserons presque toujours des solutions approximativement correctes mais pouvant être développées rapidement. Alors plutôt que de prendre du temps tout de suite pour créer un programme solide face à l’exceptionnel, nous allons simplement nous débarrasser de tout ce qui n’est pas une lettre, accentuée ou non, ou un chiffre arabe. La programmation est par essence un processus « d’affinement pas à pas ». On commence avec un problème et le début d’une solution, puis on affine cette solution jusqu’à obtenir quelque chose qui fonctionne au mieux.

Expressions régulières en Python

Nous avons retiré les majuscules, il ne reste plus qu’à éliminer toute la ponctuation. Si on la laisse dans le texte, la ponctuation va perturber nos mesures de fréquences. En effet, nous voulons bien évidemment considérer evening? (soir) comme evening et « 1780. » comme « 1780 ».

Il est possible d’utiliser la méthode .replace() sur la chaine de caractères pour en retirer tous les types de ponctuation :

text = text.replace('[', '')
text = text.replace(']', '')
text = text.replace(',', '')
#etc...

Cependant, ce n’est pas optimal. Pour continuer à créer un programme court et puissant, nous allons utiliser ce qu’on appelle des « expressions régulières ». Les expressions régulières sont disponibles dans de nombreux langages de programmation, sous différentes formes.

Les expressions régulières permettent de rechercher des patrons lexicaux (« patterns ») bien définis et qui peuvent raccourcir drastiquement votre code. Par exemple, mettons que vous vouliez trouver une lettre de l’alphabet dans une chaine de caractères. Plutôt que de créer une boucle if/else qui vérifie si chaque caractère de la chaine correspond à « a », puis à « b », puis à « c », etc., vous pouvez vous servir d’une expression régulière pour voir si le caractère de la chaine est une lettre entre « a » et « z ». Vous pourriez aussi vous en servir pour chercher un chiffre, une lettre majuscule, un caractère alphanumérique, un retour chariot, ou encore une combinaison de ces différents éléments, et bien plus.

Dans Python, les expressions régulières sont disponibles dans un module. Ce dernier n’est pas chargé automatiquement, car il n’est pas nécessaire pour tous les programmes et le charger à chaque fois prendrait du temps inutilement. Il va donc falloir l’importer (import le module nommé re), comme vous aviez importé le module obo.py.

Comme nous ne nous intéressons qu’aux caractères alphanumériques, nous allons créer une expression régulière qui isole uniquement ces éléments, et retire tout le reste. Copiez la fonction ci-dessous et collez-la à la fin du module obo.py. Vous pouvez laisser les autres fonctions du module tranquilles, nous allons continuer à les utiliser.

# Prend une chaine de caractère text, la segmente en liste avec pour délimiteurs
# les caractères non-alphanumériques (en utilisant la définition
# Unicode des alphanumériques) ou suite de caractères non-alphanumériques
# qui sont ainsi supprimés 

def stripNonAlphaNum(text):
    import re
    return re.compile(r'\W+', re.UNICODE).split(text)

L’expression régulière dans ce code est le contenu de la chaine de caractères, autrement dit \W+. \W est le diminutif utilisé pour la classe des caractères non-alphanumériques. Dans une expression régulière Python, le signe plus + correspond à une ou plusieurs occurrences d’un caractère donné. re.UNICODE informe l’interpréteur que nous voulons inclure les caractères des autres langues du monde dans notre définition « d’alphanumériques », tout comme les lettres de A à Z, de a à z et les chiffres de 0 à 9. Les expressions régulières doivent être « compilées » avant de pouvoir être utilisées. C’est ce que fait la dernière ligne de la fonction présentée plus haut. Inutile de vous embêter à comprendre la compilation pour le moment.

Après avoir peaufiné notre programme html-to-list1.py, il doit ressembler à cela :

#html-to-list1.py
import urllib.request, urllib.error, urllib.parse, obo

url = 'http://www.oldbaileyonline.org/browse.jsp?id=t17800628-33&div=t17800628-33'

response = urllib.request.urlopen(url)
html = response.read().decode('UTF-8')
text = obo.stripTags(html).lower()
wordlist = obo.stripNonAlphaNum(text)

print(wordlist)

En exécutant le programme et en regardant ce qu’il en ressort dans le panneau Command Output, vous verrez qu’il fait plutôt du bon travail. Ce code sépare les mots composés avec un trait d’union comme coach-wheels en deux mots, et compte le possessif anglais ‘s ou la forme o’clock comme des mots distincts, en retirant l’apostrophe. Il s’agit cependant d’une approximation satisfaisante de ce que nous voulions obtenir, et nous pouvons continuer d’avancer vers nos mesures de fréquences avant d’essayer de l’améliorer. (Si les sources sur lesquelles vous travaillez sont dans plus d’une langue, vous aurez besoin d’en apprendre plus sur le standard Unicode et sur sa prise en charge Python.)

Pour aller plus loin

Si vous souhaitez pratiquer davantage les expressions régulières, le chapitre 7 de Dive into Python de Mark Pilgrim peut être un bon entrainement.

Synchronisation du code

Pour pouvoir continuer vers les leçons suivantes, il est important d’avoir les bons dossiers et les bons programmes dans votre répertoire programming-historian. À la fin de chaque chapitre de cette série de leçons, vous pouvez télécharger le fichier .zip correspondant pour être sûr.e d’avoir le bon code :