Usługi internetowe Google Maps Platform to zbiór interfejsów HTTP do usług Google, które dostarczają dane geograficzne do aplikacji mapowych.
W tym przewodniku opisano kilka typowych praktyk przydatnych podczas konfigurowania żądań usługi internetowej i przetwarzania odpowiedzi usługi. Pełną dokumentację interfejsu Geocoding API znajdziesz w przewodniku dla deweloperów.
Co to jest usługa internetowa?
Usługi internetowe Google Maps Platform to interfejs do wysyłania żądań danych z interfejsu API Map Google z usług zewnętrznych i używania tych danych w aplikacjach Map Google. Zgodnie z ograniczeniami licencji opisanymi w Warunkach korzystania z usługi Google Maps Platform te usługi są przeznaczone do używania w połączeniu z mapą.
Usługi internetowe interfejsów API Map Google używają żądań HTTP(S) do określonych adresów URL, przekazując parametry adresu URL lub dane POST w formacie JSON jako argumenty do usług. Zazwyczaj te usługi zwracają dane w treści odpowiedzi w formacie JSON lub XML do przeanalizowania lub przetworzenia przez aplikację.
Typowe żądanie do interfejsu Geocoding API ma zwykle taką postać:
https://maps.googleapis.com/maps/api/geocode/output?parameters
gdzie output wskazuje format odpowiedzi (zwykle json lub xml).
Uwaga: wszystkie aplikacje Geocoding API wymagają uwierzytelniania. Dowiedz się więcej o danych uwierzytelniających.
Dostęp SSL/TLS
Protokół HTTPS jest wymagany w przypadku wszystkich żądań Google Maps Platform, które używają kluczy interfejsu API lub zawierają dane użytkownika. Żądania wysyłane przez HTTP, które zawierają dane wrażliwe, mogą zostać odrzucone.
Tworzenie prawidłowego adresu URL
Może się wydawać, że „prawidłowy” adres URL jest oczywisty, ale nie do końca tak jest. Adres URL wpisany na przykład na pasku adresu w przeglądarce może zawierać znaki specjalne (np. "上海+中國"). Przeglądarka musi wewnętrznie przetłumaczyć te znaki na inne kodowanie przed ich przesłaniem.
Z tego samego powodu każdy kod, który generuje lub akceptuje dane wejściowe w formacie UTF-8, może traktować adresy URL ze znakami UTF-8 jako „prawidłowe”, ale musi też przetłumaczyć te znaki przed wysłaniem ich do serwera WWW.
Ten proces nazywa się
kodowaniem URL lub kodowaniem procentowym.
Znaki specjalne
Znaki specjalne muszą zostać przetłumaczone, ponieważ wszystkie adresy URL muszą być zgodne ze składnią określoną w specyfikacji Uniform Resource Identifier (URI). W efekcie adresy URL mogą zawierać tylko specjalny podzbiór znaków ASCII: znane symbole alfanumeryczne i niektóre znaki zarezerwowane do użycia jako znaki sterujące w adresach URL. Te znaki są podsumowane w tabeli:
| Ustaw | znaków | Zastosowanie adresu URL |
|---|---|---|
| Znaki alfanumeryczne | 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 | ciągi tekstowe, użycie schematu (http), port (8080) itp. |
| Niezarezerwowane | - _ . ~ | Ciągi tekstowe |
| Zarezerwowane | ! * ' ( ) ; : @ & = + $ , / ? % # [ ] | znaki kontrolne lub ciągi tekstowe, |
Podczas tworzenia prawidłowego adresu URL musisz upewnić się, że zawiera on tylko znaki podane w tabeli. Dostosowanie adresu URL do tego zestawu znaków zwykle prowadzi do 2 problemów – pominięcia i zastąpienia:
- Znaki, które chcesz obsłużyć, nie należą do powyższego zestawu. Na przykład znaki w językach obcych, takie jak
上海+中國, muszą być zakodowane za pomocą powyższych znaków. Zgodnie z powszechną konwencją spacje (które nie są dozwolone w adresach URL) są często reprezentowane przez znak plusa'+'. - Znaki istnieją w powyższym zbiorze jako znaki zarezerwowane, ale muszą być używane dosłownie.
Na przykład znak
?jest używany w adresach URL do oznaczania początku ciągu zapytania. Jeśli chcesz użyć ciągu „? i Mysterions”, musisz zakodować znak'?'.
Wszystkie znaki, które mają być zakodowane na potrzeby adresu URL, są kodowane za pomocą znaku '%' i dwuznakowej wartości szesnastkowej odpowiadającej ich znakowi UTF-8. Na przykład 上海+中國 w kodowaniu UTF-8 będzie zakodowany w adresie URL jako %E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B. Ciąg znaków ? and the Mysterians zostanie zakodowany w adresie URL jako %3F+and+the+Mysterians lub %3F%20and%20the%20Mysterians.
Typowe znaki wymagające kodowania
Oto kilka typowych znaków, które muszą być zakodowane:
| Niebezpieczny znak | Wartość zakodowana |
|---|---|
| Spacja | %20 |
| ”. | %22 |
| < | %3C |
| > | %3E |
| # | %23 |
| % | %25 |
| | | %7C |
Konwertowanie adresu URL otrzymanego od użytkownika bywa trudne. Użytkownik może na przykład wpisać adres „5th&Main St.”. Zasadniczo adres URL należy tworzyć z jego części, traktując wszelkie dane wejściowe użytkownika jako znaki dosłowne.
Dodatkowo adresy URL są ograniczone do 16 384 znaków we wszystkich usługach internetowych i statycznych interfejsach API Google Maps Platform. W przypadku większości usług ten limit znaków rzadko zostanie osiągnięty. Pamiętaj jednak, że niektóre usługi mają kilka parametrów, które mogą powodować, że adresy URL będą długie.
Zasady korzystania z interfejsów API Google
Źle zaprojektowane klienty API mogą niepotrzebnie obciążać zarówno internet, jak i serwery Google. Ta sekcja zawiera sprawdzone metody dotyczące klientów interfejsów API. Stosowanie tych sprawdzonych metod może pomóc Ci uniknąć zablokowania aplikacji z powodu niezamierzonego nadużycia interfejsów API.
Zarządzanie błędami i ponownymi próbami
Informacje o kodach odpowiedzi UNKNOWN_ERROR lub OVER_QUERY_LIMIT z interfejsu Geocoding API znajdziesz w artykule o zarządzaniu błędami i ponownymi próbami.
Wzrastający czas do ponowienia
W rzadkich przypadkach podczas obsługi żądania może wystąpić błąd. Możesz otrzymać kod odpowiedzi HTTP 4XX lub 5XX albo połączenie TCP może po prostu nie zostać nawiązane między klientem a serwerem Google. Często warto spróbować ponownie, ponieważ kolejne żądanie może się powieść, nawet jeśli pierwotne się nie udało. Ważne jest jednak, aby nie wysyłać żądań do serwerów Google w pętli. Takie zapętlenie może przeciążyć sieć między klientem a Google, co może powodować problemy dla wielu stron.
Lepszym rozwiązaniem jest ponawianie prób z coraz dłuższymi przerwami między nimi. Zwykle opóźnienie jest zwiększane o mnożnik przy każdej próbie. Takie podejście jest znane jako wzrastający czas do ponowienia.
Załóżmy na przykład, że aplikacja chce wysłać do interfejsu Time Zone API takie żądanie:
https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510×tamp=1331161200&key=YOUR_API_KEYPoniższy przykład w języku Python pokazuje, jak wysłać żądanie z wykorzystaniem wzrastającego czasu do ponowienia:
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}")
Uważaj też, aby w łańcuchu wywołań aplikacji nie było kodu ponownej próby, który prowadzi do powtarzania żądań w krótkich odstępach czasu.
Żądania zsynchronizowane
Duża liczba zsynchronizowanych żądań do interfejsów API Google może wyglądać jak rozproszony atak typu DoS (DDoS) na infrastrukturę Google i zostać odpowiednio potraktowana. Aby tego uniknąć, upewnij się, że żądania do interfejsu API nie są synchronizowane między klientami.
Weźmy na przykład aplikację, która wyświetla czas w bieżącej strefie czasowej. Ta aplikacja prawdopodobnie ustawi alarm w systemie operacyjnym klienta, który wybudzi go na początku minuty, aby można było zaktualizować wyświetlany czas. Aplikacja nie powinna wykonywać żadnych wywołań interfejsu API w ramach przetwarzania związanego z tym alarmem.
Wykonywanie wywołań interfejsu API w odpowiedzi na stały alarm jest nieprawidłowe, ponieważ powoduje, że wywołania interfejsu API są synchronizowane z początkiem minuty, nawet między różnymi urządzeniami, zamiast być równomiernie rozłożone w czasie. Źle zaprojektowana aplikacja, która tak robi, na początku każdej minuty wygeneruje skok ruchu 60 razy większy niż normalnie.
Zamiast tego można ustawić drugi alarm na losowo wybraną godzinę. Gdy ten drugi alarm się uruchomi, aplikacja wywołuje wszystkie potrzebne interfejsy API i zapisuje wyniki. Gdy aplikacja chce zaktualizować wyświetlane informacje na początku minuty, używa wcześniej zapisanych wyników, zamiast ponownie wywoływać interfejs API. Dzięki temu podejściu wywołania interfejsu API są rozłożone równomiernie w czasie. Ponadto wywołania interfejsu API nie opóźniają renderowania podczas aktualizowania wyświetlacza.
Oprócz początku minuty innymi typowymi momentami synchronizacji, których nie należy wybierać, są początek godziny i północ na początku każdego dnia.
Przetwarzanie odpowiedzi
W tej sekcji dowiesz się, jak dynamicznie wyodrębniać te wartości z odpowiedzi usługi internetowej.
Usługi internetowe Map Google dostarczają odpowiedzi, które są łatwe do zrozumienia, ale nie są zbyt przyjazne dla użytkownika. Podczas wykonywania zapytania zamiast wyświetlać zbiór danych, prawdopodobnie chcesz wyodrębnić kilka konkretnych wartości. Zazwyczaj będziesz analizować odpowiedzi z usługi internetowej i wyodrębniać tylko te wartości, które Cię interesują.
Schemat analizy zależy od tego, czy zwracasz dane wyjściowe w formacie XML czy JSON. Odpowiedzi JSON, które są już w postaci obiektów w JavaScripcie, mogą być przetwarzane w JavaScripcie na kliencie. Odpowiedzi XML powinny być przetwarzane za pomocą procesora XML i języka zapytań XML, aby można było odwoływać się do elementów w formacie XML. W poniższych przykładach używamy XPath, ponieważ jest on powszechnie obsługiwany w bibliotekach przetwarzania XML.
Przetwarzanie XML za pomocą XPath
XML to stosunkowo dojrzały format informacji strukturalnych używany do wymiany danych. Chociaż nie jest tak prosty jak JSON, XML zapewnia obsługę większej liczby języków i bardziej zaawansowane narzędzia. Na przykład kod do przetwarzania XML w języku Java jest wbudowany w pakiety javax.xml.
Podczas przetwarzania odpowiedzi XML należy używać odpowiedniego języka zapytań do wybierania węzłów w dokumencie XML, zamiast zakładać, że elementy znajdują się w bezwzględnych pozycjach w znacznikach XML. XPath to składnia języka służąca do jednoznacznego opisywania węzłów i elementów w dokumencie XML. Wyrażenia XPath umożliwiają identyfikowanie konkretnych treści w dokumencie odpowiedzi XML.
Wyrażenia XPath
Podstawowa znajomość XPath bardzo pomaga w opracowaniu skutecznego schematu analizy. W tej sekcji skupimy się na tym, jak elementy w dokumencie XML są adresowane za pomocą XPath, co pozwala na adresowanie wielu elementów i tworzenie złożonych zapytań.
XPath używa wyrażeń do wybierania elementów w dokumencie XML, stosując składnię podobną do tej używanej w ścieżkach katalogów. Te wyrażenia identyfikują elementy w drzewie dokumentu XML, które jest drzewem hierarchicznym podobnym do drzewa DOM. Wyrażenia XPath są zwykle zachłanne, co oznacza, że będą pasować do wszystkich węzłów, które spełniają podane kryteria.
W naszych przykładach użyjemy tego abstrakcyjnego kodu XML:
<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>
Wybór węzła w wyrażeniach
Wybory XPath wybierają węzły. Węzeł główny obejmuje cały dokument. Wybierasz ten węzeł za pomocą specjalnego wyrażenia „/”. Pamiętaj, że węzeł główny nie jest węzłem najwyższego poziomu w dokumencie XML. Znajduje się on o jeden poziom wyżej niż element najwyższego poziomu i obejmuje go.
Węzły elementów reprezentują różne elementy w drzewie dokumentu XML. Na przykład element <WebServiceResponse> reprezentuje element najwyższego poziomu zwracany w naszej przykładowej usłudze powyżej. Poszczególne węzły wybiera się za pomocą ścieżek bezwzględnych lub względnych, które są oznaczone obecnością lub brakiem początkowego znaku „/”.
- Ścieżka bezwzględna: wyrażenie „
/WebServiceResponse/result” wybiera wszystkie węzły<result>, które są elementami podrzędnymi węzła<WebServiceResponse>. (Pamiętaj, że oba te elementy pochodzą z węzła głównego „/”). - Ścieżka względna od bieżącego kontekstu: wyrażenie „
result” pasuje do wszystkich elementów<result>w bieżącym kontekście. Zwykle nie musisz się martwić o kontekst, ponieważ wyniki usługi internetowej są zwykle przetwarzane za pomocą jednego wyrażenia.
Każde z tych wyrażeń można rozszerzyć, dodając ścieżkę wieloznaczną, oznaczoną podwójnym ukośnikiem („//”).
Ten symbol wieloznaczny wskazuje, że w ścieżce pośredniej może pasować zero lub więcej elementów. Na przykład wyrażenie XPath „//formatted_address” będzie pasować do wszystkich węzłów o tej nazwie w bieżącym dokumencie.
Wyrażenie //viewport//lat pasuje do wszystkich elementów <lat>, które mają element nadrzędny <viewport>.
Domyślnie wyrażenia XPath pasują do wszystkich elementów. Możesz ograniczyć wyrażenie, aby pasowało do określonego elementu, podając predykat, który jest ujęty w nawiasy kwadratowe ([]). Na przykład wyrażenie XPath „/GeocodeResponse/result[2] zawsze zwraca drugi wynik.
| Typ wyrażenia | |
|---|---|
| Węzeł główny | Wyrażenie XPath: „
/”Selection:
<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>
|
| Ścieżka bezwzględna | Wyrażenie XPath: „
/WebServiceResponse/result”Selection:
<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>
|
| Ścieżka z symbolem wieloznacznym | Wyrażenie XPath: „
/WebServiceResponse//location”Selection:
<location>
<lat>37.4217550</lat>
<lng>-122.0846330</lng>
</location>
|
| Ścieżka z predykatem | Wyrażenie XPath: „
/WebServiceResponse/result[2]/message”Selection:
<message>The secret message</message>
|
Wszystkie bezpośrednie elementy podrzędne pierwszego elementu result |
Wyrażenie XPath: „
/WebServiceResponse/result[1]/*”Selection:
<type>sample</type>
<name>Sample XML</name>
<location>
<lat>37.4217550</lat>
<lng>-122.0846330</lng>
</location>
|
name elementu result, którego tekst type to „sample”. |
Wyrażenie XPath: „
/WebServiceResponse/result[type/text()='sample']/name”Selection:
Sample XML
|
Pamiętaj, że wybierając elementy, wybierasz węzły, a nie tylko tekst w tych obiektach. Zwykle trzeba przejść przez wszystkie dopasowane węzły i wyodrębnić tekst. Możesz też dopasowywać bezpośrednio węzły tekstowe. Zobacz sekcję Węzły tekstowe poniżej.
Pamiętaj, że XPath obsługuje też węzły atrybutów, ale wszystkie usługi internetowe Map Google obsługują elementy bez atrybutów, więc dopasowywanie atrybutów nie jest konieczne.
Zaznaczanie tekstu w wyrażeniach
Tekst w dokumencie XML jest określany w wyrażeniach XPath za pomocą operatora węzła tekstowego. Ten operator „text()” wskazuje wyodrębnienie tekstu ze wskazanego węzła. Na przykład wyrażenie XPath „//formatted_address/text()” zwróci cały tekst w elementach <formatted_address>.
| Typ wyrażenia | |
|---|---|
| Wszystkie węzły tekstowe (w tym odstępy) | Wyrażenie XPath: „
//text()”Selection:
sample
Sample XML
37.4217550
-122.0846330
The secret message
|
| Zaznaczanie tekstu | Wyrażenie XPath: „
/WebServiceRequest/result[2]/message/text()”Selection:
The secret message
|
| Zaznaczanie zależne od kontekstu | Wyrażenie XPath: „
/WebServiceRequest/result[type/text() = 'sample']/name/text()”Selection:
Sample XML
|
Możesz też ocenić wyrażenie i zwrócić zbiór węzłów, a następnie iterować po tym „zbiorze węzłów”, wyodrębniając tekst z każdego węzła. W przykładzie poniżej używamy tego podejścia.
Więcej informacji o XPath znajdziesz w specyfikacji XPath W3C.
Ocena XPath w Javie
Java zapewnia szerokie wsparcie dla analizowania kodu XML i używania wyrażeń XPath w pakiecie javax.xml.xpath.*.
Z tego powodu przykładowy kod w tej sekcji używa języka Java, aby zilustrować, jak obsługiwać XML i analizować dane z odpowiedzi usługi XML.
Aby użyć XPath w kodzie Java, musisz najpierw utworzyć instancję XPathFactory i wywołać newXPath() w tej fabryce, aby utworzyć obiekt XPath
. Ten obiekt może następnie przetwarzać przekazane wyrażenia XML i XPath za pomocą metody evaluate().
Podczas oceny wyrażeń XPath upewnij się, że iterujesz po wszystkich możliwych „zbiorach węzłów”, które mogą zostać zwrócone. Te wyniki są zwracane jako węzły DOM w kodzie Java, dlatego należy przechwycić te wielokrotne wartości w obiekcie NodeList i wykonać iterację na tym obiekcie, aby wyodrębnić tekst lub wartości z tych węzłów.
Poniższy kod pokazuje, jak utworzyć obiekt XPath, przypisać mu kod XML i wyrażenie XPath oraz ocenić wyrażenie, aby wydrukować odpowiednią treść.
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"); } } }