# -*- coding: utf-8 -*-
# Analyse un dump wikisource et affiche les cas où
# deux pages font la même transclusion (même index,
# même pages de début et de fin, même sections...)
# ##########################################################
# Mode d'emploi :
# - télécharger un dump Wikisource depuis https://dumps.wikimedia.org/backup-index.html
# (chercher "frwikisource", puis choisir "All pages, current versions only")
# - décompacter le fichier quelque part sur son disque dur
# - indiquer le chemin dans la variable dumpfic ci-dessous
# (os.environ["USERPROFILE"] correspond au dossier de l'utilisateur courant)
# - lancer le programme : python dumpfic.py (durée : quelques minutes, plusieurs
# millions de pages sont traitées)
# - une liste des doublons est générée dans un fichier dont l'emplacement est
# indiqué par la variable out_doublons.
# - cette liste est rédigée en wikicode, donc il suffit de la copier dans une
# page temporaire de Wikisource pour voir le résultat.
import locale
import os
# Définir la locale française
locale.setlocale(locale.LC_ALL, 'fr_FR')
dumpid = "frwikisource-20231001-pages-meta-current.xml"
# Indiquer ici le chemin où a été décompacté le dump
dumpfic = (os.environ["USERPROFILE"] + f"/tmp/{dumpid}/{dumpid}")
# Le fichier généré sera ici :
out_doublons = (os.environ["USERPROFILE"] + "/tmp/frwikidump-doublons.txt")
# Fin de la configuration
# ##########################################################
import re
import xml.etree.ElementTree as ET
NS_PRINCIPAL = "0"
def main():
#read_debut()
find_doublons()
def read_debut():
maxlines = 10000
cpt = 0
with open(dumpfic, "r", encoding="utf-8") as fd:
while cpt < maxlines:
cpt += 1
print(fd.readline())
def fmtnb(n):
return locale.format_string("%d", n, grouping=True)
def find_doublons():
maxarticles = 10000000 # simple précaution pour éviter une boucle infinie
cpt = 0
cur_title = ""
cur_ns = None
cur_text = ""
transclusions = {} # relie un index aux pages qui l'utilisent
print("Début de la lecture du dump...")
for event, element in ET.iterparse(dumpfic):
if maxarticles >= 0 and cpt >= maxarticles:
print("Nombre maximum d'articles atteint")
break
tag = re.sub(r'^\{[^\}]*\}',
'',
element.tag)
if tag == "title":
cur_title = element.text
elif tag == "ns":
cur_ns = element.text
elif tag == "text":
if cur_ns == NS_PRINCIPAL:
if cur_text:
if cur_title == "Dictionnaire de théologie catholique/ANGE DE LA PASSION":
print(f"cur_text existe déjà {cur_title}")
cur_text += element.text
elif tag == "page":
cpt += 1
if cpt % 50000 == 0:
print(f"Traité {fmtnb(cpt)} pages...")
if cur_ns == NS_PRINCIPAL:
if not cur_text:
continue
# On ignore les cas où deux transclusions se trouvent
# dans la même page
if re.search(r"<pages\b .* > .* <pages\b .* >",
cur_text,
re.I | re.S | re.X):
continue
resm = re.search(r'<pages\b ( [^>]+ )>',
cur_text,
re.I | re.S | re.X)
if resm:
# On cherche les transclusions identiques
# (index, from, to, etc.)
pagestag = resm.group(1)
resm = re.search(r"""\b index=(?: "([^"]+)"
| '([^']+)'
| ([^\s'"]+) )""",
pagestag, re.I | re.X)
unir3 = lambda r: "".join([r.group(x) or "" for x in [1, 2, 3]])
if resm:
index = unir3(resm)
key = ""
for m in (["from", "to", "exclude", "include",
"fromsection", "tosection", "onlysection"]):
resm = re.search(r"\b " + m + r"""\s*=\s*(?: "([^"]+)"
| '([^']+)'
| ([^\s\/\>'"]+) )""",
pagestag,
re.I | re.S | re.X)
if resm:
key += f" {m}={unir3(resm)}"
key = key.strip()
if not index in transclusions:
transclusions[index] = {}
if not key in transclusions[index]:
transclusions[index][key] = []
transclusions[index][key].append(cur_title)
cur_ns = None
cur_text = ""
cur_title = ""
element.clear() # Important !
print("Fin de la lecture du dump")
with open(out_doublons, "w", encoding="utf-8") as outfd:
outfd.write("== Liste de doublons de transclusion potentiels ==\n")
outfd.write(f"''Généré à partir du dump {os.path.basename(dumpfic)} avec le script "
"[[Utilisateur:Seudo/doublons.py|doublons.py]]''\n")
cpt_doublons = 0
indexes = sorted(transclusions.keys())
cpt_index = 0
fauxpositifs = {} # Faux positifs présumés
for index in indexes:
index_printed = False
keys = sorted(transclusions[index].keys())
for key in keys:
nb_transclusions = len(transclusions[index][key])
if nb_transclusions >= 2:
if(key in ["from=1 to=1", "from=2 to=2"]):
if index not in fauxpositifs:
fauxpositifs[index] = {}
fauxpositifs[index][key] = transclusions[index][key]
else:
cpt_doublons += 1
if not index_printed:
group_index = 50
if cpt_index % group_index == 0:
outfd.write(f"\n=== Index n<sup>o</sup> "
f"{fmtnb(cpt_index + 1)} à "
f"{fmtnb(cpt_index + group_index + 1)} "
"===")
outfd.write(f"\n{fmtnb(cpt_index+1)}. "
f"[[Livre:{index}]]")
cpt_index += 1
index_printed = True
txt = key.strip()
if not txt:
txt = "(fac-similé entier)"
outfd.write(f"\n:: ''{txt}'' :"
+ "".join([f"\n::# [[{x}]]"
for x in transclusions[index][key]]))
indexes = sorted(fauxpositifs.keys())
if len(indexes) > 0:
outfd.write("\n== Cas spéciaux ==")
outfd.write("\nCas où seule la page 1 (from=1 to=1) ou 2 (from=2 to=2) est transcluse.\n");
cpt_index = 0
for index in indexes:
outfd.write(f"\n{fmtnb(cpt_index+1)}. "
f"[[Livre:{index}]]")
keys = sorted(fauxpositifs[index].keys())
for key in keys:
txt = key.strip()
if not txt:
txt = "(fac-similé entier)"
outfd.write(f"\n:: ''{txt}'' :"
+ "".join([f"\n::# [[{x}]]"
for x in fauxpositifs[index][key]]))
cpt_index += 1
print(f"Nombre de doublons potentiels détectés : {fmtnb(cpt_doublons)}")
print(f"Nombre de faux positifs présumés : {fmtnb(len(fauxpositifs.keys()))}.");
print(f"La liste des doublons potentiels a été enregistrée dans {out_doublons}")
main()