2024 wird gefeiert: 10 Jahre Klexikon und 5 Jahre MiniKlexikon. Spenden helfen uns sehr!

Benutzer:Klexibot/Skripte

Aus Klexikon – das Kinderlexikon
< Benutzer:Klexibot
Version vom 9. Dezember 2020, 21:50 Uhr von Thomas Karcher (Diskussion | Beiträge) (→‎Wikidata-SPARQL-Abfrage: Gibt es einen Klexikon-Artikel zum Staatsoberhaupt, wird jetzt dessen Schreibweise übernommen, und nicht mehr die offizielle Schreibweise aus Wikidata.)
Die druckbare Version wird nicht mehr unterstützt und kann Darstellungsfehler aufweisen. Bitte aktualisiere deine Browser-Lesezeichen und verwende stattdessen die Standard-Druckfunktion des Browsers.

Aktualisierung der Länder-Infoboxen

Pywikibot-Skript

import requests
import pywikibot
import mwparserfromhell
import locale
import datetime

# Testlauf: Ausgabe des Logbuchs in der Konsole ohne Änderungen am Klexikon
dryrun_answer = input ('Nur Testlauf (j/n)? ')
dryrun = dryrun_answer != 'n'

# Verwende deutsches Zahlenformat mit Tausenderpunkt und Dezimalkomma
locale.setlocale(locale.LC_ALL, 'german')

# Zuordnung von Wikidata-Abfrage-Feldern zu Vorlage-Parametern
fieldmap = {
	'Name': 'itemLabel',
	'Flagge': 'safeFlaggenDatei',
	'Hauptstadt': 'hauptstaedte',
	'Amtssprache': 'amtssprachen',
	'Staatsoberhaupt': 'oberhaeupter',
	'Einwohnerzahl': 'max_ew_in_mio',
	'Fläche': 'max_flaeche_rund',
	'Lagekarte': 'safeKartenDatei',
	'Lagebeschreibung': 'kontinente'
}

# Legt fest, welche Länder-Artikel bzw. Vorlagen-Parameter Klexibot nicht ändern soll
blacklist = {
	'Australien:Staatsoberhaupt',
	'Eidgenossenschaft',
	'Deutsches_Kaiserreich',
	'Schottland',
	'England',
	'Eswatini:Name',
	'Schweiz:Amtssprache',
	'St._Kitts_und_Nevis:Staatsoberhaupt',
	'St._Lucia:Staatsoberhaupt',
	'St._Vincent_und_die_Grenadinen:Staatsoberhaupt',
	'Vatikan:Fläche',
	'Vatikan:Hauptstadt',
	'Wales'
}

# Parameter aus dieser Liste werden nur gefüllt, falls bisher kein Wert enthalten war
preserved_values = {
	'Lagekarte',
	'Lagebeschreibung'
}

# Anmeldung beim Klexikon
site = pywikibot.Site()

# Lade Wikidata-Tabelle und speichere sie in "data"-Variable
url = 'https://query.wikidata.org/sparql'
with open('infobox_data_query.rq', 'r', encoding="utf-8") as query_file: qry = query_file.read()
r = requests.get(url, params = {'format': 'json', 'query': qry }, headers = {
	'User-Agent':f'{requests.utils.default_headers()["User-Agent"]} (Klexikon bot by User:Tkarcher)'})
data = r.json()

# Initialisiere Logbuch-Eintrag
logheader = f'\n== Logbuch vom {datetime.datetime.now().strftime("%c")} ==\n'
newloghead = '=== Neu angelegte Infoboxen ===\n'
newlogdata = ''
chgloghead = '=== Änderungen an bestehenden Infoboxen ===\n' + \
	'{| class="wikitable sortable"\n! Vorlage\n! Eigenschaft\n! Alter Wert\n! Neuer Wert\n'
chglogdata = ''
chglogfoot = '|}\n'

# Gehe Wikidata-Tabelle Zeile für Zeile durch
for item in data['results']['bindings']:
	title = item['titel_im_klexikon']['value']

	# Ignoriere Artikel, die in der Ausschlußliste enthalten sind
	if title not in blacklist:

		# Öffne Vorlagen-Seite oder lege sie neu an
		page = pywikibot.Page(site, f'Vorlage:Infobox_{title}')
		code = mwparserfromhell.parse(page.text)

		# Ersetze Seiteninhalt mit {{Infobox Land}}, falls nicht vorhanden
		if not code.filter_templates(matches = lambda n: n.name.matches ('Infobox Land')):
			code = mwparserfromhell.parse('{{Infobox Land\n}}')
			newlogdata += f'* [[Vorlage:Infobox {title}]]\n'

		# Fülle alle Vorlagen-Felder
		for prop, field in fieldmap.items():

			# Ignoriere Vorlagen-Parameter, die leer oder in der Ausschlußliste enthalten sind
			if field in item and f'{title}:{prop}' not in blacklist:

				# Ignoriere auch Parameter, die nicht überschrieben werden sollen
				if not (
					prop in preserved_values and
					code.filter_templates()[0].has(prop) and
					code.filter_templates()[0].get(prop).value.strip() != ''):

					oldval = code.filter_templates()[0].get(prop).value.strip() if code.filter_templates()[0].has(prop) else ''
					val = item[field]['value']
					if prop == 'Einwohnerzahl':
						ew = float(val)
						if round(ew) > 1:
							val = f'etwa {locale.format_string("%.0f", ew, grouping = True)} [[Million]]en'
						elif ew > 0.9:
							val = f'etwa 1 [[Million]]'
						else:
							val = f'etwa {locale.format_string("%.0f", ew * 1000000, grouping = True)}'
					elif prop == 'Fläche':
						val = f'etwa {locale.format_string("%i", int(val), grouping = True)} [[Meter|Quadratkilometer]]'
					elif prop == 'Staatsoberhaupt':
						if 'oberhaupt_bezeichnung' in item:
							val = f'{item["oberhaupt_bezeichnung"]["value"]} {val}'
					elif prop == 'Lagebeschreibung':
						val = f'Wo das Land in {val} liegt'

					# Speichere die Änderung nur, wenn sie aus mehr besteht als
					# einer geänderten Sortierreihenfolge in der Auslistung
					# (SPARQL 1.1 erlaubt kein ORDER BY in GROUP_CONCATs)
					if sorted (val) != sorted (oldval):
						code.filter_templates()[0].add (f' {prop} ', f' {val}\n', preserve_spacing = False)
						if not newlogdata.endswith (f'* [[Vorlage:Infobox {title}]]\n'):
							chglogdata += f'|- \n| [[Vorlage:Infobox {title}]]\n| {prop}\n| {oldval}\n| {val}\n'

		# Speichere die Änderungen, falls vorhanden, und verschiebe
		# Artikel ggf. zurück in Kategorie "ungeprüfte Infobox"
		if (page.text != str(code)) and not dryrun:

			# Speichere Vorlage
			page.text = str(code)
			page.save('Automatische Anlage der Länder-Infobox')

			# Öffne Länderartikel
			page = pywikibot.Page(site, title)
			code = mwparserfromhell.parse(page.text)

			# Binde Länder-Infobox ein, falls noch nicht geschehen
			# Mit "_" und " " wegen https://github.com/earwig/mwparserfromhell/issues/216
			if not code.filter_templates(matches = lambda n:
				n.name.matches ({f'Infobox_{title}', f'Infobox {title}'})):
				code = mwparserfromhell.parse(f'{{{{Infobox_{title}}}}}\n{str(code)}')

				# Lösche [[Kategorie:Artikel mit geprüfter Infobox]], falls vorhanden
				if '[[Kategorie:Artikel mit geprüfter Infobox]]' in str(code):
					code.remove ('[[Kategorie:Artikel mit geprüfter Infobox]]')

				# Füge [[Kategorie:Artikel mit ungeprüfter Infobox]] ein, falls noch nicht geschehen
				if '[[Kategorie:Artikel mit ungeprüfter Infobox]]' not in str(code):
					code.append ('\n[[Kategorie:Artikel mit ungeprüfter Infobox]]')

				# Speichere Länderartikel
				page.text = str(code)
				page.save('Automatische Einbindung der Länder-Infobox')

# Dokumentiere Änderungen in Logbuch
log = logheader \
	+ newloghead + (newlogdata if newlogdata != '' else 'Es wurden keine neuen Infoboxen angelegt.\n') \
	+ chgloghead + (chglogdata if chglogdata != '' else '|- \n| colspan=4 | Es wurden keine bestehenden Daten geändert.\n') + chglogfoot

if dryrun:
	print (log)
else:
	page = pywikibot.Page(site, 'Benutzer:Klexibot/Logbuch')
	page.text += log
	page.save('Logbuch aktualisiert')

Wikidata-SPARQL-Abfrage

#Länder-Infoboxen im Klexikon
SELECT
 ?item
 ?itemLabel
 (MAX(?ew_in_millionen) AS ?max_ew_in_mio)
 (GROUP_CONCAT(DISTINCT (IF(BOUND(?asklextitel),CONCAT('[[',?asklextitel,'|',?amtssprachenLabel,']]'),?amtssprachenLabel)); SEPARATOR=", ") AS ?amtssprachen)
 (MAX(?flaeche_rund) AS ?max_flaeche_rund)
 (GROUP_CONCAT(DISTINCT (IF(BOUND(?hsklextitel),CONCAT('[[',REPLACE(?hsklextitel, "_", " "),']]'),?hauptstadtLabel)); SEPARATOR=", ") AS ?hauptstaedte)
 (GROUP_CONCAT(DISTINCT (COALESCE (?oberhaupt_titel_ausnahme, ?oberhaupt_bezLabel)); SEPARATOR=", ") AS ?oberhaupt_bezeichnung)
 (GROUP_CONCAT(DISTINCT (IF(BOUND(?ohklextitel),CONCAT('[[',REPLACE(?ohklextitel, "_", " "),']]'),?oberhauptLabel)); SEPARATOR=", ") AS ?oberhaeupter)
 (GROUP_CONCAT(DISTINCT (CONCAT('[[',?kontinentLabel,']]')); SEPARATOR=" und ") AS ?kontinente)
 ?safeFlaggenDatei
 ?safeKartenDatei
 ?titel_im_klexikon
WHERE
{
 ?item p:P31 [ps:P31 wd:Q6256]. # Finde alle Länder
 ?item wdt:P6573 ?titel_im_klexikon. # die es im Klexikon gibt
 ?item wdt:P1082 ?ewzahl. # und zeige ihre Einwohnerzahl
 BIND (STR(ROUND(?ewzahl / 1000) / 1000) AS ?ew_in_millionen) # in Millionen
 ?item wdt:P2046 ?flaeche. # ihre Fläche
 BIND (STR(IF(?flaeche > 9999, ROUND(?flaeche / 1000) * 1000,IF(?flaeche > 999, ROUND(?flaeche / 100) * 100,ROUND(?flaeche)))) AS ?flaeche_rund) # gerundet
 ?item wdt:P37 ?amtssprache. # ihre Amtssprache
 # ?article schema:about ?amtssprache. # Zeige aber nur die bekannteren Amtssprachen, d.h. solche
 # ?article schema:inLanguage "de" . # für die ein deutschsprachiger Wikipedia-Artikel existiert
 OPTIONAL {?amtssprache wdt:P6573 ?asklextitel}
 OPTIONAL { # Optional wg. Vatikanstadt
  ?item wdt:P36 ?hauptstadt. # ihre Hauptstädte
  OPTIONAL {?hauptstadt wdt:P6573 ?hsklextitel}
 }
 ?item wdt:P35 ?oberhaupt. # das Staatsoberhaupt
 OPTIONAL {?oberhaupt wdt:P6573 ?ohklextitel}
 OPTIONAL {
  ?item wdt:P1906/wdt:P279* ?oberhaupt_titel.
  FILTER (?oberhaupt_titel IN ( # einschränken auf bekannte / kinderverständliche Titel
  wd:Q30461, # Präsident
  wd:Q2304859, # Souverän
  wd:Q7645115, # Oberster Führer
  wd:Q166382, # Emir
  wd:Q683337, # Kofürst von Andorra
  wd:Q258045, # Capitano Reggente
  wd:Q844944 # Präsidium von Bosnien und Herzegowina
  ))
  OPTIONAL {
   ?oberhaupt_titel wdt:P2521 ?oberhaupt_titel_weiblich.
   FILTER (LANG(?oberhaupt_titel_weiblich) = "de").
  }
 }
 OPTIONAL {?oberhaupt wdt:P21 ?geschlecht.}
 BIND (IF (BOUND(?oberhaupt_titel_weiblich) && BOUND(?geschlecht) && (?geschlecht = wd:Q6581072),
  IF(STR(?oberhaupt_titel_weiblich) = "Souveränin", wd:Q16511993, ?oberhaupt_titel_weiblich),
  IF(?oberhaupt_titel = wd:Q2304859, wd:Q12097, ?oberhaupt_titel)) AS ?oberhaupt_bez
 )
 OPTIONAL { # Länderspezifische Titel
  VALUES (?item ?oberhaupt_titel_ausnahme) {
   (wd:Q794 "Religionsführer") # Iran
   (wd:Q17 "[[Kaiser]]") # Japan
   (wd:Q40 "Bundespräsident") # Österreich
   (wd:Q183 "Bundespräsident") # Deutschland
   (wd:Q32 "Großherzog") # Luxemburg
   (wd:Q237 "[[Papst]]") # Vatikan
   (wd:Q347 "Fürst") # Liechtenstein
   (wd:Q683 "Häuptling") #Samoa
  }
 }
 ?item wdt:P41 ?flagge.
 BIND(REPLACE(wikibase:decodeUri(STR(?flagge)), "http://commons.wikimedia.org/wiki/Special:FilePath/", "") AS ?flaggenDatei).
 BIND(REPLACE(?flaggenDatei, " ", "_") AS ?safeFlaggenDatei)
 ?item wdt:P30 ?kontinent_alt.
 OPTIONAL {
  ?item wdt:P361*/wdt:P706*/wdt:P361* wd:Q27611
  BIND (wd:Q29876 AS ?mittelamerika)
 }
 BIND (COALESCE(?mittelamerika, ?kontinent_alt) AS ?kontinent)

 OPTIONAL { # Lagekarte: Grünes Land auf dem Globus
  ?item p:P242 ?p_karte1.
  ?p_karte1 pq:P195 wd:Q21167586.
  ?p_karte1 ps:P242 ?karte1.
 }
 OPTIONAL { # Lagekarte: Rotes Land auf dem Globus
  ?item p:P242 ?p_karte2.
  ?p_karte2 pq:P195 wd:Q47008743.
  ?p_karte2 ps:P242 ?karte2.
 }
 # Nimm Globus, falls vorhanden, sonst 2:1-Ausschnitt
 BIND(REPLACE(wikibase:decodeUri(STR(COALESCE(?karte1, ?karte2))), "http://commons.wikimedia.org/wiki/Special:FilePath/", "") AS ?kartenDatei).
 BIND(REPLACE(?kartenDatei, " ", "_") AS ?safeKartenDatei)

 SERVICE wikibase:label {
  bd:serviceParam wikibase:language "[AUTO_LANGUAGE],de".
  ?item rdfs:label ?itemLabel.
  ?amtssprache rdfs:label ?amtssprachenLabel.
  ?hauptstadt rdfs:label ?hauptstadtLabel.
  ?oberhaupt_bez rdfs:label ?oberhaupt_bezLabel.
  ?oberhaupt rdfs:label ?oberhauptLabel.
  ?kontinent rdfs:label ?kontinentLabel.
 }
}
GROUP BY ?item ?itemLabel ?safeFlaggenDatei ?safeKartenDatei ?titel_im_klexikon
ORDER BY ?itemLabel