« Wikidataficator » : différence entre les versions

De Wikipast
Aller à la navigation Aller à la recherche
Ligne 128 : Ligne 128 :
     except:
     except:
         birthdate_wikidata = " N/A"
         birthdate_wikidata = " N/A"
       
 
     try:
     try:
         deathdate_wikidata = claims['P570'][0]['mainsnak']['datavalue']['value']['time']
         deathdate_wikidata = claims['P570'][0]['mainsnak']['datavalue']['value']['time']
     except:
     except:
         deathdate_wikidata = " N/A"
         deathdate_wikidata = " N/A"
       
 
     try:
     try:
         bnfid_wikidata = claims['P268'][0]['mainsnak']['datavalue']['value']['time']
         bnfid_wikidata = claims['P268'][0]['mainsnak']['datavalue']['value']['time']
Ligne 198 : Ligne 198 :
hash = f.read()
hash = f.read()
if(not pbkdf2_sha256.verify(passw, hash)):
if(not pbkdf2_sha256.verify(passw, hash)):
print('Invalid password!')
    print('Invalid password!')
exit()
    exit()
baseurl='http://wikipast.epfl.ch/wikipast/'
baseurl='http://wikipast.epfl.ch/wikipast/'
summary='Wikidataficator update'
summary='Wikidataficator update'
Ligne 235 : Ligne 235 :
     wikidata_addr = 'https://www.wikidata.org/wiki/'
     wikidata_addr = 'https://www.wikidata.org/wiki/'
     wikidata_limit = 30
     wikidata_limit = 30
     content = ''
 
       
     content=[]
       
    import_page(content,a)
     ## Looking for the date on wikipast
 
     u_d = 'http://wikipast.epfl.ch/wikipast/index.php/' + urllib.parse.quote_plus(a)
     sleepTime = 1
    url_date = urlopen(u_d)
     if len(content) == 0:
    source_date = url_date.read()
        while len(content) == 0:
    soup_date=BeautifulSoup(source_date,'html.parser')
            time.sleep(sleepTime)
   
            import_page(content,a)
      
            print('!!! Attempt at fetching again. Will sleep for :'+str(sleepTime)+' Person name :'+a+'\n')
      
 
 
     birth_found = False
     death_found = False
     BnFID_found = False
     BnFID_found = False
     BnF_id = ""
     BnF_id = ''
     BnF_idx = 0
     for c in content:
    try:
         if (c.find('Wikidata: [') != -1):
         for t2 in soup_date.findAll('p'):
             print('Already processsed')
             if t2.getText().find('BnF ID:') == 0:
            print(c)
                BnF_id = t2.find('a').getText()
            return 0
                BnFID_found = True
        if (c.find('BnF ID:') != -1):
                break;
            print('BnF ID : '+ c.split()[-1][0:-1])
    except:
            BnF_id = c.split()[-1][0:-1]
        print("BnF ID Not FOUND : "+ a)
            BnFID_found = True
   
 
    birth_found = False 
        if(c.find('[[Naissance]] de [['+a.replace('_',' ')) != -1 or c.find('[[Naissance]] d\'[['+a.replace('_',' ')) != -1):
    for t2 in soup_date.findAll('li'):
            print('Naisance le : '+c.split('[')[2].split(']')[0])
        for n in t2.findAll(title = 'Naissance'):
            birthdate_wikipast = c.split('[')[2].split(']')[0]
            for n2 in t2.findAll(class_ = 'selflink'):
            birth_found = True
                birth_found = True
 
                break;
         if((c.find('[[Décès]] de [['+a.replace('_',' ')) != -1) or (c.find('[[Mort]] de [['+a.replace('_',' ')) != -1)or (c.find('[[Décès]] d\'[['+a.replace('_',' ')) != -1) or (c.find('[[Mort]] d\'[['+a.replace('_',' ')) != -1)):
            break;
             print('Décès le : '+c.split('[')[2].split(']')[0])
         if(birth_found):
            death_found = True
            break;
             deathdate_wikipast = c.split('[')[2].split(']')[0]               
    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;
            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
     # WIKIDATA
     url = "https://www.wikidata.org/w/api.php?action=wbsearchentities&search="
     url = "https://www.wikidata.org/w/api.php?action=wbsearchentities&search="
     #url = url + str(a.replace(' ', '_').replace('ü', 'u').replace('é', 'e').replace('è', 'e').replace('ö', 'o').replace('í', 'i'))
     #url = url + str(a.replace(' ', '_').replace('ü', 'u').replace('é', 'e').replace('è', 'e').replace('ö', 'o').replace('í', 'i'))
     url = url + urllib.parse.quote(cleanNameOfUUID(a).replace(' ', '_'))
     url = url + urllib.parse.quote(cleanNameOfUUID(a).replace(' ', '_'))
   
 
     url = url + "&language=en&limit="+str(wikidata_limit)+"&format=json"
     url = url + "&language=en&limit="+str(wikidata_limit)+"&format=json"
     url = urlopen(url)
     url = urlopen(url)
       
 
     source = json.load(url)
     source = json.load(url)


Ligne 308 : Ligne 286 :
     uncertainIdentification = False
     uncertainIdentification = False
     guess_number_wiki = -1
     guess_number_wiki = -1
   
 
     for itemsQuerryNumber in range(wikidata_limit):
     for itemsQuerryNumber in range(wikidata_limit):
       
 
         try:
         try:
             number_wiki = source['search'][itemsQuerryNumber]['id']
             number_wiki = source['search'][itemsQuerryNumber]['id']
Ligne 318 : Ligne 296 :
         ## Looking for the date on wikidata
         ## Looking for the date on wikidata
         (birthdate_wikidata, deathday_wikidata, bnfid_wikidata) = getWikidataBirthdayDeathdayBnfID(number_wiki)
         (birthdate_wikidata, deathday_wikidata, bnfid_wikidata) = getWikidataBirthdayDeathdayBnfID(number_wiki)
       
 
         birthdate_wikidata_found = (birthdate_wikidata != ' N/A')
         birthdate_wikidata_found = (birthdate_wikidata != ' N/A')
         deathday_wikidata_found = (deathday_wikidata != ' N/A')
         deathday_wikidata_found = (deathday_wikidata != ' N/A')
         bnfid_wikidata_found = (bnfid_wikidata != ' N/A')
         bnfid_wikidata_found = (bnfid_wikidata != ' N/A')
       
 
         if(BnFID_found and bnfid_wikidata_found):
         if(BnFID_found and bnfid_wikidata_found):
             if(BnF_id == bnfid_wikidata):
             if(BnF_id == bnfid_wikidata):
Ligne 339 : Ligne 317 :
                 matchFound = True
                 matchFound = True
                 break
                 break
               
 
               
 
     if(not matchFound and resultFound):
     if(not matchFound and resultFound):
         if(guess_number_wiki != -1):
         if(guess_number_wiki != -1):
Ligne 353 : Ligne 331 :
         else:
         else:
             resultFound = False
             resultFound = False
       
 
     url_name = number_wiki
     url_name = number_wiki
   
 
     if(not resultFound):
     if(not resultFound):
         url_name = "Match not found"
         url_name = "Match not found"
         number_wiki = a
         number_wiki = a


       
 
    content=[]
    import_page(content,a)
   
    sleepTime = 1
    if len(content) == 0:
        while len(content) == 0:
            time.sleep(sleepTime)
            import_page(content,a)
            print('!!! Attempt at fetching again. Will sleep for :'+str(sleepTime)+' Person name :'+a+'\n')


         '''f = open('./botoutput/error/'+a+'.txt', 'w')
         '''f = open('./botoutput/error/'+a+'.txt', 'w')
Ligne 376 : Ligne 345 :
         print('ERROR with '+a+'\n\n')
         print('ERROR with '+a+'\n\n')
         return 0'''
         return 0'''
   
 
     insertWikidataLink(' [' + wikidata_addr + number_wiki + ' ' + url_name+']',content,uncertainIdentification)
     insertWikidataLink(' [' + wikidata_addr + number_wiki + ' ' + url_name+']',content,uncertainIdentification)
     page=''
     page=''
Ligne 385 : Ligne 354 :
     if(len(content) != 0):
     if(len(content) != 0):
         r4=requests.post(baseurl+'api.php',data=payload,cookies=edit_cookie)
         r4=requests.post(baseurl+'api.php',data=payload,cookies=edit_cookie)
   
 
   
 
   
 
     f = open('./botoutput/uncertainId/'+a+'.log', 'w')
     f = open('./botoutput/uncertainId/'+a+'.log', 'w')
     if uncertainIdentification:
     if uncertainIdentification:
Ligne 394 : Ligne 363 :
         f.write('0')
         f.write('0')
     f.close()
     f.close()
   
 
     if matchFound:
     if matchFound:
         f = open('./botoutput/matchFound/'+a+'.txt', 'w')
         f = open('./botoutput/matchFound/'+a+'.txt', 'w')
Ligne 403 : Ligne 372 :
     f.write('0')
     f.write('0')
     f.close()
     f.close()
   
 
     return 0
     return 0
   
 
import pickle
import pickle


Ligne 414 : Ligne 383 :
continueProcessing = True
continueProcessing = True
while continueProcessing:
while continueProcessing:


f = open('config/startPoint.dat', 'r')
startOfFile = int(f.read())
f.close()


f = open('config/endPoint.dat', 'r')
        f = open('config/startPoint.dat', 'r')
maxFileIdx = int(f.read())
        startOfFile = int(f.read())
f.close()
        f.close()


endOfFile = min(startOfFile+450, maxFileIdx, len(joblist))
        f = open('config/endPoint.dat', 'r')
if startOfFile == maxFileIdx:
        maxFileIdx = int(f.read())
exit()
        f.close()


        endOfFile = min(maxFileIdx+50, maxFileIdx, len(joblist))
        if startOfFile == maxFileIdx:
            exit()




print('Processing from :'+str(startOfFile) + '\t\tTo : '+ str(endOfFile))


joblist1 = joblist[startOfFile:endOfFile]
        print('Processing from :'+str(startOfFile) + '\t\tTo : '+ str(endOfFile))


#processPageToAddWikidataNumber(joblist[2])
        joblist1 = joblist[startOfFile:endOfFile]


print('Running')
        #processPageToAddWikidataNumber(joblist[2])
pool = mp.Pool(4)
res = pool.map(processPageToAddWikidataNumber,joblist1)
pool.close()
stats = open('stats.txt', 'a')
size = len(joblist)
hits = sum(res)
stats.write(str(size)+" entries, " + str(hits) + " matches. " + str(hits/size*100) + "% accuracy.\n")
stats.close()


   
        print('Running')
f = open('config/startPoint.dat', 'w')
        pool = mp.Pool(4)
f.write(str(endOfFile))
        res = pool.map(processPageToAddWikidataNumber,joblist1)
f.close()
        pool.close()
        stats = open('stats.txt', 'a')
        size = len(joblist)
        hits = sum(res)
        stats.write(str(size)+" entries, " + str(hits) + " matches. " + str(hits/size*100) + "% accuracy.\n")
        stats.close()




        f = open('config/startPoint.dat', 'w')
        f.write(str(endOfFile))
        f.close()
        print('Finish match found res: ' + str(sum(res)))
        continueProcessing=False




print('Finish match found res: ' + str(sum(res)))
#awnser = input('Do you wish to continue (Y\\n):')
#if(not(awnser == '' or awnser == 'Y' or awnser == 'y')):
# continueProcessing = False
  </nowiki>
  </nowiki>

Version du 18 mai 2019 à 11:54

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 effectuant un recherche du "flag" Q5. Ce flag correspond à la catégorie "human" sur Wikidata, et indique donc qu'une entrée correspond effectivement à un personnage, et non pas à une œuvre par exemple. Le flag Q5 est inséré sur Wikipast pour tous les personnages par le bot GallicaSPARQLBot. Après avoir établi la liste de tous les personnages, le Wikidaraficator itère sur chaque entrée, en les traitant de la manière suivante :

Il recherche sur Wikidata le nom du personnage en question, et deux scénarios principaux sont alors possibles :

  • Aucun résultat de recherche n'apparaît sur Wikidata. Le bot va alors insérer "Match not found" sur Wikipast avec un lien vers la création d'une nouvelle page sur Wikidata.
  • Des résultats de recherche sont disponibles sur Wikidata.

Si tel est le cas, voici l'algorithme de recherche du Wikidataficator :

  • Sur Wikipast, le personnage possède un identifiant de la Banque Nationale de France (BNF ID, cet identifiant est également inséré par le GallicaSPARQLBot). Si un BNF ID est aussi disponible sur Wikidata, ces deux numéros sont comparés :
    • Si ces deux numéros sont égaux, le personnage est alors sélectionné, et son numéro d'identification Wikidata est inséré sur Wikipast. Le bot passe alors au personnage suivant sur Wikipast.
    • Si ces deux numéros ne sont pas égaux, le bot va regarder si les dates de naissance et de décès du personnage sur Wikidata correspondent à celles sur Wikipast. Si tel est le cas, ce personnage sera sauvegardé comme "guess", et le bot va continuer à comparer le personnage Wikipast avec les entrées suivantes sur Wikidata, afin de voir si un BNF ID correspondant peut être trouvé (ce qui serait un meilleur match, car un BNF ID est censé être unique et ne devrait pas être éronné).
  • Le BNF id n'est pas disponible sur Wikipast ou sur Wikidata. Les personnages sont alors comparés à l'aide de leur date de naissance et date de décès.
    • Si les deux dates correspondent sur Wikipast et Wikidata, le personnage sera sélectionné et son identifiant Wikidata sera inséré sur Wikipast. Le Wikidataficator passe alors au personnage Wikipast suivant.
    • Si une seule des deux dates correspond, le personnage sera sélectionné et son identifiant Wikidata sera inséré sur Wikipast, avec en plus la mention "Uncertain identification". Le Wikidataficator passe alors au personnage Wikipast suivant.
    • Si aucune des deux dates ne correspond, le Wikidataficator continue les recherches pour ce personnage Wikipast en le comparant avec l'entrée Wikidata suivante.
  • Si aucun match n'a été trouvé une fois toutes les entrées Wikidata traitées pour un personnage Wikipast donné, il y a trois possibilités :
    • Le bot avait enregistré un "guess" lors d'une comparaison de BNF ID différents. Il va donc choisir ce personnage, et insérer son numéro Wikidata sur Wikipast, ainsi que la mention "Uncertain identification".
    • Le bot n'avait enregistré aucun guess. "Match not found" sera indiqué sur Wikipast avec un lien vers la création d'une nouvelle page sur Wikidata.
    • Le personnage sur Wikipast ne possédait ni date de naissance, ni date de décès. Alors le bot va prendre le premier personnage de la liste sur Wikidata, et l'insérer sur Wikipast avec la mention "Uncertain identification".


Voir la section "Exemple de résultats" pour une illustration de ces différents résultats.

Évaluation des performances

Première version

Dans un premier temps, le bot a été testé sur les entrées issues de la page "Bibliographies". Le flag Q5 n'étant pas encore inséré à ce moment là, toutes les vérifications ont été effectuées avec les dates de naissances et de décès. Les résultats sont encore visibles sur cette page. Les résultats étaient 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 étant alors de 100%. Nous avons manuellement rajouté le flag Q5 à ces pages après coup, pour une syntaxe cohérente. Après cela, nous avons pu passer à l'étape d'après impliquant la vérification à l'aide de l'identifiant de la Banque Nationale de France.

Version finale

La version finale du bot étant la plus intéressante, voici une analyse un peu plus détaillée de ses résultats.


Le bot a d'abord été lancé sur plusieurs sous-ensembles de pages, pour éviter de rencontrer des problèmes majeurs qui affecteraient un trop grand nombre de données. En effet, le GallicaSPARQLBot ayant inséré près de 500'000 entrées sur Wikipast, nous ne voulions pas prendre le risque d'altérer autant de données. Après quelques ajustements mineurs, nous avons voulu lancer le bot sur l'ensemble des données. Cependant, nous avons rencontrés un problème indépendant de notre volonté : le serveur Wikipast était vite surchargé et ne répondait plus après quelques milliers d'entrées. Nous avons donc dû nous résoudre à les lancer petit à petit. Les statistiques que vous trouverez ci-dessous portent donc sur seulement 70'000 entrées environ.

Voici les statistiques calculées :

  • Pourcentage de "Match not found"
  • Pourcentage de "Uncertain identification"
  • Nombre de BNF id manquant sur wikidata
  • Nombre de BNF ID ne matchant pas entre Wikipast et Wikidata

Choix stratégie

  • Le BNF ID plus fort que les dates de naissance et décès :

Comme dit précedemment, un BNF ID est un numéro unique. Cela signifie que deux personnages ayant le même numéro BNF ID sont identiques, et que deux entrées ayant des BNF ID différents devraient être deux personnages différents. Cela n'étant pas le cas pour les dates de naissances, il nous a semblé naturel de d'abord se baser sur le numéro de la BNF. De plus, lors de la comparaison des dates, nous avons choisi de nous limiter à une comparaison des années, les erreurs sur les mois et les jours étant trop fréquentes.

  • La méthode du guess :

Pourquoi avoir donc instauré cette technique de guess, en cas de BNF ID différent, mais de dates de naissance et de décès similaire ? L'erreur étant humaine, il nous est arrivé à quelques reprises d'observer une erreur dans le BNF ID sur Wikidata. Ne voulant pas que ce genre d'erreurs aient trop d'impacts sur nos résultats, nous avons décidé de faire "deviner" au bot une erreur de BNF ID.

  • Uncertain identification :

Lorsque trop peu d'informations sont vérifiées entre Wikipast et Wikidata, le Wikidataficator va tout de même tenter sa chance et sélectionner l'entrée la plus probable (ayant au moins une date en commun, ou la première de la liste si aucune information concernant les dates de naissance et décès ne sont disponibles sur Wikipast). L'indication "Uncertain identification" indiquera cependant aux utilisateurs qu'il serait judicieux d'approfondir un peu la recherche pour être sûr qu'il s'agisse bien de la bonne personne. Nous pensons que cela n'est pas une trop grosse contrainte, et que ce résultat est plus intéressant qu'un simple "Match not found".

  • Match not found :

Les 500'000 entrées insérées par le GallicaSPARQLbot sont composées d'auteurs connus, et moins connus. C'est pourquoi plus ou moins la moitié de ces auteurs ne sont pas présents du tout sur Wikidata. Sans aucune personne du même nom sur Wikidata, nous n'avions donc pas le choix. C'est pour cela que le Wikidataficator insère sur Wikipast un lien direct vers la création de la page du personnage sur Wikidata.

Précision

Nous avons analysé une cinquantaine d'entrées traitées par le Wikidataficator afin de voir si les résultats étaient bien ceux attendus.


Vitesse

La phase la plus lente du bot est celle qui génère la liste des entrées à traiter (la liste de tous les personnages Wikipast). En effet, Wikipast possédant désormais plus de 500'000 personnages avec le flag Q5, le bot doit toutes les traiter. Nous avons mesuré environ 1 heure et 20 minutes pour établir la liste.

Ensuite, le traitement des entrées est relativement rapide, tant que le serveur répond. Nous avons introduit du multiprocessing afin que le bot soit effectué environ sur 6 threads. Diminuer ce nombre de threads n'a cependant pas résolu les problèmes liés au serveur Wikipast.


Analyse critique

Exemple de résultats

Entrée vérifiée grâce au numéro BnF ou aux dates de naissance/décès :

Wikidataficator exemple4.png

Entrée avec trop peu d'informations, pas de numéro BnF ni date de naissance/décès :

Wikidataficator exemple2.png

Entrée non-existante sur Wikidata :

Wikidataficator exemple3.png

Code

import requests
import json
from bs4 import BeautifulSoup
from urllib.request import urlopen
from dateutil.parser import parse
import urllib
import progressbar
import uuid
import getpass
import time
from passlib.hash import pbkdf2_sha256 
import getpass


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


def getWikidataBirthdayDeathdayBnfID(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"

    try:
        bnfid_wikidata = claims['P268'][0]['mainsnak']['datavalue']['value']['time']
    except:
        try:
            bnfid_wikidata = claims['P268'][0]['mainsnak']['datavalue']['value']
        except:
            bnfid_wikidata = " N/A"
    return (birthdate_wikidata, deathdate_wikidata, bnfid_wikidata)


def getWikidataNumberFromBnFID(bnf_id):
    try:
        url = urlopen("https://www.wikidata.org/w/api.php?action=query&list=search&srwhat=text&srsearch="+bnf_id+"&format=json")    
        source = json.load(url)
        return source['query']['search'][0]['title']
    except:
        return "-1"
#Author Robin Mamie
baseurl = 'http://wikipast.epfl.ch/wikipast/'
import lxml
content=''


def import_page(content, title):
    params = { "format":"xml", "action":"query", "prop":"revisions", "rvprop":"timestamp|user|comment|content" }
    params["titles"] = "API|%s" % urllib.parse.quote(title.replace(' ', '_'))
    qs = "&".join("%s=%s" % (k, v)  for k, v in params.items())
    url = baseurl + 'api.php?%s' % qs
    tree = lxml.etree.parse(urlopen(url))
    revs = tree.xpath('//rev')
    if revs:
        old_content = revs[-1].text.split('\n')
        listed_old_content = [x for x in old_content if x]
        content.extend(listed_old_content)


def insertWikidataLink(link,content,uncertainVerification):
    human_ver_string = ''
    if uncertainVerification:
        human_ver_string = ' \'\'Uncertain identification\'\''
    for i in range(len(content)):
        l = content[i]
        if(l.find('Wikidata: [') == -1):
            if l[10:12] == 'Q5':
                insertPoint = l.find(':') + 1
                Q5URL = ' ([https://www.wikidata.org/wiki/Q5'
                content[i] = str(l[0:insertPoint]) + Q5URL + str(l[insertPoint:]) +  str(']) ')
                l = content[i]
            if l[0:8] == 'Wikidata':
                insertionPoint = l.find(':')+1
                content[i] = str(str(l[0:insertionPoint])+str(link)+str(l[insertionPoint:])+human_ver_string)

def cleanNameOfUUID(name):
    idx = name.find("(")
    if idx != -1:
        name = name[:idx-1]
    return name


user='Wikidataficator'
passw=getpass.getpass('Password: ')
f = open('config/passwordhash.dat','r')
hash = f.read()
if(not pbkdf2_sha256.verify(passw, hash)):
    print('Invalid password!')
    exit()
baseurl='http://wikipast.epfl.ch/wikipast/'
summary='Wikidataficator update'
name='Wikidataficator'

# 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 = 30

def processPageToAddWikidataNumber(a):
    number_wiki = ''
    print(a)
    wikidata_addr = 'https://www.wikidata.org/wiki/'
    wikidata_limit = 30

    content=[]
    import_page(content,a)

    sleepTime = 1
    if len(content) == 0:
        while len(content) == 0:
            time.sleep(sleepTime)
            import_page(content,a)
            print('!!! Attempt at fetching again. Will sleep for :'+str(sleepTime)+' Person name :'+a+'\n')


    birth_found = False
    death_found = False
    BnFID_found = False
    BnF_id = ''
    for c in content:
        if (c.find('Wikidata: [') != -1):
            print('Already processsed')
            print(c)
            return 0
        if (c.find('BnF ID:') != -1):
            print('BnF ID : '+ c.split()[-1][0:-1])
            BnF_id = c.split()[-1][0:-1]
            BnFID_found = True

        if(c.find('[[Naissance]] de [['+a.replace('_',' ')) != -1 or c.find('[[Naissance]] d\'[['+a.replace('_',' ')) != -1):
            print('Naisance le : '+c.split('[')[2].split(']')[0])
            birthdate_wikipast = c.split('[')[2].split(']')[0]
            birth_found = True

        if((c.find('[[Décès]] de [['+a.replace('_',' ')) != -1) or (c.find('[[Mort]] de [['+a.replace('_',' ')) != -1)or (c.find('[[Décès]] d\'[['+a.replace('_',' ')) != -1) or (c.find('[[Mort]] d\'[['+a.replace('_',' ')) != -1)):
            print('Décès le : '+c.split('[')[2].split(']')[0])
            death_found = True
            deathdate_wikipast = c.split('[')[2].split(']')[0]                


    # WIKIDATA
    url = "https://www.wikidata.org/w/api.php?action=wbsearchentities&search="
    #url = url + str(a.replace(' ', '_').replace('ü', 'u').replace('é', 'e').replace('è', 'e').replace('ö', 'o').replace('í', 'i'))
    url = url + urllib.parse.quote(cleanNameOfUUID(a).replace(' ', '_'))

    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
    uncertainIdentification = False
    guess_number_wiki = -1

    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, bnfid_wikidata) = getWikidataBirthdayDeathdayBnfID(number_wiki)

        birthdate_wikidata_found = (birthdate_wikidata != ' N/A')
        deathday_wikidata_found = (deathday_wikidata != ' N/A')
        bnfid_wikidata_found = (bnfid_wikidata != ' N/A')

        if(BnFID_found and bnfid_wikidata_found):
            if(BnF_id == bnfid_wikidata):
                matchFound = True
                break
            else:
                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])):
                    if(guess_number_wiki == -1):
                        guess_number_wiki = number_wiki
        else:
            if((birth_found and birthdate_wikidata[1:min(5,len(birthdate_wikipast)+1)].replace('-', '.') == birthdate_wikipast[0:4]) and (death_found and deathday_wikidata[1:min(5,len(deathday_wikidata)+1)].replace('-', '.') == deathdate_wikipast[0:4])):
                matchFound = True
                break
            elif((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])):
                uncertainIdentification = True
                matchFound = True
                break


    if(not matchFound and resultFound):
        if(guess_number_wiki != -1):
            f = open('./botoutput/guess/'+a+'.log', 'w')
            f.write('1')
            f.close()
            number_wiki = guess_number_wiki
            uncertainIdentification = True
        elif(not birth_found and not death_found):
            number_wiki = source['search'][0]['id']
            uncertainIdentification = True
        else:
            resultFound = False

    url_name = number_wiki

    if(not resultFound):
        url_name = "Match not found"
        number_wiki = a



        '''f = open('./botoutput/error/'+a+'.txt', 'w')
        f.write(str(content))
        f.close()
        print('ERROR with '+a+'\n\n')
        return 0'''

    insertWikidataLink(' [' + wikidata_addr + number_wiki + ' ' + url_name+']',content,uncertainIdentification)
    page=''
    for c in content:
        page += str(c)
        page += '\n\n'
    payload={'action':'edit','assert':'user','format':'json','utf8':'','text':page,'summary':summary,'title':a.replace(' ','_'),'token':edit_token}
    if(len(content) != 0):
        r4=requests.post(baseurl+'api.php',data=payload,cookies=edit_cookie)



    f = open('./botoutput/uncertainId/'+a+'.log', 'w')
    if uncertainIdentification:
        f.write('1')
    else:
        f.write('0')
    f.close()

    if matchFound:
        f = open('./botoutput/matchFound/'+a+'.txt', 'w')
        f.write('1')
        f.close()
        return 1
    f = open('./botoutput/matchFound/'+a+'.txt', 'w')
    f.write('0')
    f.close()

    return 0

import pickle

joblist = pickle.load(open('joblist.b', 'rb'))

print('Number of jobs in joblist.b : '+str(len(joblist)))

continueProcessing = True
while continueProcessing:


        f = open('config/startPoint.dat', 'r')
        startOfFile = int(f.read())
        f.close()

        f = open('config/endPoint.dat', 'r')
        maxFileIdx = int(f.read())
        f.close()

        endOfFile = min(maxFileIdx+50, maxFileIdx, len(joblist))
        if startOfFile == maxFileIdx:
            exit()



        print('Processing from :'+str(startOfFile) + '\t\tTo : '+ str(endOfFile))

        joblist1 = joblist[startOfFile:endOfFile]

        #processPageToAddWikidataNumber(joblist[2])

        print('Running')
        pool = mp.Pool(4)
        res = pool.map(processPageToAddWikidataNumber,joblist1)
        pool.close()
        stats = open('stats.txt', 'a')
        size = len(joblist)
        hits = sum(res)
        stats.write(str(size)+" entries, " + str(hits) + " matches. " + str(hits/size*100) + "% accuracy.\n")
        stats.close()


        f = open('config/startPoint.dat', 'w')
        f.write(str(endOfFile))
        f.close()
        print('Finish match found res: ' + str(sum(res)))
        continueProcessing=False