Links in Wikipedia-Artikeln auf Wikidata-Basis automatisiert ergänzen

Im Wikimedians-in-Bibliotheken-Treffen sitzend fiel mir wieder ein altes To Do ein, das wir immer noch nicht umgesetzt haben, obwohl die Voraussetzungen eigentlich bestens sind. Da wir mit dem Wikiversum nicht so vertraut sind, haben wir das immer wieder aufgeschoben. Jetzt nehme ich mir mal vor, das umzusetzen und hier zu dokumentieren, falls andere ähnliche Dinge umsetzen wollen.

Ziel

Wikipedia-Einträge zu Orten in Nordrhein-Westfalen sollen automatisiert einen Link auf die entsprechende Literaturliste in der Nordrhein-Westfälischen Bibliographie (NWBib) erhalten.

Beispiel:

Literatur über Lindenthal (Köln) in der Nordrhein-Westfälischen Bibliographie

Gegebene Voraussetzungen

In der NWBib wird der Großteil der Ortsverschlagwortung seit einigen Jahren auf Basis von Wikidata gemacht. Das heißt der allergößte Teil von Einträgen in der NWBib-Raumsystematik ist bereits durch eine Q-ID identifiziert, z.B. Köln: https://nwbib.de/spatial#Q365. (Zum Hintergrund siehe How we built a spatial subject classification based on Wikidata .)

Auf Basis der jeweiligen Q-ID lässt sich dann eine Liste von NWBib-Literaturangaben über den jeweiligen Ort anzeigen, für unser Beispiel „Köln“ ist das https://nwbib.de/search?nwbibspatial=https%3A%2F%2Fnwbib.de%2Fspatial%23Q365.

In Wikidata sind die entsprechenden Orte mittels eines Statements mit der Property P6814/NWBib-ID markiert, siehe https://www.wikidata.org/wiki/Q365#P6814 für das Köln-Beispiel und diese SPARQL-Query für alle ca. 4500 Einträge.

Umsetzung

Ich sammel hier mal ein paar mir bekannte Ansätze, so etwas umzusetzen.

Wikipedia-Template erstellen und einbinden

Für Orte in der Rheinland-Pfälzischen Bibliographie wurde eine ähnliche Funktionalität bereits umgesetzt, siehe z.B. Kempenich – Wikipedia.

Die benutzte Wikipedia-Vorlage ist folgende: Vorlage:RPB ORT – Wikipedia

Nachteil: Wenn ich das richtig sehe, wurde der Link auf Basis des Templates in jedem Artikel manuell ergänzt. (Dafür hat das Lanesbibliothekszentrum Rheinand-Pfalz übrigens ein dediziertes (und verifiziertes) Wikipedia-Benutzerkonto, Plrlb.) Das sollte sich bei unserem Ansatz.

Einbindung über die bestehende Wikidata-Verknüpfung

Es gibt jede Menge Wikidata-Templates für Wikipedia, besonders relevant sind wohl jene in er Kategorie External link templates using Wikidata.

Fragen / Nächste Schritte

Ich frage mich jetzt, wie ich die nächsten Schritte am besten angehe:

  • Wie erstelle ich am besten selbst ein Link-Template?
  • Wie binde ich das Template automatisiert auf den entsprechenden ~4.500 Wikpedia-Seiten ein?

Hat jemand hier damit bereits Erfahrung?

2 „Gefällt mir“

kewle Idee Adrian! :slight_smile:

Guckst du mal: https://de.wikipedia.org/wiki/Lindenthal\_(Köln)#Weblinks

Vorlage einmal in meinem BNR https://de.wikipedia.org/wiki/Benutzer:Mfchris84/Sandbox/Vorlage:NWBib ganz simpel ohne Fallback und auch einmal auf Parameter verzichtet. QID ist ja fix vorhanden bei Orten aus NRW und Pagetitle/Articlebase als Name - könnte man ggf. durch einen optionalen Parameter reinholen, wenn man denn möchte.

Vorlage jetzt unter Vorlage:NWBib – Wikipedia verfügbar. Standardmässig, wenn im korrespondierenden Wikidata-Item die NWBibID (P6814) gesetzt ist, reicht es aus, nur {{NWBib}} einzusetzen und die Linkzeile erscheint, wie von dir gewünscht. Die Vorlage kennt aber dennoch zwei Parameter

  • NWBibID (Hier kann bei fehlendem Eintrag in Wikidata oder wenn ein Link auf irgendeiner nicht korrespondierendne Seite sinnvollerweise dennoch eingesetzt werden kann, gesetzt werden - einfachster Anwendungsfall ist das Beispiel in der Vorlagen-Doku)
  • Ort (Name ist standardmäßig das Label aus Wikidata. Hier kann ein Ortsnamen indidivudell angepasst werden - ggf. auch für grammatikalische Notwendigkeiten vgl. Siebengebirge – Wikipedia)

Einbindung in großer Zahl wäre wohl eher was für einen schnellen Bot bzw. weiß ich gerade nicht genau, ob Admins nicht auch die Möglichkeit hätten die Vorlage über bestimmte Such/Ersetzen-Funktionen zu platzieren… Laut meinem Admin-Spezi ist das die Spezialseite „Text ersetzen“ nur Bürokraten zugänglich.

PS: Es sind übrigens knapp 4.300 NWBib-IDs in WD-Items mit deWP-Artikel (https://w.wiki/Fzf5)

1 „Gefällt mir“

Das ist ja super cool, danke dir vielmals! Da hätten wir uns auf jeden Fall reinarbeiten müssen, der Quelltext der Vorlage sieht nicht leicht aus für Leute wie mich, die mit Lua nicht vertraut sind:

<onlyinclude><includeonly>Literatur über [https://nwbib.de/search?nwbibspatial=https%3A%2F%2Fnwbib.de%2Fspatial%23{{#if:{{{NWBibID|}}}|{{{NWBibID}}}|{{#invoke:Wikidata|claim|1=P6814}}}} {{#if:{{{Ort|}}}|{{{Ort}}}|{{#invoke: WLink|getArticleBase}}}} in der Nordrhein-Westfälischen Bibliographie]</includeonly></onlyinclude>

Die richtige Wikipedia-Anlaufstelle wäre wohl Wikipedia:WikiProjekt Vorlagen/Werkstatt – Wikipedia gewesen, aber die Variante über das Forum hier hat mir sehr gut gefallen. :smiley:

Für die 4.300 Bearbeitungen schauen wir uns dann bald an, wie ein entsprechender Bot eingerichtet werden kann. Help:Creating a bot - Wikipedia sieht ja schonmal gut aus. Falls jemand einen Bot kennt, der etwas Ähnliches gemacht hat, freue ich mich über Hinweise, dann könnten wir den ggf. als Vorlage nehmen.

1 „Gefällt mir“

@LibrErli ich will mich mit der Erstellung eines Bots beschäftigen. Ich finde die Help:Creating_a_bot Seite leider nicht hilfreich.

Hast du einen Tipp, vielleicht auch ein (Code)-Beispiel für Bots die templates ergänzen?

@TobiasNx und ich haben jetzt ein NWBibBot-Konto angelegt und angefangen mit pywikibot in einem PAWS Jupyter Notebook zu experimentieren. Wir melden uns, wenn wir etwas Funktionierendes vorzuweisen haben.

Hier ist schonmal eine erste Version, die gegen die test.wikipedia.org läuft.

Das vorfiltern klappt schonmal gut.

was uns fehlt ist eine smarte Variante für das Suchen und Ersetzen:

            # TODO: HOW TO REPLACE THIS IN A SMART WAY
            # test = page.text + "\n* {{NWBib}}"
            # page.put(test, summary='Bot: Test edit')
1 „Gefällt mir“

Ich hab es geschafft die Weblinks auszulesen ABER den content dieses Abschnitts zu überschreiben, schaffe ich nicht:

site = pywikibot.Site(None)
sect = textlib.extract_sections(page.text, site)
linkPos = sect.sections.index(„Weblinks“)
sect.sections[linkPos].content.strip()


Mittlerweile müsste ich weitergekommen sein:



site = pywikibot.Site(None)
sect = textlib.extract_sections(page.text, site)
linkPos = sect.sections.index(„Weblinks“)
webLinkContent = sect.sections[linkPos].content.strip()
neu = webLinkContent + „\n DAS IST EIN TEST“
new_content = page.text.replace(str(webLinkContent), str(neu))
page.put(new_content, summary=‚Bot: Test edit‘)

ABER anscheinend ist die API des Testwikis gerade inaktiv:

Sleeping for 9.4 seconds, 2026-03-05 16:25:58 WARNING: API error readonly: The wiki is currently in read-only mode. ERROR: Detected MediaWiki API exception internal_api_error_readonly: The wiki is currently in read-only mode.

:woman_shrugging:

Aktueller Script Stand:

1 „Gefällt mir“

ich nutze für Template-Ergänzungen gerne wikitextparser
und arbeite auch vielfach lieber direkt mit der MediaWiki-API, mit requests.get / post etc. (pywikibot ist mächtig und gut, nur bin ich mit dem Handling nicht immer ganz so firm)

wikitextparser erlaubt bspw für den Zweck Vorlage:NWbib die Seite nach der Vorlage gezielt zu suchen;
dazu kann man auch vorher überprüfen ob eine Section „Weblinks“ vorhanden ist (bzw. sie ggf. erzeugen)

und ja, ich hab sowas schon gemacht. (im konkreten für das oesterreichwiki.org) aber mediawiki bleibt mediawiki. :slight_smile:

import sys
from SPARQLWrapper import SPARQLWrapper, JSON
import requests
import re
import wikitextparser as wtp

endpoint_url = "https://query.wikidata.org/sparql"

query = """SELECT * WHERE {
    ?item wdt:P6228 ?rwat; wdt:P8544 ?ariadne.
}
"""
normdatenType= "p"

URL = "https://regiowiki.at/api.php"
S = requests.Session()

def get_rwatPage(result,URL):
    #print(result)
    #print(URL)
    try:
        pageid = result["rwat"]["value"]
        print(pageid)
        #https://regiowiki.at/api.php?action=query&prop=info&pageids=3897&format=json
        PARAMS = {
            #api.php?action=query&prop=revisions&titles=Pet_door&rvslots=*&rvprop=content&formatversion=2
            "action": "query",
            "pageids": pageid,
            "prop": "info",
            "format": "json"

        }
        R = S.post(URL, data=PARAMS)
        DATA = R.json()
        rwatPage = DATA["query"]["pages"][pageid]["title"]
    except:
        # Excpetion 1 - Show Input Prompt
        #rwatPage = input("Regiowiki-PageTitle:") 
        # Exception 2 - Write Problmeatic WD-Items into Log File
        f = open("checkWDID.tsv", "a")
        print(result["item"]["value"],"\tP6228\t\""+result["rwat"]["value"]+"\"",file=f)
        f.close()
        rwatPage = ""
    return rwatPage


    
def get_results(endpoint_url, query):
    user_agent = "WDQS-example Python/%s.%s" % (sys.version_info[0], sys.version_info[1])
    # TODO adjust user agent; see https://w.wiki/CX6
    sparql = SPARQLWrapper(endpoint_url, agent=user_agent)
    sparql.setQuery(query)
    sparql.setReturnFormat(JSON)
    return sparql.query().convert()


results = get_results(endpoint_url, query)


for result in results["results"]["bindings"]:
    
    rwatPage = get_rwatPage(result,URL)
    print(rwatPage)
    if rwatPage == "":
        continue

    # Step 1: GET request to fetch login token
    PARAMS_0 = {
        "action": "query",
        "meta": "tokens",
        "type": "login",
        "format": "json"
    }

    R = S.get(url=URL, params=PARAMS_0)
    DATA = R.json()

    LOGIN_TOKEN = DATA['query']['tokens']['logintoken']

    # Step 2: POST request to log in. Use of main account for login is not
    # supported. Obtain credentials via Special:BotPasswords
    # (https://www.mediawiki.org/wiki/Special:BotPasswords) for lgname & lgpassword
    PARAMS_1 = {
        "action": "login",
        "lgname": "YOUR-WIKI-USER",
        "lgpassword": "YOUR-WIKI-USER-BOTPASSWD",
        "lgtoken": LOGIN_TOKEN,
        "format": "json"
    }

    R = S.post(URL, data=PARAMS_1)

    # Step 3: GET request to fetch CSRF token
    PARAMS_2 = {
        "action": "query",
        "meta": "tokens",
        "format": "json"
    }

    R = S.get(url=URL, params=PARAMS_2)
    DATA = R.json()

    CSRF_TOKEN = DATA['query']['tokens']['csrftoken']

    #Step 4a: Get current text of wiki page
    PARAMS_3 = {
        #api.php?action=query&prop=revisions&titles=Pet_door&rvslots=*&rvprop=content&formatversion=2
        "action": "query",
        "titles": rwatPage,
        "prop": "revisions",
        "rvprop": "content",
        "formatversion": "2",
        "format": "json"

    }
    R = S.post(URL, data=PARAMS_3)
    DATA = R.json()
    currentText = DATA["query"]["pages"][0]["revisions"][0]["content"]


    # Step 4a: Prepare text for upload

    #Check if Vorlage:Normdaten is already set
    postWikiText = "" 
    parsed = wtp.parse(currentText)
    ariaString = "* {{ARIADNE|"+result["ariadne"]["value"]+"}}"
    
    templateSet = False
    for templ in parsed.templates:
        if templ.name == "ARIADNE":
            templateSet = True
            print("Vorlage:ARIADNE bereits gesetzt")
        if templ.name == "Commonscat":
            commonsCat = str(templ)

            
    
    webLinksSet = False
    if templateSet == False:
        for section in parsed.sections:
            #print(section.id)
            #print(section.title)
            if(str(section.title).strip() == 'Weblinks'):
                webLinksSet = True
                try:
                    #Link nach etwaigem commonscat-Link
                    #print(commonsCat)
                    postWikiText= re.sub(r"\{\{(C|c)ommonscat\|.*\}\}",commonsCat+"\n"+ariaString,currentText)
                except:
                    postWikiText= re.sub(r"(\=\=Weblinks\=\=)|(\=\= Weblinks =\=)","== Weblinks ==\n"+ariaString,currentText)
                    
                # Step 5: POST request to edit a page
                PARAMS_5 = {
                    "action": "edit",
                    "title": rwatPage,
                    "token": CSRF_TOKEN,
                    "bot":"true",
                    "format": "json",
                    "text": postWikiText,
                    "summary" : "Vorlage:Ariadne basierend auf Wikidata-Matching (RWAT-ID + GND-ID, mit P31 -> Q5) ergänzt"
                }
        
        if webLinksSet == False:
            # Noch keine Weblinks-Section vorhanden
            postWikiText = re.sub(r"(\=\=Einzelnachweise\=\=)|(\=\= Einzelnachweise =\=)","== Weblinks ==\n"+ariaString+"\n\n== Einzelnachweise ==",currentText)
            
            PARAMS_5 = {
                    "action": "edit",
                    "title": rwatPage,
                    "token": CSRF_TOKEN,
                    "bot":"true",
                    "format": "json",
                    "text": postWikiText,
                    "summary" : "Vorlage:Ariadne basierend auf Wikidata-Matching (RWAT-ID + GND-ID, mit P31 -> Q5) hinzugefügt"
            }
            
     
        print(webLinksSet)            
        #print(postWikiText)



    #if templateSet == False:
        R = S.post(URL, data=PARAMS_5)
        #DATA = R.json()
        #print(DATA)
1 „Gefällt mir“

So ich habe in den letzten zwei Tagen etwas lauffähiges geschrieben, dass über PAWS als Jupyther Notebook aufgesetzt ist. Hier kann man das Notebook zum Skript in Python mit Hilfe von PyWikiBot und rdflib einsehen.

Die ersten Seiten wurden auch auf der deutschen Wikipedia angepasst: Beiträge von NWBibBot – Wikipedia

Jetzt warten wir auf unseren Antrag auf das Bot Flag: Wikipedia:Bots/Anträge auf Botflag – Wikipedia

Insgesamt war das ziemlich verwirrend, wie ein Bot überhaupt für Wikipedia erstellt, beantragt und produktiv geschaltet werden kann. Ich denke, dass ich, wenn alles durch ist, ein Lessons Learned hier verfasse.

2 „Gefällt mir“

Ich habe jetzt auch noch die Vorlage:NWBib angepasst, um sie an die Vorlage der DNB anzugleichen

Dieses kleine Projekt ist nun abgeschlossen. Wir haben gerade folgende E-Mail an die NWBib-Redaktion geschickt (und planen zudem, demnächst einen Blogbeitrag inklusive Lessons Learned zu schreiben):

On 6/3/26 13:14, Adrian Pohl wrote:

Liebe Kolleg:innen,

wir freuen uns Ihnen mitteilen zu können, dass in der deutschen
Wikipedia nun mehr als 4300 Links auf NWBib-Ergebnislisten verweisen.
Konkret wurden Links auf allen Artikel-Seiten ergänzt, die einem Eintrag
in der NWBib-Raumsystematik entsprechen. Abgedeckt sind 4308 von 4570
Einträgen aus der Raumsystematik.

Um diese Links zu ergänzen wurde

  1. eine Vorlage zur einfachen Ergänzung der Links angelegt (Dank an
    Christian Erlinger, der uns im metadaten.community-Forum unterstützt
    und die erste Version der Vorlage erstellt hat.)
  2. ein NWBibBot entwickelt, der die Links automatisch in den
    jeweiligen Artikeln ergänzt (Dank an Tobias Bülte, der den Großteil der
    Arbeit un der Kommunikation mit der Wikipedia-Commnity erledigt hat.)

Alle Wikipedia-Artikel, in denen die Vorlage genutzt wird, lassen sich
hier einsehen: https://de.wikipedia.org/w/index.php?title=Spezial:Suche&search=hastemplate%3ANWBib.de+&ns0=1

Die Basis für dieses kleine Projekt hatten wir bereits vor einigen
Jahren gelegt mit dem Umstieg auf eine Wikidata-basierte Raumsystematik.
Da Wikidata und die Wikipedia verknüpft sind, ließ sich das alles
größtenteils automatisiert erledigen. (Es gab dennoch einige Probleme
mit dem Bot, der zwischenzeitlich gesperrt wurde. Wir haben auf jeden
Fall dabei eine Menge über die deutschsprachige Wikipedia gelernt.)

Die Aufrufzahlen von NWBib-Seiten aus der Wikipedia sind bereits
gestiegen. Wir werden die Entwicklung weiter beobachten und haben sogar
einen Beitrag bei der diesjährigen SWIB-Konferenz eingereicht, um von
diesem kleinen Projekt zu berichten. Das heißt, spätestens Mitte
November wird es einen Vortrag plus Foliensatz geben, der die
Aktivitäten etwas genauer beschreibt.

Viele Grüße
Adrian Pohl & Tobias Bülte

PS: Wir waren einzig nicht in der Lage auf der Seite von Uerdingen die
Verlinkung mittels der Vorlage zu ergänzen, weil die Bearbeitung der
Seite beschränkt ist. Falls unter Ihnen jemand einen aktiven
Sichterstatus auf Wikipedia hat, bitten wir darum, das nachzuholen.

PPS: Falls jemand wissen will, für welche NWbib-Ortseinträge kein
Wikipedia-Eintrag existiert. Eine Abfrage bei Wikidata listet die
Einträge auf. Fühlen Sie sich frei bei der Wikipedia für diese Orte
einen Artikel zu erstellen.

1 „Gefällt mir“

Die Aufrufzahlen von NWBib-Seiten aus der Wikipedia sind bereits
gestiegen. Wir werden die Entwicklung weiter beobachten

vgl. https://doi.org/10.1515/abitech-2025-0011 // https://www.doi.org/10.5281/zenodo.14002514 :wink:

Hier ist nun der Blogeitrag: NWBibBot – Eine Regionalbibliographie in Wikipedia sichtbar machen | lobid.org

3 „Gefällt mir“