<jupyter_start><jupyter_text>Requests Installation und Beispielanwendung  InstallationFür die Kommunikation mit solchen REST-APIs ist die [requests](https://requests.readthedocs.io/)-Bibliothek hilfreich. Mit [Spack](https://jupyter-tutorial.readthedocs.io/de/latest/productive/envs/spack/index.html) könnt ihr requests in eurem Kernel bereitstellen:``` bash$ spack env activate python-374$ spack install py-requests ^python@3.7.4%gcc@9.1.0```Alternativ könnt ihr requests auch mit anderen Paketmanagern installieren, z.B.``` bash$ pipenv install requests```  Beispiel OSM Nomination APIIn diesem Beispiel holen wir unsere Daten von der [OpenStreetMap Nomination API](https://nominatim.org/release-docs/develop/api/Overview/nominatim-api). Diese ist erreichbar über die URL `https://nominatim.openstreetmap.org/search?`. Um z.B. Informationen über das Berlin Congress Center in Berlin im JSON-Format zu erhalten, sollte die URL `https://nominatim.openstreetmap.org/search.php?q=Alexanderplatz+Berlin&format=json` angegeben werden, und wenn ihr euch den entsprechenden Kartenausschnitt anzeigenlassen wollt, so müsst ihr einfach nur `&format=json` weglassenAnschließend definieren wir die Basis-URL und die Parameter. Nominatim erwartet mindestens die folgenden beiden Parameter| Schlüssel | Werte                                || --------- | ------------------------------------ || `q` | Adressabfrage, die folgende Spezifikationen erlaubt: `street`, `city`, `county`, `state`, `country` und `postalcode`. || `format` | Format, in dem die Daten zurückgegeben werden. Möglich Werte sind `html`, `xml`, `json`, `jsonv2`, `geojson` und `geocodejson`. |Die Abfrage kann dann gestellt werden mit:<jupyter_code>import requests

base_url = 'https://nominatim.openstreetmap.org/search?'
params = {
    'q': 'Alexanderplatz, Berlin',
    'format':'json',
}
r = requests.get(base_url,params=params)
r.status_code
r.json()<jupyter_output><empty_output><jupyter_text>Es werden drei verschiedene Orte gefunden, der Platz, eine Bushaltestelle und ein Hotel. Um nun weiter filtern zu können, können wir uns nur  den bedeutendsten Ort anzeigen lassen:<jupyter_code>params = {
    'q': 'Alexanderplatz, Berlin',
    'format':'json',
    'limit':'1'
}
r = requests.get(base_url,params=params)
r.json()<jupyter_output><empty_output><jupyter_text>Clean Code Nachdem wir nun wissen, dass der Code funktioniert, wollen wir alles in eine saubere und flexible Funktion umwandeln.  Um sicherzustellen, dass die Interaktion erfolgreich war, verwenden wir die Methode `raise_for_status` von `requests`, die eine Exception auslöst, wenn der HTTP-Statuscode nicht `200 OK` ist:<jupyter_code>r.raise_for_status()<jupyter_output><empty_output><jupyter_text>Da wir die Lastgrenzen der Nomination-API nicht überschreiten möchten, werden wir unsere Anforderungen mit der Funktion `time.sleep` verzögern:<jupyter_code>from time import sleep

sleep(1)
r.json()<jupyter_output><empty_output><jupyter_text>Als nächstes deklarieren wir die Funktion selbst. Als Argumente benötigen wir die Adresse, das Format, das Limit der zurückzugebenden Objekte mit dem Standardwert `1` und weitere `kwargs` (**k**ey**w**ord **arg**ument**s**), die als Parameter übergeben werden:<jupyter_code>def nominatim_search(address, format="json", limit=1, **kwargs):
    """Thin wrapper around the Nominatim search API.
    For the list of parameters see 
    https://nominatim.org/release-docs/develop/api/Search/#parameters
    """
    search_url = "https://nominatim.openstreetmap.org/search?"
    params = {"q": address, "format": format, "limit": limit, **kwargs}
    r = requests.get(search_url, params=params)
    # Raise an exception if the status is unsuccessful
    r.raise_for_status()

    sleep(1)
    return r.json()<jupyter_output><empty_output><jupyter_text>Nun können wir die Funktion ausprobieren, z.B. mit<jupyter_code>nominatim_search('Alexanderplatz, Berlin')<jupyter_output><empty_output><jupyter_text>Ihr könnt jedoch neben `address` noch weitere Parameter verwenden. Eine Übersicht erhaltet ihr in den [Nominatim Docs](https://nominatim.org/release-docs/develop/api/Search/parameters).<jupyter_code>nominatim_search(address=None, street='8, Marienburger Straße', city='Berlin',
    country='Germany')<jupyter_output><empty_output><jupyter_text>CachingFalls innerhalb einer Session immer wieder dieselben Abfragen gestellt werden sollen,ist es sinnvoll, diese Daten nur einmal abzurufen und wiederzuverwenden. In Python können wir `lru_cache` aus der `functools`-Standardbibliothek von Python verwenden. `lru_cache` speichert die N letzten Anfragen (**L**east **R**ecent **U**sed) und sobald das Limit überschritten wird, werden die ältesten Werte verworfen. Um dies für die Methode `nominatim_search` zu verwenden, müsst ihr lediglich einen Import und einen *Decorator* defnieren:<jupyter_code>from functools import lru_cache

@lru_cache(maxsize=1000)
def nominatim_search(address, format='json', limit=1, **kwargs):
     '''…
     '''<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11411530815109344, 'fraction_numerical': 0.004771371769383698, 'mean_word_length': 5.699067909454061, 'pattern_counts': {'":': 3, '<': 33, '<?xml version=': 0, '>': 33, 'https://': 9, 'lorem ipsum': 0, 'www.': 0, 'xml': 1}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '469596', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 1678, 'n_tokens_neox': 1679, 'n_words': 453, 'path': 'docs/data-processing/requests/requests.ipynb', 'repo_name': 'veit/jupyter-tutorial-de', 'size': '5030'}