{}
run-icon
main.py
import re import pywikibot from pywikibot import pagegenerators SITE = pywikibot.Site("fr", "wikipedia") TARGET_HOST = "l2tc.com" EDIT_SUMMARY = ( "Retrait de liens externes/références vers l2tc.com (site blacklisté) " "([[Wikipédia:Bot/Requêtes|requête bot]])" ) PAGE_LIST = [ "" ] # --------------------------------------------------------------------------- # Regex # --------------------------------------------------------------------------- # (?<!/)> : exclut les balises autofermantes <ref name=foo /> # sans quoi elles avalent la ligne suivante jusqu'au prochain </ref> RE_REF_DEF = re.compile( r'<ref(?P<attrs>[^>]*?)(?<!/)>(?P<content>.*?)</ref>', re.DOTALL | re.IGNORECASE, ) # Toute ligne de puce contenant l2tc.com (filtrée ensuite par _line_has_bare_l2tc) RE_BULLET_CANDIDATE = re.compile( r'^\*[^\n]*' + re.escape(TARGET_HOST) + r'[^\n]*\n?', re.IGNORECASE | re.MULTILINE, ) def _extract_name(attrs: str) -> str | None: """Extrait le name d'une balise <ref>, avec ou sans guillemets.""" m = re.search(r'\bname\s*=\s*(["\']?)(?P<n>[^"\'>\s/]+)\1', attrs, re.IGNORECASE) return m.group("n") if m else None def _re_call(name: str) -> re.Pattern: """Regex pour les appels <ref name="foo" /> (toutes variantes de guillemets).""" return re.compile( r'<ref\s+name\s*=\s*(["\']?)' + re.escape(name) + r'\1\s*/\s*>', re.IGNORECASE, ) def _line_has_bare_l2tc(line: str) -> bool: """True si l2tc.com apparaît dans la ligne EN DEHORS de toute balise <ref>.""" masked = re.sub(r'<ref[^>]*(?<!/)>.*?</ref>', '', line, flags=re.IGNORECASE | re.DOTALL) masked = re.sub(r'<ref\b[^>]*/>', '', masked, flags=re.IGNORECASE) return TARGET_HOST in masked.lower() # --------------------------------------------------------------------------- # Nettoyage # --------------------------------------------------------------------------- def clean_text(text: str) -> tuple[str, list[str]]: log: list[str] = [] names_to_delete: set[str] = set() work = text # ── Étape 1 : supprimer les définitions <ref>…l2tc…</ref> ──────────── # On relance la recherche après chaque suppression pour garder des # positions toujours valides dans `work`. while True: found = False for m in RE_REF_DEF.finditer(work): if TARGET_HOST not in m.group("content").lower(): continue found = True log.append("Référence l2tc supprimée.") name = _extract_name(m.group("attrs")) if name: names_to_delete.add(name) log.append(f" → Nom retenu : {name}") work = work[:m.start()] + work[m.end():] break # positions invalides : on repart du début if not found: break # ── Étape 2 : supprimer les appels <ref name="foo" /> associés ─────── for name in names_to_delete: cr = _re_call(name) n = len(cr.findall(work)) if n: log.append(f" → {n} appel(s) de '{name}' supprimé(s)") work = cr.sub("", work) # ── Étape 3 : supprimer les puces * contenant un lien l2tc nu ──────── # (cas liens externes sans balise <ref>) def _replace_bare_bullet(m): if _line_has_bare_l2tc(m.group(0)): log.append("Puce (* l2tc.com) supprimée.") return "" return m.group(0) work = RE_BULLET_CANDIDATE.sub(_replace_bare_bullet, work) return work, log # --------------------------------------------------------------------------- # Générateur de pages # --------------------------------------------------------------------------- def get_pages(local_args: list) -> pagegenerators.GeneratorFactory: gf = pagegenerators.GeneratorFactory(site=SITE) gf.handle_args(local_args) gen = gf.getCombinedGenerator(preload=True) if gen: return gen pywikibot.output(f"Utilisation de la liste intégrée ({len(PAGE_LIST)} articles).") return pagegenerators.PreloadingGenerator( iter(pywikibot.Page(SITE, t) for t in PAGE_LIST) ) # --------------------------------------------------------------------------- # Point d'entrée # --------------------------------------------------------------------------- def main(*args): local_args = pywikibot.handle_args(args) total = treated = changed = skipped = 0 for page in get_pages(local_args): total += 1 pywikibot.output(f"\n{'─'*60}\n[{total}] {page.title()}") try: original = page.text except pywikibot.exceptions.NoPageError: pywikibot.warning("Page inexistante.") skipped += 1 continue if TARGET_HOST not in original: pywikibot.output("Aucun lien – skip.") continue treated += 1 cleaned, entries = clean_text(original) if not entries or cleaned == original: pywikibot.output("Lien présent mais structure non reconnue.") skipped += 1 continue changed += 1 for e in entries: pywikibot.output(f" • {e}") try: page.text = cleaned page.save(summary=EDIT_SUMMARY, minor=True, watch="nochange") pywikibot.output(" ✓ Sauvegardé.") except pywikibot.exceptions.EditConflictError: pywikibot.warning(" Conflit d'édition.") skipped += 1 except pywikibot.exceptions.LockedPageError: pywikibot.warning(" Page protégée.") skipped += 1 except Exception as exc: pywikibot.error(f" Erreur : {exc}") skipped += 1 pywikibot.output( f"\n{'='*60}\n" f"Parcourues : {total} | avec l2tc.com : {treated} " f"| modifiées : {changed} | ignorées : {skipped}" ) if __name__ == "__main__": main()
Output