Liste de mots

Récemment, j’ai eu besoin de trouver une liste de mots, une liste exhaustive de mots en français. Une telle liste peut avoir plusieurs usages comme,  par exemple:

  • jouer au scrabble en solitaire
  • solutionner automatiquement des grilles de mots-croisés
  • etc

Ma recherche sur Internet ne m’a pas permis de trouver un fichier suffisamment complet mais j’ai découvert le programme CeWL qui permet de créer des listes en puisant des mots dans des sites WEB. L’idée est bonne.

CeWL est un logiciel libre écrit en Ruby mais je n’ai aucune connaissance de Ruby. J’ai donc décidé d’utiliser le même concept mais de le réaliser en Python. Mon approche initiale est de puiser tous les mots de Wikipédia en français. Pourquoi Wikipédia? La quantité des textes disponibles est impressionnante et la qualité de leurs textes est excellente (peu d’erreurs orthographiques).

La logique d’un tel programme se résume comme suit:

  1. visiter toutes (ou du moins plusieurs) pages de Wikipédia français.
  2. en extraire tous les mots affichés (ne pas tenir compte des mots HTML)
  3. filtrer pour conserver les mots appropriés au besoin
  4. éliminer les duplicata

La 1ère étape se fait facilement avec l’utilitaire wget et les 3 suivantes se font facilement en Python. Le résultat de la 1ère étape prendrait la forme de:

wget [options] fr.wikipedia.org | monFiltre > listeDeMots.txt

Le principe de wget est d’analyser une page WEB et de suivre tous les liens qu’elle contient. Chacune de ces pages contient aussi des liens qui en contiennent d’autres. En explorant tous ces liens, on réussit à explorer un site WEB en ne spécifiant qu’un seul point d’entrée.

wget -E -e robots=off -nd -r -l6 -w1 fr.wikipedia.org

La 2ième étape est plus complexe. Le fichier reçu est en langage HTML. On ne veut pas extraire des mots de la syntaxe HTML. Il faut donc localiser le texte affiché à l’intérieur du HTML. Ce pourrait être complexe car le HTML n’est pas simple.  Heureusement, Python et le module HTMLParser nous simplifieront la tâche. Il suffit d’utiliser le texte qui se trouve à l’intérieur des balises <body … </body> et à l’extérieur des balises <script … </script> et <a … </a>. Une version simplifiée serait:

class htmlParser( HTMLParser):
   def handle_starttag( self, tag, attr):
       enCours.append( tag)

   def handle_endtag( self, tag):
       for i in range( len(enCours), 0, -1):
          if enCours[i-1] == tag: del enCours[i-1]
 
   def handle_data( self, data):
      if ('body' in enCours) and not (('script' in enCours) or ('a' in enCours)):
         for c in ',.;:/(){}[]|?"%@!$&+=_\t\\': txt2 = txt2.replace( c, ' ') # tous sauf "-\'"
         mots = txt2.split()
         for mot in mots:
             # on met ici certaines exclusions ou contraintes:
             if mot.isnumeric(): continue # on ne veut pas de nombres
             mot = mot.lower(): tout en minuscules

             if not (mot in listeMots):
                 listeMots.append(mot)
                 print( mot)

L’étape 3 dépend des besoins spécifiques. Si on veut des mots pour le Scrabble, on remplacera les lettres accentuées par leur version non-accentuée et on convertira tout en majuscules (ou tout en minuscules).

L’étape 4 est facile à réaliser.

Une version plus complète est disponible ici.

Combien de pages faut-il visiter? La section française de Wikipédia contient près de 2 millions de pages. Faut-il toutes les visiter? Non! ce serait un gaspillage d’électrons libres. Plus on visite des pages moins on découvrira de mots nouveaux.  À partir d’un certain point, il n’y aura plus de nouveauté (du moins, c’est ce que je pensais).

Après 20 000 pages (plus de 3Go de trafic), ma liste s’allonge encore (elle est actuellement de plus de 700 000 mots. Malheureusement, j’avais sous-estimé le nombre de noms propres et de noms scientifiques dans les pages Wikipédia. Ma liste contient donc des milliers de noms de fleurs (en français et en latin), des dizaines de milliers de noms d’individus (tel « Éric » « Lapointe »), des centaines de milliers de noms de lieux (tel « Saint-Jean » « Matha ») et des fautes (telle ciconscriptions). Ma 1ère conclusion est que Wikipédia fournit un contenu trop encyclopédique pour mon besoin. Je referai donc le même exercice avec le contenu d’un ensemble de romans. C’est à suivre.

2ième conclusion: ce qui est simple n’est pas nécessairement performant. Le programme présenté plus haut est simple et même trop simple. Un énoncé du genre « if mot in listeMots » semble inoffensif mais lorsque la liste contient plusieurs centaines de milliers de mots, le temps d’exécution de l’énoncé est non-négligeable et devient un goulot d’étranglement. Il existe des techniques plus performantes et ce fera l’objet d’un article futur.

Aide-mémoire: commandes PDB

La majorité des programmeurs Python utilise le print pour déboguer leurs programmes. C’est simple et efficace. Mais il y a des cas plus pointus qui nécessitent un outil plus puissant. pdb en est un.

pdb est un débogueur python en ligne de commande. Il s’inspire de gdb qui est utilisé par les programmeurs de langages compilés tels C et C++.

Parce qu’on ne l’utilise pas souvent, il est facile d’oublier comment on utilise pdb. Voici donc un résumé des commandes. Les crochets [] indiquent les informations optionnelles et la barre verticale | sépare les alternatives d’un choix.

Commande Description
L [début [, fin | nombre]] Liste les lignes du code source. Sans paramètre additionnel, 11 lignes seront affichées, de part et d’autre de la ligne courante.
Le premier paramètre est le numéro de la première ligne à afficher. Le second paramètre est le numéro de la ligne de fin. Si ce second paramètre est inférieur au premier, il représente alors un nombre de lignes.
LL Liste toutes les lignes de la fonction courante. Si aucune fonction n’est en cours, liste tout le fichier courant.
N Exécute l’énoncé courant. S’il s’agit d’un appel de fonction, exécute la fonction au complet.
S Exécute l’énoncé courant. S’il s’agit d’un appel de fonction, le contexte deviendra celui de la fonction.
R Retourne au contexte précédent.
B Liste les points d’arrêts (breakpoints) actifs.

B [fichier:]ligne [condition]

Insère un point d’arrêt à la ligne spécifiée. Si une condition est spécifiée, le point d’arrêt ne sera effectif que si cette condition est vraie.
CL [numéro] Retire le point d’arrêt dont le numéro est spécifié. Si aucun n’est spécifié, retire tous les points d’arrêt.

Pour utiliser pdb, il suffit de spécifier son nom sur la ligne de commande:

python3 -m pdb monProg.py mesParamètres

On peut aussi lancer pdb si notre programme détecte une condition anormale. Il suffit d’ajouter une ligne à l’endroit désiré:

breakpoint()