Drei neue Spiegelabfragen schliessen die gemessene Luecke im
Akteurslexikon: landtage (1539), regierung (2003), sondergesandte (5).
Lexikon 5540 -> 8856 Akteure, Aufloesung 21,1 % -> 23,2 % ueber 5018
Items. Der Gewinn steckt in wenigen Personen - Siegmund 123, Witkoff
102, Kushner 93, Lawrow 21.
Drei Abfragen, weil Wikidata drei Wege verlangt:
landtage P31/P279*, nicht P279* - "Mitglied des Landtag
Sachsen-Anhalt" ist eine Instanz von Q1939559, keine
Unterklasse. Dazu ein schaerferer Datumsfilter: von
11464 je gewaehlten Personen tragen 9700 keinerlei
Zeitangabe, ein blosses "kein Ende gesetzt" liesse die
Weimarer Republik herein.
regierung P1308, Amt -> derzeitiger Inhaber. Ueber die Amtszeit
der Person faellt Lawrow (amtiert seit 2004) aus jedem
Datumsfenster, ohne Fenster kommen Tausende Minister
herein, deren Amtszeit nie zu Ende gepflegt wurde.
sondergesandte Der andere Weg, fuer Witkoff - sein Amt traegt kein
P1308. Beide Wege in einer Abfrage laufen in die
Zeitgrenze der WDQS.
Kushner steht in hand.json: sein Amt endete 2021, seine Rolle nicht.
Die Erweiterung hat zuerst Schaden angerichtet. Mit den Landtagen kamen
Janine und Heiner Merz ins Lexikon, "Merz" wurde mehrdeutig, und 125
Nennungen des Bundeskanzlers fielen aus. Aus jedem "Hamburg" wurde eine
Kultusministerin (22-mal), aus "KI" ein Nachname (16-mal). Daher sieben
neue Sperreintraege und eine dritte Regel in waehle(): traegt genau ein
Kandidat den niedrigsten Rang, gewinnt er. Der Rang kommt aus der
Spiegelabfrage, Staatsfuehrung vor Bundestag vor Landtag - keine
Wichtigkeitsordnung, sondern eine Erwartung darueber, wen eine deutsche
Schlagzeile mit blossem Nachnamen meint. Abschlag 0,15, Grund steht in
der Nutzlast. Die Amtstabelle bekommt den schlechtesten Rang: sie reicht
bis 1794 zurueck, mit Rang 0 wurde aus "Teufel" ein
Ministerpraesident a. D.
Dabei fiel ein Loch im Fingerabdruck auf: NACHNAMEN_SPERRE stand nicht
in EINSTELLUNGEN_FEST. Ein Eintrag mehr aenderte das Ergebnis von 4868
auf 4820 aufgeloeste Nennungen unter derselben coder_version - genau
der Fehler, den die Version verhindern soll. Sperrliste und Rangordnung
gehen jetzt als Hash ein.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
368 lines
16 KiB
Python
368 lines
16 KiB
Python
"""Akteurslexikon: Wikidata spiegeln, Spiegel laden.
|
|
|
|
Zwei streng getrennte Betriebsarten, und die Trennung ist der Zweck der
|
|
Datei:
|
|
|
|
lexikon.py --spiegeln fragt Wikidata ab und schreibt lexikon/*.json
|
|
lexikon.py --laden liest lexikon/*.json und fuellt die Tabellen
|
|
|
|
Nur --spiegeln fasst das Netz an, und es laeuft von Hand, nicht im Takt.
|
|
Der Slot-Lauf der Phase 2 sieht Wikidata nie. Das ist keine Feinheit: die
|
|
Wikidata Query Service ist ein von Spenden getragener oeffentlicher Dienst,
|
|
und eine Pipeline, die alle 15 Minuten dagegenlaeuft, ist Schmarotzertum.
|
|
|
|
Der Spiegel ist eine getreue Abschrift, keine Auslegung. Was in
|
|
lexikon/*.json steht, steht so in Wikidata; jede Ableitung - Kurzformen,
|
|
Rollenzuordnung, Normalisierung - geschieht beim Laden und ist damit
|
|
nachvollziehbar und wiederholbar, ohne erneut abzufragen.
|
|
"""
|
|
|
|
import argparse
|
|
import datetime
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import pathlib
|
|
import sys
|
|
import time
|
|
import urllib.error
|
|
import urllib.parse
|
|
import urllib.request
|
|
|
|
VERZEICHNIS = pathlib.Path(__file__).parent / "lexikon"
|
|
ENDPUNKT = "https://query.wikidata.org/sparql"
|
|
|
|
# Die Nutzungsrichtlinie der WDQS bittet um einen sprechenden User-Agent mit
|
|
# Kontaktangabe. Hier steht die Impressumsadresse des Projekts - eine
|
|
# oeffentliche Adresse, die genau dafuer da ist.
|
|
KENNUNG = os.environ.get(
|
|
"WDQS_USER_AGENT",
|
|
"wurzelwerk-p2/0.1 (Datenjournalismus-Pipeline Wurzelwerkt; stress@bnd.wtf)",
|
|
)
|
|
|
|
# Pause zwischen zwei Abfragen. Die WDQS erlaubt mehr, aber wir haben es
|
|
# nicht eilig - der Spiegel entsteht einmal und liegt dann Monate.
|
|
PAUSE = 2.0
|
|
VERSUCHE = 3
|
|
ZEITGRENZE = 120
|
|
|
|
# Stichtag fuer Personen: wer vor 2017 aus allen Aemtern ausgeschieden ist,
|
|
# taucht in Nachrichten von 2026 hoechstens als Rueckblick auf. Die Grenze
|
|
# ist willkuerlich, aber sie steht hier und nicht verstreut in Abfragen.
|
|
STICHTAG = "2017-01-01"
|
|
|
|
# Anfang der Wahlperioden, die 2026 noch nachwirken. Dient als Untergrenze
|
|
# fuer laufende Mandate ohne Endedatum - siehe die Landtagsabfrage.
|
|
WAHLPERIODE = "2016-01-01"
|
|
|
|
# Aemter, die in deutschen Schlagzeilen ohne Namen auftauchen ("der
|
|
# Kanzler", "die Aussenministerin"). Handverlesen, weil eine generische
|
|
# Abfrage hier tausende Aemter liefert, von denen keines je in einer
|
|
# Schlagzeile steht. Das ist die "manuelle Ergaenzung" aus der Spezifikation.
|
|
AEMTER = {
|
|
"Q4970706": ("amt:bundeskanzler", "Bundeskanzler", "GOV"),
|
|
"Q25223": ("amt:bundespraesident", "Bundespraesident", "GOV"),
|
|
"Q56024": ("amt:bundestagspraesident", "Praesident des Deutschen Bundestages", "LEG"),
|
|
"Q568605": ("amt:aussenminister", "Bundesminister des Auswaertigen", "GOV"),
|
|
"Q22575628": ("amt:innenminister", "Bundesminister des Innern", "GOV"),
|
|
"Q22575092": ("amt:finanzminister", "Bundesminister der Finanzen", "GOV"),
|
|
"Q23016036": ("amt:verteidigungsminister", "Bundesminister der Verteidigung", "GOV"),
|
|
"Q30533368": ("amt:gesundheitsminister", "Bundesminister fuer Gesundheit", "GOV"),
|
|
"Q30543924": ("amt:verkehrsminister", "Bundesminister fuer Verkehr", "GOV"),
|
|
"Q30533351": ("amt:arbeitsminister", "Bundesminister fuer Arbeit und Soziales", "GOV"),
|
|
"Q30544097": ("amt:umweltminister", "Bundesminister fuer Umwelt", "GOV"),
|
|
# Justiz und Wirtschaft fuehrt Wikidata je Zuschnitt als eigenes Amt.
|
|
# Fuer uns ist es dasselbe Amt: die Ressortnamen wechseln, die Rolle in
|
|
# der Schlagzeile nicht. Mehrere QIDs zeigen deshalb auf eine amt_id.
|
|
"Q30533247": ("amt:justizminister", "Bundesminister der Justiz", "GOV"),
|
|
"Q30542236": ("amt:justizminister", "Bundesminister der Justiz", "GOV"),
|
|
"Q30543192": ("amt:wirtschaftsminister", "Bundesminister fuer Wirtschaft", "GOV"),
|
|
"Q30533307": ("amt:wirtschaftsminister", "Bundesminister fuer Wirtschaft", "GOV"),
|
|
"Q30319173": ("amt:wirtschaftsminister", "Bundesminister fuer Wirtschaft", "GOV"),
|
|
# Regierungschefs der Laender. Jedes Land fuehrt sein eigenes Amt als
|
|
# eigenes Wikidata-Objekt; einen gemeinsamen Oberbegriff, ueber den sich
|
|
# alle sechzehn einsammeln liessen, gibt es nicht. Also stehen sie hier.
|
|
"Q30968305": ("amt:mp_sachsen_anhalt", "Ministerpraesident Sachsen-Anhalts", "GOV"),
|
|
"Q30569887": ("amt:mp_sachsen", "Ministerpraesident Sachsens", "GOV"),
|
|
"Q30584228": ("amt:mp_thueringen", "Ministerpraesident Thueringens", "GOV"),
|
|
"Q30562601": ("amt:mp_brandenburg", "Ministerpraesident Brandenburgs", "GOV"),
|
|
"Q14917672": ("amt:mp_mecklenburg_vorpommern", "Ministerpraesident Mecklenburg-Vorpommerns", "GOV"),
|
|
"Q15303555": ("amt:mp_bayern", "Ministerpraesident Bayerns", "GOV"),
|
|
"Q31096505": ("amt:mp_baden_wuerttemberg", "Ministerpraesident Baden-Wuerttembergs", "GOV"),
|
|
"Q30534793": ("amt:mp_nordrhein_westfalen", "Ministerpraesident Nordrhein-Westfalens", "GOV"),
|
|
"Q31524732": ("amt:mp_niedersachsen", "Niedersaechsischer Ministerpraesident", "GOV"),
|
|
"Q1615935": ("amt:mp_hessen", "Ministerpraesident Hessens", "GOV"),
|
|
"Q15720142": ("amt:mp_rheinland_pfalz", "Ministerpraesident von Rheinland-Pfalz", "GOV"),
|
|
"Q31068298": ("amt:mp_saarland", "Ministerpraesident des Saarlandes", "GOV"),
|
|
"Q30263013": ("amt:mp_schleswig_holstein", "Ministerpraesident Schleswig-Holsteins", "GOV"),
|
|
"Q641159": ("amt:rb_berlin", "Regierender Buergermeister von Berlin", "GOV"),
|
|
"Q1362118": ("amt:eb_hamburg", "Erster Buergermeister von Hamburg", "GOV"),
|
|
"Q31388880": ("amt:b_bremen", "Bremer Buergermeister", "GOV"),
|
|
}
|
|
|
|
_PERSONEN_RUMPF = """
|
|
?p rdfs:label ?name . FILTER (lang(?name) = "de")
|
|
OPTIONAL { ?p skos:altLabel ?alias . FILTER (lang(?alias) = "de") }
|
|
OPTIONAL { ?p wdt:P102 ?pt . ?pt rdfs:label ?ptl . FILTER (lang(?ptl) = "de") }
|
|
"""
|
|
|
|
ABFRAGEN = {
|
|
# Bundestagsabgeordnete, alle seit dem Stichtag amtierenden.
|
|
"bundestag": """
|
|
SELECT ?p ?name (GROUP_CONCAT(DISTINCT ?alias; separator="|") AS ?aliase)
|
|
(SAMPLE(?ptl) AS ?partei)
|
|
WHERE {
|
|
?p p:P39 ?s . ?s ps:P39 ?a . ?a wdt:P279* wd:Q1939555 .
|
|
OPTIONAL { ?s pq:P582 ?ende . }
|
|
FILTER (!BOUND(?ende) || ?ende >= "%(stichtag)s"^^xsd:dateTime)
|
|
%(rumpf)s
|
|
} GROUP BY ?p ?name
|
|
""",
|
|
# Parteien in Deutschland, Oesterreich und der Schweiz.
|
|
"parteien": """
|
|
SELECT ?p ?name (GROUP_CONCAT(DISTINCT ?alias; separator="|") AS ?aliase)
|
|
(SAMPLE(?landc) AS ?land)
|
|
WHERE {
|
|
?p wdt:P31/wdt:P279* wd:Q7278 .
|
|
?p wdt:P17 ?l . VALUES ?l { wd:Q183 wd:Q40 wd:Q39 }
|
|
OPTIONAL { ?l wdt:P298 ?landc . }
|
|
?p rdfs:label ?name . FILTER (lang(?name) = "de")
|
|
OPTIONAL { ?p skos:altLabel ?alias . FILTER (lang(?alias) = "de") }
|
|
FILTER NOT EXISTS { ?p wdt:P576 ?aufgeloest . FILTER (?aufgeloest < "%(stichtag)s"^^xsd:dateTime) }
|
|
} GROUP BY ?p ?name
|
|
""",
|
|
# Souveraene Staaten mit ISO-3166-1 alpha-3.
|
|
"staaten": """
|
|
SELECT ?p ?name (GROUP_CONCAT(DISTINCT ?alias; separator="|") AS ?aliase) ?land
|
|
WHERE {
|
|
?p wdt:P31 wd:Q3624078 ; wdt:P298 ?land .
|
|
FILTER NOT EXISTS { ?p wdt:P576 ?ende . }
|
|
?p rdfs:label ?name . FILTER (lang(?name) = "de")
|
|
OPTIONAL { ?p skos:altLabel ?alias . FILTER (lang(?alias) = "de") }
|
|
} GROUP BY ?p ?name ?land
|
|
""",
|
|
# Amtierende Staats- und Regierungschefs aller Staaten. Ohne sie fehlen
|
|
# die haeufigsten Personen des Korpus, denn im Bundestag sass keiner von
|
|
# ihnen. Nur der aktuelle Stand - Vorgaenger holt die Amtstabelle.
|
|
"staatsfuehrung": """
|
|
SELECT ?p ?name (GROUP_CONCAT(DISTINCT ?alias; separator="|") AS ?aliase) ?land
|
|
WHERE {
|
|
?l wdt:P31 wd:Q3624078 ; wdt:P298 ?land .
|
|
{ ?l wdt:P35 ?p } UNION { ?l wdt:P6 ?p }
|
|
?p rdfs:label ?name . FILTER (lang(?name) = "de")
|
|
OPTIONAL { ?p skos:altLabel ?alias . FILTER (lang(?alias) = "de") }
|
|
} GROUP BY ?p ?name ?land
|
|
""",
|
|
# Landtagsabgeordnete aller sechzehn Laender. Die Landeslisten sind
|
|
# nicht ueber Unterklassen erreichbar: "Mitglied des Landtag
|
|
# Sachsen-Anhalt" ist eine *Instanz* von Q1939559, keine Unterklasse.
|
|
# Deshalb P31/P279* statt P279* wie beim Bundestag.
|
|
#
|
|
# Der Datumsfilter ist schaerfer als beim Bundestag, und das mit Grund:
|
|
# von den 11464 Personen, die je in einem Landtag sassen, tragen 9700
|
|
# ueberhaupt keine Zeitangabe. Ein blosses "kein Ende gesetzt" liesse
|
|
# die Abgeordneten der Weimarer Republik herein. Es zaehlt also, wer
|
|
# nachweislich nach dem Stichtag ausgeschieden ist oder wer ohne
|
|
# Endedatum seit der Wahlperiode ab 2016 im Amt steht.
|
|
"landtage": """
|
|
SELECT ?p ?name (GROUP_CONCAT(DISTINCT ?alias; separator="|") AS ?aliase)
|
|
(SAMPLE(?ptl) AS ?partei)
|
|
WHERE {
|
|
?a wdt:P31/wdt:P279* wd:Q1939559 .
|
|
?p p:P39 ?s . ?s ps:P39 ?a .
|
|
OPTIONAL { ?s pq:P580 ?von . }
|
|
OPTIONAL { ?s pq:P582 ?bis . }
|
|
FILTER (?bis >= "%(stichtag)s"^^xsd:dateTime
|
|
|| (!BOUND(?bis) && ?von >= "%(wahlperiode)s"^^xsd:dateTime))
|
|
%(rumpf)s
|
|
} GROUP BY ?p ?name
|
|
""",
|
|
# Amtierende Regierungsmitglieder unterhalb der Staatsspitze, weltweit.
|
|
# Ohne sie fehlt Lawrow - er sass in keinem Bundestag und fuehrt keinen
|
|
# Staat.
|
|
#
|
|
# Ueber P1308, das vom Amt auf seinen derzeitigen Inhaber zeigt. Der
|
|
# naheliegende Weg ueber die Amtszeit der Person taugt nicht: wer wie
|
|
# Lawrow seit 2004 amtiert, faellt aus jedem Datumsfenster, und ohne
|
|
# Fenster kommen Tausende Minister herein, deren Amtszeit nie zu Ende
|
|
# gepflegt wurde. P1308 ist Wikidatas eigene Aussage darueber, wer
|
|
# gerade im Amt ist, und damit die ehrlichere Quelle.
|
|
#
|
|
# Minister nur zwei Ebenen tief: "russischer Aussenminister" ist eine
|
|
# Unterklasse von "Aussenminister", das wiederum direkt von Q83307.
|
|
# Das volle P279* laeuft in die Zeitgrenze der WDQS - 9122 Aemter, und
|
|
# der Kreuzschnitt mit den Amtstraegern bricht ab.
|
|
"regierung": """
|
|
SELECT ?p ?name (GROUP_CONCAT(DISTINCT ?alias; separator="|") AS ?aliase)
|
|
(SAMPLE(?landc) AS ?land)
|
|
WHERE {
|
|
?a wdt:P279/wdt:P279? wd:Q83307 ; wdt:P1308 ?p .
|
|
OPTIONAL { ?a wdt:P1001 ?l . ?l wdt:P298 ?landc . }
|
|
?p rdfs:label ?name . FILTER (lang(?name) = "de")
|
|
OPTIONAL { ?p skos:altLabel ?alias . FILTER (lang(?alias) = "de") }
|
|
} GROUP BY ?p ?name
|
|
""",
|
|
# Sondergesandte. Eine eigene Abfrage, weil sie den anderen Weg
|
|
# brauchen: Witkoffs Amt traegt kein P1308, seine Amtszeit dagegen
|
|
# steht sauber da. Die Klasse ist klein und flach, hier ist P279*
|
|
# billig und ein Datumsfenster unnoetig - es gibt vier amtierende.
|
|
"sondergesandte": """
|
|
SELECT ?p ?name (GROUP_CONCAT(DISTINCT ?alias; separator="|") AS ?aliase)
|
|
(SAMPLE(?landc) AS ?land)
|
|
WHERE {
|
|
?a (wdt:P31|wdt:P279)/wdt:P279* wd:Q117403755 .
|
|
?p p:P39 ?s . ?s ps:P39 ?a .
|
|
FILTER NOT EXISTS { ?s pq:P582 ?bis . }
|
|
OPTIONAL { ?a wdt:P1001 ?l . ?l wdt:P298 ?landc . }
|
|
?p rdfs:label ?name . FILTER (lang(?name) = "de")
|
|
OPTIONAL { ?p skos:altLabel ?alias . FILTER (lang(?alias) = "de") }
|
|
} GROUP BY ?p ?name
|
|
""",
|
|
# Boersennotierte Unternehmen aus den Leitindizes. Sie sind Akteure wie
|
|
# Parteien es sind - "VW streicht Stellen" ist eine Handlung. Ueber die
|
|
# Indexmitgliedschaft statt ueber "Unternehmen in Deutschland", weil
|
|
# letzteres Hunderttausende liefert und keine Auswahl trifft.
|
|
"unternehmen": """
|
|
SELECT ?p ?name (GROUP_CONCAT(DISTINCT ?alias; separator="|") AS ?aliase) (SAMPLE(?landc) AS ?land)
|
|
WHERE {
|
|
?p wdt:P361 ?idx .
|
|
VALUES ?idx { wd:Q155718 wd:Q457539 wd:Q457540 wd:Q642856 wd:Q319415
|
|
wd:Q180816 wd:Q242345 }
|
|
OPTIONAL { ?p wdt:P17 ?l . ?l wdt:P298 ?landc . }
|
|
?p rdfs:label ?name . FILTER (lang(?name) = "de")
|
|
OPTIONAL { ?p skos:altLabel ?alias . FILTER (lang(?alias) = "de") }
|
|
} GROUP BY ?p ?name
|
|
""",
|
|
# Internationale Organisationen.
|
|
"organisationen": """
|
|
SELECT ?p ?name (GROUP_CONCAT(DISTINCT ?alias; separator="|") AS ?aliase)
|
|
WHERE {
|
|
?p wdt:P31/wdt:P279* wd:Q484652 .
|
|
FILTER NOT EXISTS { ?p wdt:P576 ?ende . }
|
|
?p rdfs:label ?name . FILTER (lang(?name) = "de")
|
|
OPTIONAL { ?p skos:altLabel ?alias . FILTER (lang(?alias) = "de") }
|
|
} GROUP BY ?p ?name
|
|
""",
|
|
}
|
|
|
|
# Amtsinhaber je Amt, mit Anfang und Ende. Ein Amt je Abfrage - das ist
|
|
# langsamer, aber jede Antwort bleibt klein und nachvollziehbar.
|
|
ABFRAGE_INHABER = """
|
|
SELECT ?p ?name ?von ?bis WHERE {
|
|
?p p:P39 ?s . ?s ps:P39 wd:%(qid)s .
|
|
OPTIONAL { ?s pq:P580 ?von . }
|
|
OPTIONAL { ?s pq:P582 ?bis . }
|
|
?p rdfs:label ?name . FILTER (lang(?name) = "de")
|
|
}
|
|
"""
|
|
|
|
|
|
def _abfragetext(name):
|
|
if name in ABFRAGEN:
|
|
return ABFRAGEN[name] % {
|
|
"stichtag": STICHTAG,
|
|
"wahlperiode": WAHLPERIODE,
|
|
"rumpf": _PERSONEN_RUMPF,
|
|
}
|
|
raise KeyError(name)
|
|
|
|
|
|
def frage(sparql):
|
|
"""Eine SPARQL-Abfrage, mit Wiederholung bei 429 und 5xx."""
|
|
ziel = ENDPUNKT + "?" + urllib.parse.urlencode({"query": sparql, "format": "json"})
|
|
anfrage = urllib.request.Request(
|
|
ziel,
|
|
headers={"Accept": "application/sparql-results+json", "User-Agent": KENNUNG},
|
|
)
|
|
for versuch in range(1, VERSUCHE + 1):
|
|
try:
|
|
with urllib.request.urlopen(anfrage, timeout=ZEITGRENZE) as antwort:
|
|
return json.load(antwort)["results"]["bindings"]
|
|
except urllib.error.HTTPError as fehler:
|
|
if fehler.code not in (429, 500, 502, 503, 504) or versuch == VERSUCHE:
|
|
raise
|
|
# Retry-After ernst nehmen, sonst geometrisch zurueckweichen.
|
|
warte = float(fehler.headers.get("Retry-After") or 0) or PAUSE * 2**versuch
|
|
print(f" HTTP {fehler.code}, warte {warte:.0f}s", file=sys.stderr)
|
|
time.sleep(warte)
|
|
raise RuntimeError("unerreichbar")
|
|
|
|
|
|
def _werte(bindungen):
|
|
"""SPARQL-Bindungen auf schlichte Dicts eindampfen."""
|
|
return [{k: v["value"] for k, v in b.items()} for b in bindungen]
|
|
|
|
|
|
def spiegeln(nur=None, erneuern=False):
|
|
VERZEICHNIS.mkdir(exist_ok=True)
|
|
stand = datetime.date.today().isoformat()
|
|
manifest = {}
|
|
manifestdatei = VERZEICHNIS / "MANIFEST.json"
|
|
if manifestdatei.exists():
|
|
manifest = json.loads(manifestdatei.read_text())
|
|
|
|
auftraege = [(n, _abfragetext(n)) for n in ABFRAGEN if not nur or n in nur]
|
|
if not nur or "aemter" in nur:
|
|
auftraege.append(("aemter", None))
|
|
|
|
for name, sparql in auftraege:
|
|
datei = VERZEICHNIS / f"{name}.json"
|
|
if datei.exists() and not erneuern:
|
|
print(f"{name}: Spiegel vorhanden, uebersprungen (--erneuern erzwingt)")
|
|
continue
|
|
|
|
if name == "aemter":
|
|
zeilen = []
|
|
for qid, (_id, _n, _r) in AEMTER.items():
|
|
zeilen_amt = _werte(frage(ABFRAGE_INHABER % {"qid": qid}))
|
|
for z in zeilen_amt:
|
|
z["amt_qid"] = qid
|
|
zeilen.extend(zeilen_amt)
|
|
print(f" {qid}: {len(zeilen_amt)} Inhaber")
|
|
time.sleep(PAUSE)
|
|
fingerabdruck = hashlib.blake2b(
|
|
json.dumps(sorted(AEMTER), ensure_ascii=False).encode(), digest_size=8
|
|
).hexdigest()
|
|
else:
|
|
zeilen = _werte(frage(sparql))
|
|
fingerabdruck = hashlib.blake2b(sparql.encode(), digest_size=8).hexdigest()
|
|
time.sleep(PAUSE)
|
|
|
|
datei.write_text(
|
|
json.dumps(zeilen, ensure_ascii=False, indent=1, sort_keys=True) + "\n"
|
|
)
|
|
manifest[name] = {
|
|
"stand": stand,
|
|
"zeilen": len(zeilen),
|
|
"abfrage_hash": fingerabdruck,
|
|
"endpunkt": ENDPUNKT,
|
|
}
|
|
print(f"{name}: {len(zeilen)} Zeilen -> {datei}")
|
|
|
|
manifestdatei.write_text(
|
|
json.dumps(manifest, ensure_ascii=False, indent=1, sort_keys=True) + "\n"
|
|
)
|
|
return manifest
|
|
|
|
|
|
def main(argumente=None):
|
|
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
p.add_argument("--spiegeln", action="store_true", help="Wikidata abfragen")
|
|
p.add_argument("--laden", action="store_true", help="Spiegel in die Tabellen laden")
|
|
p.add_argument("--nur", nargs="*", help="nur diese Abfragen")
|
|
p.add_argument("--erneuern", action="store_true", help="vorhandenen Spiegel ersetzen")
|
|
a = p.parse_args(argumente)
|
|
|
|
if a.spiegeln:
|
|
spiegeln(nur=a.nur, erneuern=a.erneuern)
|
|
if a.laden:
|
|
import lexikon_laden
|
|
|
|
lexikon_laden.laden(nur=a.nur)
|
|
if not (a.spiegeln or a.laden):
|
|
p.error("--spiegeln oder --laden angeben")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|