diff --git "a/DNB-SRU-Abfragen_leicht_erkl\303\244rt.ipynb" "b/DNB-SRU-Abfragen_leicht_erkl\303\244rt.ipynb" new file mode 100644 index 0000000..cbd7892 --- /dev/null +++ "b/DNB-SRU-Abfragen_leicht_erkl\303\244rt.ipynb" @@ -0,0 +1,297 @@ +{ + "metadata": { + "kernelspec": { + "name": "python", + "display_name": "Python (Pyodide)", + "language": "python" + }, + "language_info": { + "codemirror_mode": { + "name": "python", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8" + } + }, + "nbformat_minor": 4, + "nbformat": 4, + "cells": [ + { + "cell_type": "markdown", + "source": "# DNBLab Jupyter Notebook Tutorial\n\n## SRU-Abfragen erklärt - Tutorial für Einsteiger\\*innen (JupyterLite-Version) ", + "metadata": {} + }, + { + "cell_type": "markdown", + "source": "Dieses Tutorial beschreibt, wie Sie in der browserbasierten JupyterLite-Umgebung und der Programmiersprache Python die SRU-Schnittstelle der DNB abfragen und mit den erhaltenen Antworten arbeiten können. Der Aufbau der Abfragen wird anhand von Beispielen erklärt und stützt sich auf die Dokumentation der SRU-Schnittstelle unter https://www.dnb.de/sru.\n\nDas Tutorial ist wie folgt aufgebaut: \n\n* [1. Einrichten der Arbeitsumgebung](#Teil1) \n* [2. Abfragen verschiedener Datensätze der DNB](#Teil2) \n* [3. Aufbau einer gezielten Suche](#Teil3) ", + "metadata": {} + }, + { + "cell_type": "markdown", + "source": "Hinweis: JupyterLite ist eine schnelle, \"leichtgewichtige\" und sehr ressourcenschonende Coding-Umgebung. Dadurch stehen in JupyterLite nicht alle Funktionalitäten zur Verfügung und der Code des Tutorials wurde im Vergleich zur Jupyter Notebook-Version entsprechend angepasst. ", + "metadata": {} + }, + { + "cell_type": "markdown", + "source": "## 1. Einrichten der Arbeitsumgebung", + "metadata": {} + }, + { + "cell_type": "markdown", + "source": "Um die Arbeitsumgebung für die folgenden Schritte einzurichten, werden zunächst die benötigten Python-Biblitoheken importiert: \"urllib\", \"pyodide\" und \"js\" für die Abfragen an die SRU-Schnittstelle in der JupyterLite-Umgebung, \"ElementTree\" über \"lxml\" (als ET) und \"BeautifulSoup\", um die XML-Antworten der Schnittstelle besser verarbeiten zu können sowie \"unicodedata\" zur Verarbeitung der Zeichencodierung:", + "metadata": {} + }, + { + "cell_type": "code", + "source": "import urllib.parse\nfrom pyodide.http import open_url, pyfetch\nfrom js import fetch\nfrom bs4 import BeautifulSoup as soup\nimport unicodedata\nfrom lxml import etree as ET", + "metadata": { + "trusted": true + }, + "outputs": [], + "execution_count": 2 + }, + { + "cell_type": "markdown", + "source": "Die SRU-Schnittstelle der DNB ist unter der Basis-URL https://services.dnb.de/sru erreichbar. Ein Aufruf dieser Adresse im Browser zeigt den aktuellen Status sowie die Version der Schnittstelle an. ", + "metadata": {} + }, + { + "cell_type": "markdown", + "source": "## 2. Abfrage verschiedener Datensätze der DNB ", + "metadata": {} + }, + { + "cell_type": "markdown", + "source": "Die DNB bietet Ihre Daten über drei verschiedene \"Kataloge\" an, die entsprechend für eine Abfrage ausgewählt werden müssen. Dies geschieht über eine Erweiterung der o.g. Basis-URL. Zur Verfügung stehen folgende Kataloge: \n\n* Katalog der Deutschen Nationalbibliothek (DNB) - hierin befinden sich die Titeldaten\n* Katalog des Deutschen Musikarchivs (DMA) - Datensätze des Deutschen Musikarchivs\n* Katalog der Gemeinsamen Normdatei (GND) - hierin befinden sich die Normdaten\n\nDie Erweiterungen für die URL sind folgende: \n\n* DNB: https://services.dnb.de/sru/dnb\n* DMA: https://services.dnb.de/sru/dnb.dma\n* GND: https://services.dnb.de/sru/authorities\n\nWerden die jeweiligen Bereiche ohne weitere Spezifikationen abgefragt, senden sie eine Selbstbeschreibung im XML-Standardformat https://services.dnb.de/sru/dnb?operation=explain&version=1.1 zurück. \n", + "metadata": {} + }, + { + "cell_type": "code", + "source": "#URL der SRU-Schnittstelle der DNB: \nbase_url = \"https://services.dnb.de/sru/dnb\"\n\n#Anfrage - speichern der Antwort in die Variable \"basic_request\" und Überführung des Inhalts in die Variable \"response\":\nbasic_request = await fetch(base_url)\ncontent = await basic_request.text()", + "metadata": { + "trusted": true + }, + "outputs": [], + "execution_count": 3 + }, + { + "cell_type": "markdown", + "source": "Mit Hilfe der Bibliothek \"BeautifulSoup\" kann die Antwort direkt in XML umgewandelt werden. \n\nHinweis: Um das Tutorial übersichtlich zu halten, wird die Ausgabe der Antwort im folgenden auf die ersten 500 Zeichen gekürzt - die eigentliche Antwort ist länger und kann durch einfaches Löschen der Einschränkung \"[0:500]\" in der \"print\"-Zeile komplett angezeigt werden. Natürlich können auch andere Bereiche zur Anzeige gewählt werden. ", + "metadata": {} + }, + { + "cell_type": "code", + "source": "#Umwandeln in XML und Ausgabe der ersten 500 Zeichen: \nresponse = soup(content, features=\"xml\")\nprint(response.prettify()[0:500])", + "metadata": { + "trusted": true + }, + "outputs": [ + { + "name": "stdout", + "text": "\n\n \n 1.1\n \n \n \n http://explain.z3950.org/dtd/2.0/\n \n \n xml\n \n \n \n \n \n services.dnb.de\n \n \n 443\n \n", + "output_type": "stream" + } + ], + "execution_count": 4 + }, + { + "cell_type": "markdown", + "source": "Für eine Suchanfrage an die Daten der DNB wird nun über die URL der Katalog definiert. Mit Hilfe der Variable *parameter* werden dann alle weiteren benötigten Parameter übergeben. \n\nBesonders relevant sind dabei die beiden Parameter 'query' : 'Klimawandel', sowie 'recordSchema' : 'MARC21-xml'. Statt \"Klimawandel\" kann hier jeder beliebige Suchbegriff eingetragen werden - auch Suchanfragen, die aus mehreren Wörtern bestehen, können mittels boolscher Operatoren übergeben werden. Die genaue Syntax wird unter https://www.dnb.de/sru beschrieben. Statt \"MARC21-xml\" stehen außerdem noch die Ausgabeformate \"oai_dc\" oder \"RDFxml\" als Ausgabeformate zur Verfügung (siehe weiter unten). \n", + "metadata": {} + }, + { + "cell_type": "code", + "source": "base_url = \"https://services.dnb.de/sru/dnb\"\nparams = {'recordSchema' : 'MARC21-xml',\n 'operation': 'searchRetrieve',\n 'version': '1.1',\n 'query': \"Klimawandel\"\n }\n \nr = await fetch(base_url + \"?\" + urllib.parse.urlencode(params)) \nr_text = await r.text()\n\nresponse = soup(r_text, features=\"xml\")\nprint(response.prettify()[0:500])", + "metadata": { + "trusted": true + }, + "outputs": [ + { + "name": "stdout", + "text": "\n\n \n 1.1\n \n \n 9979\n \n \n \n \n MARC21-xml\n \n \n xml\n \n \n \n \n 00000nam a22000008c 4500\n \n \n 1147699615\n \n\n \n 1.1\n \n \n 29\n \n \n \n \n oai_dc\n \n \n xml\n \n \n \n \n Anpassung an den Klimawandel : Gründe, Folgen, Handlungsoptionen / Bundesministerium für Wirtschaftliche Zusammenarbeit und Entwicklung ; Gtz, Deutsche Gesellschaft für Technische Zusammenarbeit (GTZ) GmbH\n \n ZAHL\n\n```\n\nWenn man diese Information nicht im XML suchen möchte, kann auch der Code so angepasst werden, dass der entsprechende Abschnitt mithilfe des Zusatzes \".find('numberOfRecords')\" gesucht und als Attribut \".text\" an die Variable *number* angehängt wird.\n", + "metadata": {} + }, + { + "cell_type": "markdown", + "source": "number = response.find('numberOfRecords')\nprint(number.text, 'Ergebnisse')\n\n#Einfache Ausgabe:\n#print(number)", + "metadata": {} + }, + { + "cell_type": "markdown", + "source": "Da die einzelzen Treffer bzw. Datensätze jeweils durch \"record\"-Tags gekennzeichnet sind, werden diese nun gesucht und in der Variable records zwischengespeichert. Im Anschluss wird die Länge der Variable ausgeben,um sie mit der Angabe unter \"numberOfRecords\" zu vergleichen.\n\nHinweis: Die SRU-Schnittstelle gibt immer erstmal nur die ersten 100 Treffer zurück, d.h. auch bei größeren Treffermengen wird maximal die Länge 100 angezeigt. Wie man größere Treffermengen sammeln kann, folgt weiter unten im Tutorial.", + "metadata": {} + }, + { + "cell_type": "code", + "source": "records = response.find_all('record')\nprint(len(records), 'Ergebnisse')", + "metadata": { + "trusted": true + }, + "outputs": [ + { + "name": "stdout", + "text": "29 Ergebnisse\n", + "output_type": "stream" + } + ], + "execution_count": 8 + }, + { + "cell_type": "markdown", + "source": "Hinweis: Da die Datensätze bei einer geänderten Ausgabe in 'recordSchema' : 'MARC21-xml' anstatt des voreingestellten 'recordSchema' : 'oai_dc' unterschiedlich verschachtelt sein können, muss neben der Auswahl des Katalogs hinter der Basis-URL \"/dnb\" oder \"/authorities\" bei der oben stehenden Suche zusätzlich der Typ des Datensatzes angegeben werden:\n\n\nMAR21-xml Titeldaten (/dnb): `records = response.find_all('record', {'type':'Bibliographic'})`
\nMAR21-xml Normdaten: (/authorities) `records = response.find_all('record', {'type':'Authority'})` \n", + "metadata": {} + }, + { + "cell_type": "markdown", + "source": "Die Ergebnisse werden als Liste gespeichert und stehen dabei jeweils an einem Platz innerhalb der Listenvariable. Bei 9 Ergebnissen werden in der Liste die Plätze 0-8 belegt, was bei der Adressierung bedacht werden muss.\n\nDer 3. Eintrag wird entsprechend über den Listenplatz Nummer 2 aufgerufen:", + "metadata": {} + }, + { + "cell_type": "code", + "source": "print(records[2])", + "metadata": { + "trusted": true + }, + "outputs": [ + { + "name": "stdout", + "text": "oai_dcxml\nDie EU im Einsatz gegen den Klimawandel : der EU-Emissionshandel - ein offenes System, das weltweit Innovationen fördert / [Europäische Kommission]\nEuropäische Kommission\n[Luxemburg] : [Amt für Amtliche Veröff. der Europ. Gemeinschaften]\n2005\nger\n92-894-9187-6 geh.\n992017882\n360 Soziale Probleme, Sozialdienste, Versicherungen\n330 Wirtschaft\n20 S.\n3\n", + "output_type": "stream" + } + ], + "execution_count": 10 + }, + { + "cell_type": "markdown", + "source": "Die bisherigen Schritte können wie folgt in einer Funktion gefolgt von der Abfrage zusammengefasst werden:", + "metadata": {} + }, + { + "cell_type": "code", + "source": "#Funktion\nasync def dnb_sru_short(query):\n \n base_url = \"https://services.dnb.de/sru/dnb\"\n parameter = {'recordSchema' : 'oai_dc',\n 'operation': 'searchRetrieve',\n 'version': '1.1',\n 'maximumRecords': '100',\n 'query': query\n }\n \n r = await fetch(base_url + \"?\" + urllib.parse.urlencode(parameter)) \n content = await r.text()\n xml = soup(content, features=\"xml\")\n records = xml.find_all('record')\n \n return records\n \n", + "metadata": { + "trusted": true + }, + "outputs": [], + "execution_count": 11 + }, + { + "cell_type": "code", + "source": "#Formulierung der Abfrage: \nmyquery = await dnb_sru_short('tit=Klimawandel and jhr=2005') #Aufruf der Funktion 'sru-dnb' mit der Abfrage 'tit=Klimawandel...'\nprint(len(myquery), \"Ergebnisse\")", + "metadata": { + "trusted": true + }, + "outputs": [ + { + "name": "stdout", + "text": "29 Ergebnisse\n", + "output_type": "stream" + } + ], + "execution_count": 12 + }, + { + "cell_type": "markdown", + "source": "Die SRU-Schnittstelle gibt zunächst maximal 100 Treffer aus. Wenn es sich um mehr Ergebnisse handelt, können weitere Treffer mithilfe einer \"Schleife\" in 100er Schritten geholt und zwischengespeichert werden. Das so gesammelte Ergebnis wird ausgegeben, sobald die Ergebnismenge aus \"numberOfRecords\" erreicht wurde:", + "metadata": {} + }, + { + "cell_type": "code", + "source": "#Funktion\nasync def dnb_sru(query):\n \n base_url = \"https://services.dnb.de/sru/dnb\"\n parameter = {'recordSchema' : 'oai_dc',\n 'operation': 'searchRetrieve',\n 'version': '1.1',\n 'maximumRecords': '100',\n 'query': query\n }\n \n r = await fetch(base_url + \"?\" + urllib.parse.urlencode(parameter)) \n content = await r.text()\n xml = soup(content, features=\"xml\")\n number = int(xml.find('numberOfRecords').text)\n records = xml.find_all('record')\n \n if number <= 101: # wurden maximal 100 Treffer gefunden? Wenn ja, erfolgt direkt die Rückgabe.\n return records\n \n else: # wurden mehr als 100 Treffer gefunden, wird hier die Schleife gestartet.\n num_results = 100\n i = 101\n \n while num_results == 100:\n \n parameter.update({'startRecord': i}) \n r = await fetch(base_url + \"?\" + urllib.parse.urlencode(parameter)) \n content = await r.text()\n xml = soup(content, features=\"xml\")\n new_records = xml.find_all('record')\n records+=new_records\n i+=100\n num_results = len(new_records)\n \n return records\n ", + "metadata": { + "trusted": true + }, + "outputs": [], + "execution_count": 13 + }, + { + "cell_type": "markdown", + "source": "Eine Abfrage für das Titelstichwort \"Klimawandel\" kombiniert mit dem Jahr 2019 ergibt folgende Treffermenge:", + "metadata": {} + }, + { + "cell_type": "code", + "source": "myquery = await dnb_sru('tit=Klimawandel and jhr=2019')\nprint(len(myquery), \"Ergebnisse\")", + "metadata": { + "trusted": true + }, + "outputs": [ + { + "name": "stdout", + "text": "305 Ergebnisse\n", + "output_type": "stream" + } + ], + "execution_count": 14 + }, + { + "cell_type": "markdown", + "source": "Die so abgerufenen Treffer können entweder direkt weiterverarbeitet oder lokal zwischengespeichert werden. \nIm Folgenden werden die Ergebnisse in eine XML-Datei geschrieben, die heruntergeladen werden kann: ", + "metadata": {} + }, + { + "cell_type": "code", + "source": "with open('sru_abfrage_klimawandel.xml', 'w', encoding=\"utf-8\") as f:\n f.write(str(myquery))", + "metadata": { + "trusted": true + }, + "outputs": [], + "execution_count": 16 + }, + { + "cell_type": "markdown", + "source": "", + "metadata": {} + }, + { + "cell_type": "code", + "source": "", + "metadata": {}, + "outputs": [], + "execution_count": null + } + ] +} \ No newline at end of file