Récemment j'ai voulu ré-utilisé l'API Wikipedia avec Python. Mais celle-ci avait tellement changé, que ce soit l'API ou l'organisation de la BDD Wikipedia, que mon projet était envahi d'erreurs, que ce soit sur la pertinence des résultats, du fait de DisambiguationError ou d'autres exceptions.PageError...

J'ai donc décidé de revoir complètement ma façon d'utiliser l'API Wikipedia, ci-dessous un petit retour d'expérience.

Ce n'est pas parfait, mais ça permet de maximiser le retour de résultats, de vérifier leur pertinence, de les mettre en forme et d'éviter un certain nombre de messages d'erreur. Et surtout, ça donne une base solide sur laquelle améliorer encore sa façon d'utiliser l'API. 

Les versions utilisées sont Python 3.9 et Wikipedia 1.4.

Que cherche-t-on ?

Voici une première façon d'utiliser l'API Wikipedia.

Ici je cherche le texte Aiguille Dibonna, une montagne du massif des Écrins, dans les Alpes françaises :

import wikipedia
 
wikipedia.set_lang("fr")
 
summary = wikipedia.summary("Aiguille Dibona", sentences=10)
 
print(summary)

Si vous testez ce code, vous verrez qu'il fonctionne parfaitement. D'autant qu'un article Wikipédia existe et est très exactement intitulé... Aiguille Dibona 😎⛰️👴

https://fr.wikipedia.org/wiki/Aiguille_Dibona

L'aiguille Dibona est une aiguille culminant à 3 131 m d'altitude dans le massif des Écrins...

== Histoire ==
1913 - Première ascension, peu difficile par l'arête nord...

Mais si vous enlevez le mot Aiguille, et ne cherchez que le mot Dibona (allez-y, testez...) :

Dipoena est un genre d'araignées 🕷️ aranéomorphes de la famille des Theridiidae.

== Distribution ==
Les espèces de ce genre se rencontrent en Amérique... 😭😵😭

Il serait donc bel et bon de préciser ce que l'on cherche, par exemple en utilisant une expression généralisant le domaine de recherche. Exemple ci-dessous an ajoutant à la recherche la chaîne montagne (attention, jeu de mot 😜 !) :

import wikipedia
 
wikipedia.set_lang("fr")
 
chercherMot = 'Dibona'
aide = 'montagne'
chercher = aide + ' ' + chercherMot
 
summary = wikipedia.summary(chercher, sentences=10)
 
print(summary)

Et là OK, on retrouve notre jolie montagne !

Qui cherche-t-on ?

Reprenons notre 1er exemple mais en cherchant cette fois une montagne d'un nom très générique, exemple : l'Ourson.

import wikipedia
 
wikipedia.set_lang("fr")
 
summary = wikipedia.summary("l'Ourson", sentences=10)
 
print(summary)
wikipedia.exceptions.DisambiguationError: "Ours (homonymie)" may refer to:
Ursidés
ours
ours
ours
ours
Ours de Digne...

Wikipédia hésite fortement et déclenche une DisambiguationError : plusieurs articles Wikipédia peuvent faire référence à votre recherche (on peut plus ou moins parler d'articles homonymes).

Bien sûr nous pourrions lancer notre recherche au hasard sur l'un de ces articles :

import wikipedia
import random
 
wikipedia.set_lang("fr")
 
try:
    summary = wikipedia.summary("L'Ourson", sentences=3)
 
except wikipedia.DisambiguationError as e:
    print('\nAttention : DisambiguationError !')
    s = random.choice(e.options)
    summary = wikipedia.summary(s)
 
print(summary)

Ok, au hasard, ça marche... Mais ce n'est pas très précis 😡

Le bug BeautifulSoup

Et en plus on a une erreur GuessedAtParserWarning. Elle doit venir d'un conflit avec la bibliothèque BeautifulSoup. N'étant pas résolue à ma connaissance, je me contente de l'ignorer :

import warnings
warnings.catch_warnings()
warnings.simplefilter('ignore')

Et du coup... Qui cherche-t-on ?

Ok pour le GuessedAtParserWarning, mais quid de nos résultats hasardeux ? On est content ? Pas du tout, nous allons donc explorer un à un ces différents articles :

import wikipedia
import warnings
warnings.catch_warnings()
warnings.simplefilter('ignore')
 
wikipedia.set_lang("fr")
 
try:
    summary = wikipedia.summary("L'Ourson", sentences=3)
    print(summary)
 
except wikipedia.DisambiguationError as e:
    for p in e.options:
        try:
            summary = wikipedia.summary(p, sentences=3)
            print('\n' + summary)
        except:
            pass
Ursins est une commune suisse du canton de Vaud...

Tours (prononcé [tuʁ] ) est une ville située dans l'Ouest de la France...

Urse d’Auxerre († 507 ou 508) ou Ursus ou Ours, est le 10e évêque d'Auxerre...

Ah ! Il est donc possible d'itérer sur chaque article homonyme et de récupérer leur résumé Wikipédia.

Qu'avons nous trouvé ?

De même qu'au début de cet article nous avons ajouté une précision sur le domaine de recherche pour aider l'API, nous pouvons aussi utiliser un test logique pour vérifier la pertinence des résultats. Dans mon cas par exemple, je compte chercher des articles Wikipédia sur des sommets montagneux du massif des Écrins. Et bien je vais vérifier si le texte massif des Écrins est présent dans mes résultats :

import wikipedia
import warnings
warnings.catch_warnings()
warnings.simplefilter('ignore')
 
wikipedia.set_lang("fr")
 
try:
    summary = wikipedia.summary("L'Ourson", sentences=3)
 
except wikipedia.DisambiguationError as e:
    for p in e.options:
        try:
            print(p)
 
            summary = wikipedia.summary(p, sentences=3)
 
            if str('massif des Écrins').lower() in str(summary).lower():
                break
 
            if not str('massif des Écrins').lower() in str(summary).lower():
                summary = 'Pas pertinent'
 
        except:
            pass
 
print(summary)

Bouquet final

Bien, je ne vais pas vous faire patienter plus longtemps, ci-dessous un code qui va tirer parti de ce qu'on appris au-dessus, à savoir :

  • L'aide à la recherche
  • La vérification des résultats
  • La gestion des articles homonymes

Mais à cela nous allons ajouter :

  • La gestion des pages introuvables (exceptions.PageError)
  • La gestion de l'absence de connection internet (exceptions.ConnectionError)
  • La mise en forme du résultat
  • Le raccourcissement du résultat
import wikipedia
import requests
import warnings
import re
 
warnings.catch_warnings()
warnings.simplefilter('ignore')
 
wikipedia.set_lang('fr')
 
# On précise ici la chaîne à chercher car on l'utilisera sans doute plusieurs fois
chercherMot = "Grand Pic de la Meije"
 
# Chaîne pour aider à trouver l'article
aide = 'montagne'
 
# Chaîne pour vérification du résultat
# On la précise ici car on l'utilisera sans doute plusieurs fois
chaineTemoin = 'Massif des Écrins'
 
# Nombre de phrases à récupérer
# On le précise ici car on l'utilisera sans doute plusieurs fois
numberPhrase = 10
 
# Nombre de lettres à récupérer
numberLetter = 500
 
myWikiContent = 'Pas pertinent'
 
if chercherMot != '' and chercherMot != None :
    chercher = aide + ' ' + chercherMot
    print('\nChercher :', chercher)
 
    try:
        myWikiContent = wikipedia.summary(chercher, sentences=numberPhrase)
 
        # Vérifier la pertinence du résultat
        if str(chaineTemoin).lower() in str(myWikiContent).lower():
            pass
 
        else:
            myWikiContent = 'Pas pertinent'
 
    except wikipedia.exceptions.PageError as e:
        myWikiContent = 'Pas pertinent'
 
    except requests.exceptions.ConnectionError as e:
        myWikiContent = 'Pas pertinent'
 
    # Gestion des DisambiguationErrors
    except wikipedia.DisambiguationError as e:
        for p in e.options:
            print('Option testée : ' + p)
 
            try:
                myWikiContent = wikipedia.summary(p, sentences=numberPhrase)
 
                # Vérifier la pertinence du résultat
                if str(chaineTemoin).lower() in str(myWikiContent).lower():
                    break
 
                if not str(chaineTemoin).lower() in str(myWikiContent).lower():
                    myWikiContent = 'Pas pertinent'
 
            except wikipedia.DisambiguationError as e:
                myWikiContent = 'Pas pertinent'
 
            except wikipedia.exceptions.PageError as e:
                myWikiContent = 'Pas pertinent'
 
            except requests.exceptions.ConnectionError as e:
                myWikiContent = 'Pas pertinent'
 
    # Mise en forme du résultat
    myWikiContent = re.sub('==(.+)==', '', myWikiContent)
    myWikiContent = re.sub('\n\n', '\n', myWikiContent)
    myWikiContent = re.sub('\n\n', '\n', myWikiContent)
 
    myWikiContent = re.sub('\. ', '.\n', myWikiContent)
    myWikiContent = re.sub('\[réf\.\n', ' [réf. ', myWikiContent)
 
    # Raccourcissement du résultat
    if len(myWikiContent) > numberLetter:
        myWikiContent = myWikiContent[:numberLetter] + '...'
 
        print('\n' + str(myWikiContent))
 
    if len(myWikiContent) < numberLetter:
        print('\n' + str(myWikiContent))

Testez le code ci-dessus en recherchant L'Ourson et en vidant l'aide à la recherche, vous comprendrez mieux son intérêt !

...
 
# On précise ici la chaîne à chercher car on l'utilisera sans doute plusieurs fois
chercherMot = "l'Ourson"
 
# Chaîne pour aider à trouver l'article
aide = ''
 
...

😎👨‍💻🕵️‍♂️🤗