Wikidataficator
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 : année de naissance, année 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
Entrée vérifiée :
Entrée avec trop peu d'informations :
Entrée non-existante sur Wikidata :
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='******'
baseurl='http://wikipast.epfl.ch/wikipast/'
summary='Wikipastbot update'
name='test_bot_mionscalisi'
# Login request
payload={'action':'query','format':'json','utf8':'','meta':'tokens','type':'login'}
r1=requests.post(baseurl + 'api.php', data=payload)
#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)
#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. 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)


