Wikidataficator

De Wikipast
Aller à la navigation Aller à la recherche

Résumés des fonctionnalités

Ce bot a pour but d'aller récupérer les numéros d'identification Wikidata de tous les personnages présents sur Wikipast, et les intégrer aux pages Wikipast de ces derniers.

Description technique

Le bot récupère la liste de tous les personnages présents sur Wikipast en utilisant le flag Q5. Ce flag correspond à la catégorie "human" sur Wikidata, et est inséré sur Wikipast pour tous les personnages, par le bot GallicaSPARQLBot. Une fois le flag Q5 trouvé, le Wikidataficator sélectionne les entrées les unes après les autres et regarde pour chacune d'entre elle si elle possède également le numéro d'identification de la Bibliothèque Nationale de France (ce numéro est aussi ajouté aux personnages par le bot GallicaSPARQLBot). Deux scénarios sont alors possibles :

  • Le numéro d'identification de la BNF est présent sur Wikipast. Une recherche d'après le nom du personnage est effectuée sur Wikidata, et le numéro d'identification de la BNF est comparé avec les résultats trouvés. Si le numéro de la BNF présent sur Wikidata correspond à celui de Wikipast, alors le personnage est le bon, et son numéro d'identification Wikidata est récupéré, et inséré sur sa page Wikipast.
  • Le numéro d'identification de la BNF n'est pas présent sur Wikipast. Une recherche d'après le nom du personnage est effectuée sur Wikidata, et le bot évalue ensuite la probabilité que le premier résultat soit le bon en se basant sur les critères suivants : date de naissance, date du décès. Si les informations ne correspondent pas entre Wikipast et Wikidata, le bot va comparer l'entrée suivante, et ainsi de suite, jusqu'à ce qu'il trouve une entrée correspondante. Lorsque cette dernière est trouvée, le bot récupère le numéro d'identification et l'intègre à la page Wikipast du personnage. Si aucune entrée ne correspond, ou si les dates de naissance et de décès ne sont pas disponibles sur Wikipast, le premier résultat de la liste sera sélectionné, et une vérification humaine sera nécessaire. Nous avons pris cette décision, car la plupart du temps une correspondance est trouvée dans la liste des résultats (voir section "Evaluation des performances"), et avons jugé que le fait de nécessiter quelques vérifications humaines n'était pas trop contraignant, et que cela était plus intéressant que de simplement abandonner une entrée.

Évaluation des performances

A l'heure actuelle, le bot a uniquement été testé sur les entrées issues de la page "Bibliographies". Le flag Q5 n'étant pas encore inséré, toutes les vérifications on été effectuées avec les dates de naissances et de décès.

Précision

Les résultats sont visibles sur cette page. Les résultats sont très encourageants, avec seulement 4 entrées sur 50 nécessitant une vérification humaine. Ces 4 entrées ont été vérifiées, et sont correctes. Toutes les entrées ne nécessitant pas de vérification humaine étant correctes également, la précision est, à l'heure actuelle, de 100%.

Vitesse

Pour générer les 50 entrées, notre programme prend environ 5 secondes, en utilisant du multiprocessing.

Exemple de résultats

Wikidataficator illustration.png


Code

import requests import json from bs4 import BeautifulSoup from urllib.request import urlopen from dateutil.parser import parse

def getWikidataBirthdayDeathday(number_wiki):

   response = urlopen('https://www.wikidata.org/wiki/Special:EntityData/'+number_wiki+'.json')
   page_source=json.loads(response.read())
   claims = page_source['entities'][number_wiki]['claims']
   try:
       birthdate_wikidata = claims['P569'][0]['mainsnak']['datavalue']['value']['time']
   except:
       birthdate_wikidata = " N/A"
   try:
       deathdate_wikidata = claims['P570'][0]['mainsnak']['datavalue']['value']['time']
   except:
       deathdate_wikidata = " N/A"
   return (birthdate_wikidata, deathdate_wikidata)

user='testbot' passw='dhbot2017' baseurl='http://wikipast.epfl.ch/wikipast/' summary='Wikipastbot update' name='test_bot_mionscalisi'

  1. Login request

payload={'action':'query','format':'json','utf8':,'meta':'tokens','type':'login'} r1=requests.post(baseurl + 'api.php', data=payload)

  1. login confirm

login_token=r1.json()['query']['tokens']['logintoken'] payload={'action':'login','format':'json','utf8':,'lgname':user,'lgpassword':passw,'lgtoken':login_token} r2=requests.post(baseurl + 'api.php', data=payload, cookies=r1.cookies)

  1. get edit token2

params3='?format=json&action=query&meta=tokens&continue=' r3=requests.get(baseurl + 'api.php' + params3, cookies=r2.cookies) edit_token=r3.json()['query']['tokens']['csrftoken']

edit_cookie=r2.cookies.copy() edit_cookie.update(r3.cookies)

  1. 1. retrouver le contenu et l'imprimer

url_wikipast = urlopen("http://wikipast.epfl.ch/wikipast/index.php/Biographies") source = url_wikipast.read() soup=BeautifulSoup(source,'html.parser')

wikidata_addr = 'https://www.wikidata.org/wiki/' wikidata_limit = 15

def processEntry3(a):

   wikidata_addr = 'https://www.wikidata.org/wiki/'
   wikidata_limit = 15
   content = 
   print(a.string)
        
       
   ## Looking for the date on wikipast
   
   u_d = 'http://wikipast.epfl.ch' + a.get('href')
   url_date = urlopen(u_d)
   source_date = url_date.read()
   soup_date=BeautifulSoup(source_date,'html.parser')
       
   birth_found = False
       
   for t2 in soup_date.findAll('li'):
       for n in t2.findAll(title = 'Naissance'):
           birth_found = True
           break;
       if(birth_found):
           break;
   if(birth_found):
       birthdate_wikipast = n.parent.a.text
       
   death_found = False
   for t2 in soup_date.findAll('li'):
       for n in t2.findAll(title = 'Décès'):
           for n2 in t2.findAll(class_ = 'selflink'):
               death_found = True
           break;
       if(death_found):
           break;
   if(death_found):
       deathdate_wikipast = n.parent.a.text
   else:
       for t2 in soup_date.findAll('li'):
           for n in t2.findAll(title = 'Mort'):
               for n2 in t2.findAll(class_ = 'selflink'):
                   death_found = True 
               break;
           if(death_found):
               break;
       if(death_found):
           deathdate_wikipast = n.parent.a.text
                   
       
   # WIKIDATA
   url = "https://www.wikidata.org/w/api.php?action=wbsearchentities&search="
   url = url + str(a.string.replace(' ', '_').replace('ü', 'u').replace('é', 'e').replace('è', 'e').replace('ö', 'o').replace('í', 'i'))
   
   url = url + "&language=en&limit="+str(wikidata_limit)+"&format=json"
   url = urlopen(url)
       
   source = json.load(url)
   matchFound = False
   resultFound = False # indicates that at least one page has been found
   
   for itemsQuerryNumber in range(wikidata_limit):
       
       try:
           number_wiki = source['search'][itemsQuerryNumber]['id']
       except:
           break;
       resultFound = True
       ## Looking for the date on wikidata
       (birthdate_wikidata, deathday_wikidata) = getWikidataBirthdayDeathday(number_wiki)
       #try with parse(birthdate_wikidata[1:11], "yyyy.dd.mm")
       #print(str(itemsQuerryNumber)+" "+birthdate_wikipast[0:4] + " - "+birthdate_wikidata[1:min(5,len(birthdate_wikipast)+1)].replace('-', '.'))
           
       if((birth_found and birthdate_wikidata[1:min(5,len(birthdate_wikipast)+1)].replace('-', '.') == birthdate_wikipast[0:4]) or (death_found and deathday_wikidata[1:min(5,len(deathday_wikidata)+1)].replace('-', '.') == deathdate_wikipast[0:4])):
           matchFound = True
           break
   
   if(not matchFound and resultFound):
       number_wiki = source['search'][0]['id']
       (birthdate_wikidata,deathday_wikidata) = getWikidataBirthdayDeathday(number_wiki)
   if(not resultFound):
       wikidata_addr = 
       number_wiki = 
       birthdate_wikidata = 
       birthdate_wikipast = 
       deathdate_wikipast = 
       deathday_wikidata = 
   content+= '|-'+ '\n' + '| ' + a.string + '\n'
   content+= '|[' + wikidata_addr + number_wiki + ' ' + number_wiki+']\n' 
   if(birth_found):
       content+= '|' + birthdate_wikipast + '\n'
   else:
       content+= '| -' + '\n'  
   content+= '|[[' + str((birthdate_wikidata[1:11])).replace('-', '.') + ']]\n'
   
   if(death_found):
       content+= '|'+deathdate_wikipast+'\n'
   else:
       content+= '| -' + '\n'
   content+= '|[[' + str((deathday_wikidata[1:11])).replace('-', '.') + ']]\n'
   content+= '|'+str(not matchFound)+'\n'
   return content

import multiprocessing as mp import sys sys.setrecursionlimit(10000)

%%time

joblist = [] for primitive in soup.findAll('table'):

   for a in primitive.findAll('a'):
       joblist.append(a)
       

pool = mp.Pool(20) res = pool.map(processEntry3,joblist) pool.close()

content='{| class="wikitable" \n|-\n! scope="col" | Noms\n ! scope="col" | Wikidata\n ! scope="col" | Birthdate wikipast\n ! scope="col" | Birthdate wikidata\n ! scope="col" | Deathdate wikipast\n ! scope="col" | Deathdate wikidata\n ! scope="col" | Human verification required\n'

for i in range(len(joblist)):

   content+=res[i]

content += '|}' payload={'action':'edit','assert':'user','format':'json','utf8':,'text':content,'summary':summary,'title':name,'token':edit_token} r4=requests.post(baseurl+'api.php',data=payload,cookies=edit_cookie)