Skip to content

Instantly share code, notes, and snippets.

@duarteocarmo
Created February 17, 2026 19:40
Show Gist options
  • Select an option

  • Save duarteocarmo/ee007986e70e8e3b31c966c199a7fea9 to your computer and use it in GitHub Desktop.

Select an option

Save duarteocarmo/ee007986e70e8e3b31c966c199a7fea9 to your computer and use it in GitHub Desktop.
Lookup a GND person record via lobid.org – returns VIAF ID, geocoordinates, Wikidata ID, ISNI, professions, etc. Usage: uv run lookup_gnd.py <gnd_id>
# /// script
# requires-python = ">=3.11"
# dependencies = ["httpx"]
# ///
"""Lookup a GND person record via lobid.org and return enriched info (VIAF, coordinates, etc).
Usage:
uv run scripts/lookup_gnd.py <gnd_id>
uv run scripts/lookup_gnd.py 110952928
Example output includes VIAF ID, ISNI, Wikidata ID, birth/death info,
geocoordinates for places, professions, and more.
"""
import sys
import json
import httpx
def extract_viaf_id(same_as: list[dict]) -> str | None:
for entry in same_as:
if entry.get("collection", {}).get("abbr") == "VIAF":
return entry["id"].rsplit("/", 1)[-1]
return None
def extract_identifier(same_as: list[dict], abbr: str) -> str | None:
for entry in same_as:
if entry.get("collection", {}).get("abbr") == abbr:
return entry["id"].rsplit("/", 1)[-1]
return None
def resolve_place(place_ref: dict, client: httpx.Client) -> dict:
"""Resolve a place GND reference to get label and coordinates."""
place_id = place_ref.get("id", "")
label = place_ref.get("label", "")
result = {
"label": label,
"lat": None,
"lng": None,
"gnd_id": place_id.rsplit("/", 1)[-1],
}
if not place_id or not place_id.startswith("https://d-nb.info/gnd/"):
return result
gnd_id = place_id.rsplit("/", 1)[-1]
try:
resp = client.get(f"https://lobid.org/gnd/{gnd_id}.json", timeout=10)
resp.raise_for_status()
place_data = resp.json()
for geom in place_data.get("hasGeometry", []):
wkt = geom.get("asWKT", [None])[0]
if wkt and "Point" in wkt:
# Format: "Point ( +013.416669 +052.500000 )"
coords = wkt.replace("Point", "").strip(" ()")
parts = coords.split()
if len(parts) == 2:
result["lng"] = float(parts[0])
result["lat"] = float(parts[1])
except (httpx.HTTPError, ValueError, KeyError):
pass
return result
def lookup_gnd(gnd_id: str) -> dict:
with httpx.Client() as client:
resp = client.get(f"https://lobid.org/gnd/{gnd_id}.json", timeout=10)
resp.raise_for_status()
data = resp.json()
same_as = data.get("sameAs", [])
name_entity = data.get("preferredNameEntityForThePerson", {})
# Resolve places
birthplace = None
if data.get("placeOfBirth"):
birthplace = resolve_place(data["placeOfBirth"][0], client=client)
deathplace = None
if data.get("placeOfDeath"):
deathplace = resolve_place(data["placeOfDeath"][0], client=client)
activity_places = []
for place_ref in data.get("placeOfActivity", []):
activity_places.append(resolve_place(place_ref, client=client))
professions = [
p.get("label", "") for p in data.get("professionOrOccupation", [])
]
countries = [c.get("label", "") for c in data.get("geographicAreaCode", [])]
gender_list = data.get("gender", [])
gender = gender_list[0].get("label", "") if gender_list else None
variant_names = data.get("variantName", [])
bio_info = data.get("biographicalOrHistoricalInformation", [])
return {
"gnd_id": gnd_id,
"viaf_id": extract_viaf_id(same_as),
"isni_id": extract_identifier(same_as, "ISNI"),
"wikidata_id": extract_identifier(same_as, "WIKIDATA"),
"preferred_name": data.get("preferredName"),
"forename": (name_entity.get("forename") or [None])[0],
"surname": (name_entity.get("surname") or [None])[0],
"variant_names": variant_names or None,
"date_of_birth": (data.get("dateOfBirth") or [None])[0],
"date_of_death": (data.get("dateOfDeath") or [None])[0],
"birthplace": birthplace,
"deathplace": deathplace,
"activity_places": activity_places or None,
"associated_countries": countries or None,
"professions": professions or None,
"gender": gender,
"biographical_info": bio_info[0] if bio_info else None,
}
def main():
if len(sys.argv) != 2:
print("Usage: uv run scripts/lookup_gnd.py <gnd_id>")
print("Example: uv run scripts/lookup_gnd.py 110952928")
sys.exit(1)
gnd_id = sys.argv[1]
try:
result = lookup_gnd(gnd_id=gnd_id)
print(json.dumps(result, indent=2, ensure_ascii=False))
except httpx.HTTPStatusError as e:
print(f"Error: HTTP {e.response.status_code} for GND {gnd_id}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment