Die Webdienste der Google Maps Platform sind eine Sammlung von HTTP-Schnittstellen zu Google-Diensten, die geografische Daten für Ihre Kartenanwendungen bereitstellen.
In diesem Leitfaden werden einige gängige Best Practices für das Einrichten von Webdienst-Anfragen und das Verarbeiten von Dienstantworten beschrieben. Eine vollständige Dokumentation zur Geocoding API finden Sie im Entwicklerhandbuch.
Was ist ein Webdienst?
Die Webdienste der Google Maps Platform sind eine Schnittstelle, über die Sie Maps API-Daten von externen Diensten anfordern und in Ihren Maps-Anwendungen verwenden können. Diese Dienste sind gemäß den Lizenzbeschränkungen in den Nutzungsbedingungen für die Google Maps Platform für die Verwendung in Verbindung mit einer Karte vorgesehen.
Die Google Maps-APIs verwenden HTTP(S)-Anfragen an bestimmte URLs und übergeben URL-Parameter und/oder POST-Daten im JSON-Format als Argumente an die Dienste. Im Allgemeinen geben diese Dienste Daten im Antworttext entweder als JSON oder XML zurück, damit sie von Ihrer Anwendung geparst und/oder verarbeitet werden können.
Eine typische Geocoding API-Anfrage hat im Allgemeinen das folgende Format:
https://maps.googleapis.com/maps/api/geocode/output?parameters
Dabei gibt output das Antwortformat an (normalerweise json oder xml).
Hinweis: Für alle Geocoding API-Anwendungen ist eine Authentifizierung erforderlich. Weitere Informationen zu Authentifizierungsdaten
SSL/TLS-Zugriff
HTTPS ist für alle Google Maps Platform-Anfragen erforderlich, die API-Schlüssel verwenden oder Nutzerdaten enthalten. Anfragen, die über HTTP gestellt werden und sensible Daten enthalten, werden möglicherweise abgelehnt.
Gültige URL erstellen
Es mag den Anschein haben, dass „gültige“ URLs eine Selbstverständlichkeit sind. Das ist jedoch nicht der Fall. So kann beispielsweise eine URL, die in die Adresszeile eines Browsers eingegeben wird, Sonderzeichen wie "上海+中國" enthalten. Der Browser muss diese Zeichen vor der Übertragung intern in eine andere Codierung umwandeln.
Ebenso ist es möglich, dass Code, der UTF-8-Eingaben erzeugt oder akzeptiert, URLs mit UTF-8-Zeichen als „gültig“ behandelt; diese Zeichen müssten jedoch vor dem Senden an einen Webbrowser ebenfalls umgewandelt werden.
Dieser Vorgang wird als URL-Codierung oder Prozentcodierung bezeichnet.
Sonderzeichen
Sonderzeichen müssen umgewandelt werden, da alle URLs der Syntax entsprechen müssen, die in der Spezifikation Uniform Resource Identifier (URI) angegeben ist. Das bedeutet, dass URLs nur einen Teil der ASCII-Zeichen enthalten dürfen: die bekannten alphanumerischen Symbole und einige reservierte Zeichen, die in den URLs als Steuerzeichen dienen. Hier eine Übersicht:
| Zeichensatz | Zeichen | Verwendung in der URL |
|---|---|---|
| Alphanumerisch | a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 | Textstrings, Schemas (http), Portangaben (8080) usw. |
| Nicht reserviert | - _ . ~ | Textstrings |
| Reserviert | ! * ' ( ) ; : @ & = + $ , / ? % # [ ] | Steuerzeichen und/oder Textstrings |
Beachten Sie bei der Generierung einer URL, dass diese nur die in der Tabelle aufgeführten Zeichen enthalten darf. Die Anpassung der URL an diesen Zeichensatz führt in der Regel zu zwei Problemen, nämlich dass Zeichen weggelassen oder ersetzt werden müssen:
- Die Zeichen, die Sie verarbeiten möchten, sind nicht im obigen Zeichensatz enthalten. So müssen beispielsweise Zeichen ausländischer Sprachen, wie
上海+中國, mithilfe der oben angegebenen Zeichen codiert werden. Auch werden Leerzeichen, die innerhalb von URLs nicht zulässig sind, entsprechend den geltenden Konventionen oftmals durch das Zeichen'+'dargestellt. - Die Zeichen sind im obigen Zeichensatz als reservierte Zeichen enthalten, müssen aber im ursprünglichen Sinn des Zeichens verwendet werden.
So wird beispielsweise
?in URLs für den Beginn eines Abfragestrings verwendet. Möchten Sie es stattdessen für den Text „? and the Mysterions“ verwenden, müssen Sie das Zeichen'?'codieren.
Alle Zeichen, die als URL codiert werden sollen, werden mithilfe des Zeichens '%' und eines Hexadezimalwerts aus zwei Zeichen codiert, der ihrem UTF-8-Zeichen entspricht. So würde zum Beispiel der UTF-8-String 上海+中國 durch die URL-Codierung in %E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B umgewandelt. Und aus ? and the Mysterians würde %3F+and+the+Mysterians oder %3F%20and%20the%20Mysterians werden.
Häufig vorkommende Zeichen, die codiert werden müssen
Folgende häufig vorkommende Zeichen müssen codiert werden:
| Unsicheres Zeichen | Codierter Wert |
|---|---|
| Leerzeichen | %20 |
| " | %22 |
| < | %3C |
| > | %3E |
| # | %23 |
| % | %25 |
| | | %7C |
Die Konvertierung von URLs, die aus Nutzereingaben empfangen werden, kann manchmal Probleme mit sich bringen. Beispielsweise kann ein Nutzer eine Adresse als „5th&Main St.“ eingeben. Im Allgemeinen sollten Sie die URL aus ihren Teilen erstellen und jede Nutzereingabe wortwörtlich betrachten.
Außerdem sind URLs für alle Google Maps Platform-Webdienste und statischen Web APIs auf 16.384 Zeichen beschränkt. Bei den meisten Diensten wird diese Zeichenbeschränkung selten erreicht. Beachten Sie jedoch, dass bestimmte Dienste einige Parameter haben, die zu langen URLs führen können.
Respektvolle Nutzung der Google APIs
Schlecht konzipierte API-Clients können sowohl das Internet als auch die Google-Server unnötig belasten. Dieser Abschnitt enthält einige bewährte Methoden für Kunden der APIs. Wenn Sie diese Best Practices befolgen, können Sie verhindern, dass Ihre Anwendung aufgrund eines unbeabsichtigten Missbrauchs der APIs blockiert wird.
Fehler und Wiederholversuche
Informationen zu den Antwortcodes UNKNOWN_ERROR oder OVER_QUERY_LIMIT der Geocoding API finden Sie unter Fehler und Wiederholungsversuche verwalten.
Exponential Backoff
In seltenen Fällen kann bei der Bearbeitung Ihrer Anfrage ein Fehler auftreten. Sie erhalten dann möglicherweise einen HTTP-Antwortcode vom Typ 4XX oder 5XX oder die TCP-Verbindung schlägt einfach irgendwo zwischen Ihrem Client und dem Google-Server fehl. Häufig lohnt es sich, die Anfrage noch einmal zu senden, da die Folgeanfrage erfolgreich sein kann, wenn die ursprüngliche Anfrage fehlgeschlagen ist. Es ist jedoch wichtig, nicht einfach wiederholt Anfragen an die Google-Server zu senden. Dieses Schleifenverhalten kann das Netzwerk zwischen Ihrem Client und Google überlasten und Probleme für viele Beteiligte verursachen.
Ein besserer Ansatz ist es, wiederholte Versuche in immer größeren Abständen durchzuführen. Normalerweise wird die Verzögerung bei jedem Versuch um einen Multiplikationsfaktor erhöht. Dieser Ansatz wird als exponentieller Backoff bezeichnet.
Angenommen, eine Anwendung möchte die folgende Anfrage an die Time Zone API senden:
https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510×tamp=1331161200&key=YOUR_API_KEYIm folgenden Beispiel mit Python wird gezeigt, wie die Anforderung mit exponentiellem Backoff durchgeführt wird:
import json import time import urllib.error import urllib.parse import urllib.request # The maps_key defined below isn't a valid Google Maps API key. # You need to get your own API key. # See https://developers.google.com/maps/documentation/timezone/get-api-key API_KEY = "YOUR_KEY_HERE" TIMEZONE_BASE_URL = "https://maps.googleapis.com/maps/api/timezone/json" def timezone(lat, lng, timestamp): # Join the parts of the URL together into one string. params = urllib.parse.urlencode( {"location": f"{lat},{lng}", "timestamp": timestamp, "key": API_KEY,} ) url = f"{TIMEZONE_BASE_URL}?{params}" current_delay = 0.1 # Set the initial retry delay to 100ms. max_delay = 5 # Set the maximum retry delay to 5 seconds. while True: try: # Get the API response. response = urllib.request.urlopen(url) except urllib.error.URLError: pass # Fall through to the retry loop. else: # If we didn't get an IOError then parse the result. result = json.load(response) if result["status"] == "OK": return result["timeZoneId"] elif result["status"] != "UNKNOWN_ERROR": # Many API errors cannot be fixed by a retry, e.g. INVALID_REQUEST or # ZERO_RESULTS. There is no point retrying these requests. raise Exception(result["error_message"]) if current_delay > max_delay: raise Exception("Too many retry attempts.") print("Waiting", current_delay, "seconds before retrying.") time.sleep(current_delay) current_delay *= 2 # Increase the delay each time we retry. if __name__ == "__main__": tz = timezone(39.6034810, -119.6822510, 1331161200) print(f"Timezone: {tz}")
Achten Sie auch darauf, dass es in der Aufrufkette der Anwendung keinen Wiederholungscode gibt, der zu wiederholten Anfragen in schneller Folge führt.
Synchronisierte Anforderungen
Eine große Anzahl synchronisierter Anfragen an die APIs von Google kann wie ein DDoS-Angriff (Distributed Denial of Service) auf die Infrastruktur von Google aussehen und entsprechend behandelt werden. Um dies zu vermeiden, sollten Sie darauf achten, dass API-Anfragen nicht zwischen Clients synchronisiert werden.
Angenommen, eine Anwendung zeigt die Zeit in der aktuellen Zeitzone an. Diese Anwendung stellt wahrscheinlich einen Wecker im Client-Betriebssystem ein, der es zu Beginn der Minute weckt, damit die angezeigte Zeit aktualisiert werden kann. Die Anwendung sollte im Rahmen der Verarbeitung, die mit diesem Alarm verbunden ist, keine API-Aufrufe ausführen.
API-Aufrufe als Reaktion auf einen festen Alarm sind nicht empfehlenswert, da die API-Aufrufe dann mit dem Beginn der Minute synchronisiert werden, auch zwischen verschiedenen Geräten, anstatt gleichmäßig über die Zeit verteilt zu werden. Eine schlecht konzipierte Anwendung, die dies tut, erzeugt zu Beginn jeder Minute eine Trafficspitze, die das Sechzigfache des normalen Niveaus beträgt.
Stattdessen wird bei einer guten Lösung ein zweiter Alarm für eine zufällig gewählte Zeit festgelegt. Wenn dieser zweite Alarm ausgelöst wird, ruft die Anwendung alle erforderlichen APIs auf und speichert die Ergebnisse. Wenn die Anwendung die Anzeige zu Beginn der Minute aktualisieren möchte, verwendet sie zuvor gespeicherte Ergebnisse, anstatt die API noch einmal aufzurufen. Bei diesem Ansatz werden API-Aufrufe gleichmäßig über die Zeit verteilt. Außerdem verzögern die API-Aufrufe das Rendering nicht, wenn die Anzeige aktualisiert wird.
Neben dem Beginn einer Minute sollten Sie auch den Beginn einer Stunde und den Beginn eines Tages um Mitternacht nicht als Synchronisierungszeitpunkte verwenden.
Verarbeiten von Antworten
In diesem Abschnitt wird erklärt, wie Sie diese Werte dynamisch aus den Webdienstantworten extrahieren.
Die Google Maps-Webdienste liefern Antworten, die leicht verständlich, aber nicht gerade nutzerfreundlich sind. Wenn Sie eine Abfrage ausführen, möchten Sie wahrscheinlich nicht eine Reihe von Daten anzeigen, sondern einige bestimmte Werte extrahieren. In der Regel parsen Sie die Antworten des Webdienstes und extrahieren nur die Werte, die Sie interessieren.
Das verwendete Parsingschema hängt davon ab, ob Sie die Ausgabe im XML- oder JSON-Format zurückgeben. JSON-Antworten liegen bereits in Form von JavaScript-Objekten vor und können auf dem Client in JavaScript verarbeitet werden. XML-Antworten sollten mit einem XML-Prozessor und einer XML-Abfragesprache verarbeitet werden, um Elemente im XML-Format anzusprechen. In den folgenden Beispielen verwenden wir XPath, da es in XML-Verarbeitungsbibliotheken häufig unterstützt wird.
Verarbeiten von XML mit XPath
XML ist ein relativ ausgereiftes strukturiertes Informationsformat, das für den Datenaustausch verwendet wird. XML ist zwar nicht so einfach wie JSON, bietet aber mehr Sprachunterstützung und leistungsfähigere Tools. Code für die Verarbeitung von XML in Java ist beispielsweise in die javax.xml-Pakete integriert.
Bei der Verarbeitung von XML-Antworten sollten Sie eine geeignete Abfragesprache verwenden, um Knoten im XML-Dokument auszuwählen, anstatt davon auszugehen, dass sich die Elemente an absoluten Positionen im XML-Markup befinden. XPath ist eine Sprachsyntax, mit der Knoten und Elemente in einem XML-Dokument eindeutig beschrieben werden. Mit XPath-Ausdrücken können Sie bestimmte Inhalte im XML-Antwortdokument identifizieren.
XPath-Ausdrücke
Einige Kenntnisse über XPath sind für die Entwicklung eines robusten Parsingschemas sehr hilfreich. In diesem Abschnitt geht es darum, wie Elemente in einem XML-Dokument mit XPath adressiert werden, sodass Sie mehrere Elemente adressieren und komplexe Abfragen erstellen können.
XPath verwendet Ausdrücke, um Elemente in einem XML-Dokument auszuwählen. Die Syntax ähnelt der für Verzeichnispfade. Diese Ausdrücke identifizieren Elemente in einem XML-Dokumentbaum, der ein hierarchischer Baum ähnlich dem eines DOM ist. Im Allgemeinen sind XPath-Ausdrücke „gierig“, d. h., sie stimmen mit allen Knoten überein, die die angegebenen Kriterien erfüllen.
Wir verwenden die folgende abstrakte XML-Datei, um unsere Beispiele zu veranschaulichen:
<WebServiceResponse> <status>OK</status> <result> <type>sample</type> <name>Sample XML</name> <location> <lat>37.4217550</lat> <lng>-122.0846330</lng> </location> </result> <result> <message>The secret message</message> </result> </WebServiceResponse>
Auswahl von Knoten in Ausdrücken
Mit XPath-Auswahlen werden Knoten ausgewählt. Der Stammknoten umfasst das gesamte Dokument. Sie wählen diesen Knoten mit dem speziellen Ausdruck „/“ aus. Beachten Sie, dass der Stammknoten nicht der Knoten auf oberster Ebene Ihres XML-Dokuments ist. Er befindet sich eine Ebene über diesem Element auf oberster Ebene und enthält es.
Elementknoten stellen die verschiedenen Elemente im XML-Dokumentbaum dar. Ein <WebServiceResponse>-Element stellt beispielsweise das Element auf oberster Ebene dar, das im obigen Beispieldienst zurückgegeben wird. Sie wählen einzelne Knoten entweder über absolute oder relative Pfade aus, die durch das Vorhandensein oder Fehlen eines führenden „/“-Zeichens gekennzeichnet sind.
- Absoluter Pfad: Mit dem Ausdruck „
/WebServiceResponse/result“ werden alle<result>-Knoten ausgewählt, die untergeordnete Elemente des<WebServiceResponse>-Knotens sind. Beide Elemente stammen vom Stammknoten „/“ ab. - Relativer Pfad vom aktuellen Kontext: Der Ausdruck „
result“ würde mit allen<result>-Elementen im aktuellen Kontext übereinstimmen. Im Allgemeinen müssen Sie sich keine Gedanken über den Kontext machen, da Sie die Ergebnisse des Webdienstes normalerweise über einen einzelnen Ausdruck verarbeiten.
Beide Ausdrücke können durch Hinzufügen eines Platzhalterpfads erweitert werden, der mit einem doppelten Schrägstrich („//“) gekennzeichnet ist. Dieser Platzhalter gibt an, dass null oder mehr Elemente im dazwischenliegenden Pfad übereinstimmen können. Der XPath-Ausdruck „//formatted_address“ stimmt beispielsweise mit allen Knoten dieses Namens im aktuellen Dokument überein.
Der Ausdruck //viewport//lat würde mit allen <lat>-Elementen übereinstimmen, die <viewport> als übergeordnetes Element haben.
Standardmäßig werden durch XPath-Ausdrücke alle übereinstimmenden Elemente ausgewählt. Sie können den Ausdruck auf ein bestimmtes Element beschränken, indem Sie ein Prädikat angeben, das in eckige Klammern ([]) gesetzt wird. Der XPath-Ausdruck „/GeocodeResponse/result[2]“ gibt beispielsweise immer das zweite Ergebnis zurück.
| Ausdrucktyp | |
|---|---|
| Stammknoten | XPath-Ausdruck: „
/“Auswahl:
<WebServiceResponse>
<status>OK</status>
<result>
<type>sample</type>
<name>Sample XML</name>
<location>
<lat>37.4217550</lat>
<lng>-122.0846330</lng>
</location>
</result>
<result>
<message>The secret message</message>
</result>
</WebServiceResponse>
|
| absoluter Pfad | XPath-Ausdruck: „
/WebServiceResponse/result“Auswahl:
<result>
<type>sample</type>
<name>Sample XML</name>
<location>
<lat>37.4217550</lat>
<lng>-122.0846330</lng>
</location>
</result>
<result>
<message>The secret message</message>
</result>
|
| Pfad mit Platzhalter | XPath-Ausdruck: „
/WebServiceResponse//location“Auswahl:
<location>
<lat>37.4217550</lat>
<lng>-122.0846330</lng>
</location>
|
| Pfad mit Prädikat | XPath-Ausdruck: „
/WebServiceResponse/result[2]/message“Auswahl:
<message>The secret message</message>
|
Alle direkt untergeordneten Elemente des ersten result |
XPath-Ausdruck: „
/WebServiceResponse/result[1]/*“Auswahl:
<type>sample</type>
<name>Sample XML</name>
<location>
<lat>37.4217550</lat>
<lng>-122.0846330</lng>
</location>
|
Die name eines result, dessen type-Text „sample“ lautet. |
XPath-Ausdruck: „
/WebServiceResponse/result[type/text()='sample']/name“Auswahl:
Sample XML
|
Beachten Sie, dass Sie beim Auswählen von Elementen Knoten auswählen, nicht nur den Text in diesen Objekten. Im Allgemeinen sollten Sie alle übereinstimmenden Knoten durchlaufen und den Text extrahieren. Sie können Textknoten auch direkt abgleichen. Weitere Informationen finden Sie unten im Abschnitt Textknoten .
XPath unterstützt auch Attributknoten. Da jedoch alle Google Maps-Webdienste Elemente ohne Attribute bereitstellen, ist ein Abgleich von Attributen nicht erforderlich.
Auswahl von Text in Ausdrücken
Text in einem XML-Dokument wird in XPath-Ausdrücken über einen Textknoten-Operator angegeben. Dieser Operator „text()“ gibt an, dass Text aus dem angegebenen Knoten extrahiert wird. Der XPath-Ausdruck „//formatted_address/text()“ gibt beispielsweise den gesamten Text in <formatted_address>-Elementen zurück.
| Ausdrucktyp | |
|---|---|
| alle Textknoten (einschließlich Leerstellen) | XPath-Ausdruck: „
//text()“Auswahl:
sample
Sample XML
37.4217550
-122.0846330
The secret message
|
| Textauswahl | XPath-Ausdruck: „
/WebServiceRequest/result[2]/message/text()“Auswahl:
The secret message
|
| Kontextbezogene Auswahl | XPath-Ausdruck: „
/WebServiceRequest/result[type/text() = 'sample']/name/text()“Auswahl:
Sample XML
|
Alternativ können Sie einen Ausdruck auswerten und eine Reihe von Knoten zurückgeben und dann über diesen „Knotensatz“ iterieren, um den Text aus jedem Knoten zu extrahieren. Dieser Ansatz wird im Beispiel unten verwendet.
Weitere Informationen zu XPath finden Sie in der XPath-W3C-Spezifikation.
XPath in Java auswerten
Java bietet umfassende Unterstützung für das Parsen von XML und die Verwendung von XPath-Ausdrücken im Paket javax.xml.xpath.*.
Aus diesem Grund wird im Beispielcode in diesem Abschnitt Java verwendet, um zu veranschaulichen, wie XML verarbeitet und Daten aus XML-Dienstantworten geparst werden.
Wenn Sie XPath in Ihrem Java-Code verwenden möchten, müssen Sie zuerst eine Instanz einer XPathFactory instanziieren und newXPath() für diese Factory aufrufen, um ein XPath
-Objekt zu erstellen. Dieses Objekt kann dann übergebene XML- und XPath-Ausdrücke mit der Methode evaluate() verarbeiten.
Achten Sie beim Auswerten von XPath-Ausdrücken darauf, dass Sie alle möglichen „Knotensätze“ durchlaufen, die zurückgegeben werden können. Da diese Ergebnisse als DOM-Knoten in Java-Code zurückgegeben werden, sollten Sie solche Mehrfachwerte in einem NodeList-Objekt erfassen und dieses Objekt iterieren, um Text oder Werte aus diesen Knoten zu extrahieren.
Der folgende Code veranschaulicht, wie Sie ein XPath-Objekt erstellen, ihm XML und einen XPath-Ausdruck zuweisen und den Ausdruck auswerten, um den relevanten Inhalt auszugeben.
import org.xml.sax.InputSource; import org.w3c.dom.*; import javax.xml.xpath.*; import java.io.*; public class SimpleParser { public static void main(String[] args) throws IOException { XPathFactory factory = XPathFactory.newInstance(); XPath xpath = factory.newXPath(); try { System.out.print("Web Service Parser 1.0\n"); // In practice, you'd retrieve your XML via an HTTP request. // Here we simply access an existing file. File xmlFile = new File("XML_FILE"); // The xpath evaluator requires the XML be in the format of an InputSource InputSource inputXml = new InputSource(new FileInputStream(xmlFile)); // Because the evaluator may return multiple entries, we specify that the expression // return a NODESET and place the result in a NodeList. NodeList nodes = (NodeList) xpath.evaluate("XPATH_EXPRESSION", inputXml, XPathConstants.NODESET); // We can then iterate over the NodeList and extract the content via getTextContent(). // NOTE: this will only return text for element nodes at the returned context. for (int i = 0, n = nodes.getLength(); i < n; i++) { String nodeString = nodes.item(i).getTextContent(); System.out.print(nodeString); System.out.print("\n"); } } catch (XPathExpressionException ex) { System.out.print("XPath Error"); } catch (FileNotFoundException ex) { System.out.print("File Error"); } } }