Da Darksky von Apple übernommen wurde, kann man keine neuen kostenlosen Accounts mehr anlegen. Deshalb hier das zweite Update, diesmal mit dem kostenfreien Service Openweathermap.
Inhalt
Update vom 2. Update:
April 2024: Die API Version 2.5 läuft im Juni 2024 aus und wird mit Version 3.0 ersetzt. Der Service ist bis zu 1000 Abrufen/Tag immer noch kostenlos, aber man muss den Service trotzdem mit einer Kreditkarte abonnieren (subscribe). Wenn man unter den 1000 Abrufen/Tag bleibt, wird die Karte nicht belastet. Funktionell bleibt alles beim Alten. d.h. im Aufruf muss lediglich die API Version von 2.5 auf 3.0 geändert werden. Der API Key bleibt unverändert. Dieser Beitrag wurde daher insgesamt auf den neuesten Stand gebracht.
Also zum Beispiel: https://api.openweathermap.org/data/<strong>3.0</strong>/onecall?lat={lat}&lon={lon}&exclude={part}&appid={API key}
Nach der Subscription dauert es u.U. ein paar Stunden, bis der API Key umgestellt ist und die Abrufe mit 3.0 klappen.
-.-.-.-.-.-.-.-.-.-.-.-.-
In meine Webcam möchte ich einen Wetterbericht mit einigen Tagen Vorausschau einblenden.
Die bisher von mir verwendete Webservices von Weather Underground und Darksky.net stellen keine neuen kostenfreien APIs mehr zur Verfügung. Ich bin deshalb auf Openweathermap umgestiegen, in der Hoffnung, dass der Service etwas länger bestehen bleibt.
Openweathermap.org ist ein sehr mächtiger Service mit aktuellem Wetter, stundengenauer 4 Tage Vorhersage, 16 Tage Vorhersage, Klimavorhersage über 30 Tage und inzwischen gibt es auch die sogenannte "One Call API" mit vielen kostenfreien Features wie minutengenauer Vorhersage für die nächste Stunde, 7 Tage Vorhersage, 5 Tages Rückschau etc. Die Wettertexte sind leider nicht ganz so ausführlich wie die von Darksky.
Die 7 Tage Vorhersage aus der One Call API ist genau das, was ich brauche.
Mondphasen gibt es hier leider keine: vielleicht etwas nerdig, aber ich will sowieso eine möglichst genaue Anzeige der Mondphase und der sichtbaren Mondoberfläche in Prozent. Ich errechne deshalb die Mondphase selbst.
Update April 2024: Wie ich beim Ansehen der neuen API gemerkt habe, wird jetzt auch die Montphase als numerischer Wert ausgegeben.
Schritt 1: Account anlegen
Surft zu https://home.openweathermap.org/users/sign_in und klickt den Button "Create an account" an.
Die Registrierung ist selbsterklärend. Am Schluss erhaltet ihr euren eigenen geheimen API Schlüssel [key] der Art "700416988c7436cd5abc92c328d2b320". Mit diesem Schlüssel müsst ihr euch bzw. euer Programm bei künftigen Abfragen ausweisen.
Seit 2024 muss man sich mit einer Kreditkarte registrieren. Bleibt man unter der 1000er Freigrenze, wird die Karte nicht belastet.
Schritt 2: Webservice ansprechen
Irgendwie müssen die Daten aus dem Webservice in Python eingelesen werden. Dafür gibt es in Python Standardmethoden. Mit der standardmäßig vorhandenen urllib2 Library habe ich mich allerdings etwas schwer getan. Die neuere Library "Requests" ist meines Erachtens leichter zu bedienen, enthält netterweise auch die benötigten JSON Funktionen, muss aber erst noch installiert werden. Doch keine Angst, die Installation ist unkompliziert:
Requests is an elegant and simple HTTP library for Python, built for human beings.
Der Entwickler empfiehlt, das Paket mit pip
zu installieren:
1 |
sudo pip install requests |
Wenn es zu einer Fehlermeldung kommt, weil pip
noch nicht installiert ist, dann muss das pip
Paket ebenfalls installiert werden – am besten gleich noch sicherstellen, dass ein weiteres wichtiges Entwicklerpaket mit installiert wird:
sudo apt-get install python-pip python-dev
anschließend requests
wie oben dargestellt installieren.
Wer tiefer einsteigen will: weitere Info gibts bei Python Requests.
Damit ist euer Pi für die folgenden Schritte gerüstet.
Schritt 3: JSON verarbeiten
Bezüglich der Openweathermap angebotenen Daten und deren Struktur gibt es eine gute Dokumentation auf deren Site https://openweathermap.org/api/one-call-api.
a: Das JSON Format anschauen
Um das Prinzip zu verdeutlichen, nehmen wir uns für den Anfang den allgemeinen Wetterüberblick vor.
Gebt zuerst einmal folgende Zeile in euren Browser ein:
1 |
https://api.openweathermap.org/data/3.0/onecall?lat=48.1404&lon=11.5583&units=metric&lang=de&appid=DeinenGeheimenKey |
Nach dem Fragezeichen werden die Aufrufparameter übergeben. Zuerst kommen die Geokoordinaten (lan/lon bzw. Breite und Länge) für den Ort, für den ihr die heutigen Wetterdaten wissen wollt – in diesem Beispiel ist es der Hauptbahnhof München. Die weiteren Parameter werden dann untereinander durch ein Ampersand "&" getrennt werden. Beim obigen Beispiel ist das die Sprache (lang=de) sowie metrische Einheiten ( &units=metric) und zum Schluss anstatt DeinenGeheimenKey natürlich euer eigener, vorhin generierter Key.
Hinweis: Geokoordinaten bekommt ihr heraus, indem ihr bei Google Maps die gewünschte Gegend anzeigt und dann einen Rechtsklick auf die Stelle macht, deren Koordinaten ihr wissen wollt. Dann "was ist hier" auswählen. In einem kleinen Popup unten auf der Karte werden dann die Geokoordinaten schon im richtigen Format angezeigt. Diese einfach übernehmen und in die Codezeile eintragen.
Ich arbeite praktisch nur mit dem Firefox Browser, da weiß ich wenigstens, dass mein Surfverhalten nicht weiterverkauft wird – anders als bei Chrome oder Edge. Firefox hat eine sehr gute eingebaute Darstellung des empfangenen JSON Objekts. Edge und Chome können das übrigens von Haus aus nicht.
Das im Firefox Browser angezeigte Ergebnis für den 23. Oktober 2020 ist dann wie folgt, wobei ich hier der Übersichtlichkeit halber die Daten für minutely und hourly eingeklappt habe:
Diese wunderschön strukturierten Informationen können wir in Python jetzt ganz elegant verarbeiten.
Exkurs: Auch eine schön lesbare Struktur erhaltet ihr mit meinem Lieblingseditor Notepad++, der allerdings noch über den Plugin Manager die Erweiterung "JSON Viewer" bekommen muss. Notepad++ gibts kostenlos im Internet. Tante Google fragen.
Dazu den Menüpunkt Erweiterungen auswählen und dann auf Plugin Manager gehen. Dort dann unter dem Tab Available den JSON Viewer auswählen und auf Install klicken.
Zum Ansehen, den JSON String aus dem Browser in ein Bearbeitungsfenster von Notepad++ reinkopieren, mit der Maus komplett markieren und dann unter Erweiterungen – JSON Viewer – Format JSON anklicken. Et voilà, JSON ist so noch besser anzusehen.
Ganz toll ist auch der Online JSON Editor bei http://jsoneditoronline.org/
b: Ein JSON Ojekt erzeugen
In die erste Zeile unseres Progrämmchens kommt das "Shebang" für Python. Dann noch die Codierung für UTF-8
1 2 |
!/usr/bin/python #coding=UTF-8 |
Anschließend wird die soeben installierte Requests Bibliothek importiert
1 |
import requests |
Zunächst einmal müssen wir den Webservice mit dem oben ausprobierten Aufruf ansprechen und den gelieferten Output einem Python Objekt [hier: r] zuweisen.
1 |
r=requests.get("https://api.openweathermap.org/data/3.0/onecall?lat=48.1404&lon=11.5583&units=metric&lang=de&exclude=minutely,hourly&appid=DeinKey") |
Damit kann man allerdings herzlich wenig anfangen. Das Objekt muss noch als JSON String geparsed (d.h. zergliedert) werden:
data = r.json()
data
ist ein Python Dictionary Objekt und enthält den ganzen JSON String , nicht besonders schön anzusehen, aber komplett.
Hier noch das ganze Code Snippet:
1 2 3 4 5 6 7 8 9 10 |
#!/usr/bin/python #coding=UTF-8 import requests #get webservice data r = requests.get("https://api.openweathermap.org/data/3.0/onecall?lat=48.1404&lon=11.5583&units=metric&lang=de&exclude=minutely,hourly&appid=DeinKey", timeout=120) data = r.json() # data contains the parsed JSON string print data r.close() |
und hier der Output davon:
1 |
{u'timezone_offset': 7200, u'lon': 10.74, u'daily': [{u'wind_speed': 1.46, u'clouds': 87, u'dew_point': 10.03, u'temp': {u'min': 10.41, u'max': 12.64, u'eve': 12.33, u'morn': 10.98, u'night': 12.64, u'day': 11.78}, u'uvi': 2.31, u'pop': 0.77, u'humidity': 89, u'pressure': 1020, u'weather': [{u'main': u'Rain', u'id': 501, u'icon': u'10d', u'description': u'M\xe4\xdfiger Regen'}], u'sunset': 1603469870, u'rain': 10.15, u'dt': 1603450800, u'wind_deg': 49, u'feels_like': {u'eve': 12.31, u'morn': 9.92, u'day': 10.81, u'night': 12.41}, u'sunrise': 1603431871}, {u'wind_speed': 2.03, u'clouds': 67, u'dew_point': 10.66, u'temp': {u'min': 11.8, u'max': 15.22, u'eve': 12.36, u'morn': 11.92, u'night': 11.86, u'day': 15.22}, u'uvi': 2.47, u'pop': 0.96, u'humidity': 74, u'pressure': 1017, u'weather': [{u'main': u'Rain', u'id': 501, u'icon': u'10d', u'description': u'M\xe4\xdfiger Regen'}], u'sunset': 1603556173, u'rain': 5.72, u'dt': 1603537200, u'wind_deg': 12, u'feels_like': {u'eve': 10.56, u'morn': 12.25, u'day': 14.01, u'night': 9.1}, u'sunrise': 1603518354}, {u'wind_speed': 1.96, u'clouds': 1, u'dew_point': 8.78, u'temp': {u'min': 10.04, u'max': 14.68, u'eve': 11.87, u'morn': 10.04, u'night': 11.54, u'day': 14.68}, u'uvi': 2.14, u'pop': 0.04, u'humidity': 67, u'pressure': 1015, u'weather': [{u'main': u'Clear', u'id': 800, u'icon': u'01d', u'description': u'Klarer Himmel'}], u'sunset': 1603642476, u'dt': 1603623600, u'wind_deg': 217, u'feels_like': {u'eve': 11.42, u'morn': 6.36, u'day': 12.99, u'night': 11.03}, u'sunrise': 1603604837}, {u'wind_speed': 1.16, u'clouds': 92, u'dew_point': 9.98, u'temp': {u'min': 10.59, u'max': 12.32, u'eve': 11.38, u'morn': 11.36, u'night': 10.59, u'day': 12.32}, u'uvi': 1.91, u'pop': 1, u'humidity': 85, u'pressure': 1007, u'weather': [{u'main': u'Rain', u'id': 502, u'icon': u'10d', u'description': u'Starker Regen'}], u'sunset': 1603728781, u'rain': 23.99, u'dt': 1603710000, u'wind_deg': 52, u'feels_like': {u'eve': 9.97, u'morn': 10.27, u'day': 11.52, u'night': 9.67}, u'sunrise': 1603691321}, {u'wind_speed': 6.84, u'clouds': 88, u'dew_point': 5.37, u'temp': {u'min': 7.22, u'max': 10.57, u'eve': 8.15, u'morn': 8.67, u'night': 7.22, u'day': 8.46}, u'uvi': 2.12, u'pop': 1, u'humidity': 80, u'pressure': 1007, u'weather': [{u'main': u'Rain', u'id': 501, u'icon': u'10d', u'description': u'M\xe4\xdfiger Regen'}], u'sunset': 1603815087, u'rain': 8.88, u'dt': 1603796400, u'wind_deg': 16, u'feels_like': {u'eve': 4.09, u'morn': 5.11, u'day': 2.59, u'night': 3.22}, u'sunrise': 1603777805}, {u'wind_speed': 0.86, u'clouds': 36, u'dew_point': 3.51, u'temp': {u'min': 5.93, u'max': 11.52, u'eve': 8.64, u'morn': 5.93, u'night': 7.8, u'day': 11.52}, u'uvi': 2.03, u'pop': 0, u'humidity': 57, u'pressure': 1016, u'weather': [{u'main': u'Clouds', u'id': 802, u'icon': u'03d', u'description': u'M\xe4\xdfig bew\xf6lkt'}], u'sunset': 1603901394, u'dt': 1603882800, u'wind_deg': 259, u'feels_like': {u'eve': 6.32, u'morn': 0.76, u'day': 9.47, u'night': 4.57}, u'sunrise': 1603864289}, {u'wind_speed': 1.23, u'clouds': 71, u'dew_point': 3.93, u'temp': {u'min': 6.37, u'max': 11.3, u'eve': 8.74, u'morn': 6.37, u'night': 8.5, u'day': 11.3}, u'uvi': 2.07, u'pop': 0, u'humidity': 60, u'pressure': 1022, u'weather': [{u'main': u'Clouds', u'id': 803, u'icon': u'04d', u'description': u'\xdcberwiegend bew\xf6lkt'}], u'sunset': 1603987703, u'dt': 1603969200, u'wind_deg': 142, u'feels_like': {u'eve': 7.03, u'morn': 1.75, u'day': 9.09, u'night': 5.65}, u'sunrise': 1603950774}, {u'wind_speed': 0.79, u'clouds': 100, u'dew_point': 4.3, u'temp': {u'min': 7.08, u'max': 12.37, u'eve': 9.74, u'morn': 7.08, u'night': 9.07, u'day': 12.37}, u'uvi': 2.03, u'pop': 0, u'humidity': 57, u'pressure': 1026, u'weather': [{u'main': u'Clouds', u'id': 804, u'icon': u'04d', u'description': u'Bedeckt'}], u'sunset': 1604074013, u'dt': 1604055600, u'wind_deg': 192, u'feels_like': {u'eve': 7.93, u'morn': 2.75, u'day': 10.52, u'night': 6.69}, u'sunrise': 1604037259}], u'current': {u'wind_speed': 0.5, u'clouds': 75, u'dew_point': 9.47, u'temp': 11.56, u'uvi': 2.31, u'visibility': 1800, u'humidity': 87, u'pressure': 1021, u'weather': [{u'main': u'Rain', u'id': 501, u'icon': u'10d', u'description': u'M\xe4\xdfiger Regen'}, {u'main': u'Mist', u'id': 701, u'icon': u'50d', u'description': u'Tr\xfcb'}], u'sunset': 1603469870, u'rain': {u'1h': 1.42}, u'dt': 1603448772, u'wind_deg': 0, u'feels_like': 11.11, u'sunrise': 1603431871}, u'lat': 45.66, u'timezone': u'Europe/Rome'} |
c: Einzelne JSON Elemente ansprechen
Um beispielsweise direkt die Stunde des Sonnenaufgangs anzusprechen und in eine Variable hineinzuschreiben, muss man sich ganz einfach durch die Hierarchie des Directory Elements hangeln. An oberster Stelle kommt daily
dann die fortlaufende Nummer, wobei 0
für den aktuellen Tag steht und schließlich das Element
sunrise
In Python ausgedrückt sieht das so aus:
1 |
sunrise_time = data['daily'][0]['sunrise'] |
Die Zeit wird allerdings als UNIX Zeitstempel, eine 10stellige Zahl, ausgegeben. Zur lesbaren Darstellung kann man diesen Wert mittels strftime natürlich gleich konvertieren. Hierzu muss allerdings noch "datetime" aus der datetime Bibliothek importiert werden.
from datetime import datetime as dt
Die darin steckenden Routinen brauchen wir später auch noch für die Anzeige des Wochentags. Das as dt ist nur eine wahlfreie Abkürzung zur Vermeidung von Schreibarbeit.
1 |
sunrise_time = dt.fromtimestamp(data['daily'][0]['sunrise']).strftime('%H:%M') |
Hinweis: Openweathermap bezieht sich bei den Zeitangaben immer auf die Zeit des Ortes, dessen Koordinaten man eingegeben hat. Es ist also keine Konversion von UTC (Universal Time Coordinated) nötig. Die Zeitstempel für die einzelnen Tages-Wetterberichte beziehen sich immer auf 0 Uhr Ortszeit.
d: Iteration
Bei iterativen Daten, z.B. der Wettervorhersage für heute und die nächsten 7 Tage kann man das natürlich auch per Schleife auslesen. Die Schleife läuft über den Wert 'day' in der zweiten Ebene
['daily'][day]
also die Stelle, bei der wir vorhin nur die "0" verwendet haben.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#!/usr/bin/python #coding=UTF-8 #get webservice data import requests from datetime import datetime as dt from datetime import date #UTF-8 assignments degreeChar = u'\N{DEGREE SIGN}' # inits fcast = [] # create array wdays = ["Mo","Di","Mi","Do","Fr","Sa","So"] r = requests.get("https://api.openweathermap.org/data/2.5/onecall?lat=48.14&lon=11.55&units=metric&lang=de&exclude=minutely,hourly&appid=deinKey", timeout=120) data = r.json() # data contains the parsed JSON string r.close() # Get sunrise/set in readable format sunrise_time = dt.fromtimestamp(data['daily'][0]['sunrise']).strftime('%H:%M') sunset_time = dt.fromtimestamp(data['daily'][0]['sunset']).strftime('%H:%M') # daily forecast today plus next 7 days for day in data['daily']: fcdaytext = day['weather'][0]['description'] fcdegmax = day['temp']['max'] fcdegmin = day['temp']['min'] fcweekday = date.fromtimestamp(day['dt']).weekday() fctext = "%s: %s %1.0f%sC/%1.0f%sC" % (wdays[fcweekday],fcdaytext,fcdegmax,degreeChar,fcdegmin,degreeChar) fcast.append(fctext) # for further processing print "%s %1.0f%sC/%1.0f%sC" % (fctext,fcdegmax,degreeChar,fcdegmin,degreeChar) ### end get forecast print "Sonnenaufgang: %s, Sonnenuntergang: %s" % (sunrise_time, sunset_time) |
Beim requests Statement habe ich noch einen 120 Sekunden Timeout eingebaut, damit das Programm bei einem Abrufproblem nicht ewig hängt. Am besten fängt man Zugriffsfehler mit try: except: Blöcken ab.
Der Array fcst[x]
enthält die Wettervorhersage für den Tag 0 bis 7, also acht Tage. Diese Daten könnt ihr nach Belieben in euren Programmen weiterverwenden.
Ich persönlich verwende nur die ersten vier Tage des Wetterberichts. Hier ist die Eintrittswahrscheinlichkeit noch relativ groß.
Das Gradzeichen ist im normalen Zeichensatz nicht vorhanden und wird über degreeChar = u'\N{DEGREE SIGN}'
erzeugt.
Der Output sieht dann hoffentlich so ähnlich wie hier aus.
1 2 3 4 5 6 7 8 9 |
Fr: Mäßiger Regen 16°C/12°C 16°C/12°C Sa: Mäßiger Regen 14°C/9°C 14°C/9°C So: Klarer Himmel 17°C/6°C 17°C/6°C Mo: Mäßiger Regen 9°C/6°C 9°C/6°C Di: Mäßiger Regen 8°C/5°C 8°C/5°C Mi: Leichter Regen 10°C/3°C 10°C/3°C Do: Leichter Regen 12°C/6°C 12°C/6°C Fr: Leichter Regen 15°C/9°C 15°C/9°C Sonnenaufgang: 07:45, Sonnenuntergang, 18:10 |
Bei Interesse einfach tiefer bei Openweathermap graben, es gibt da alle möglichen Wetter-Informationen.
Dieser Beitrag wurde inspiriert von der wirklich gut gemachten Site http://www.pythonforbeginners.com/scraping/scraping-wunderground wobei auch diese Seite jetzt veraltet sein dürfte.