Sprawdzone metody korzystania z usług internetowych interfejsu Places API

Usługi internetowe Google Maps Platform to zbiór interfejsów HTTP do usług Google, które udostępniają dane geograficzne dla aplikacji mapowych.

W tym przewodniku opisano kilka typowych metod, które są przydatne podczas konfigurowania żądań usługi internetowej i przetwarzania odpowiedzi usługi. Pełną dokumentację interfejsu Places API znajdziesz w przewodniku dla programistów.

Co to jest usługa internetowa?

Usługi internetowe Google Maps Platform to interfejs do wysyłania żądań do interfejsu API Map Google z usług zewnętrznych i używania tych danych w aplikacjach Map Google. Usługi te zostały zaprojektowane do użytku w połączeniu z mapą zgodnie z ograniczeniami licencyjnymi zawartymi w Warunkach korzystania z Google Maps Platform.

Usługi internetowe interfejsów API Map Google używają żądań HTTP(S) do określonych adresów URL i przekazują do tych usług parametry adresu URL lub dane POST w formacie JSON. Zasadniczo te usługi zwracają dane w treści odpowiedzi w formacie JSON lub XML na potrzeby analizy lub przetwarzania przez aplikację.

Typowe żądanie do interfejsu Places API ma zwykle następujący format:

https://places.googleapis.com/v1/places/PLACE_ID?parameters

Uwaga: wszystkie aplikacje interfejsu Places API wymagają uwierzytelniania. Dowiedz się więcej o danych uwierzytelniających.

Dostęp przez SSL/TLS

Protokół HTTPS jest wymagany w przypadku wszystkich żądań Google Maps Platform, które korzystają z kluczy interfejsu API lub zawierają dane użytkowników. Żądania przesłane za pomocą protokołu HTTP, które zawierają dane wrażliwe, mogą zostać odrzucone.

Tworzenie prawidłowego adresu URL

Może Ci się wydawać, że „prawidłowy” adres URL jest oczywisty, ale nie jest to słuszne. Na przykład adres URL wpisany w pasku adresu w przeglądarce może zawierać znaki specjalne (np."上海+中國"). Przed przesłaniem przeglądarka musi je wewnętrznie przetłumaczyć na inne kodowanie. Na podstawie tego samego tokena każdy kod generujący lub akceptujący dane wejściowe w formacie UTF-8 może traktować adresy URL ze znakami UTF-8 jako „prawidłowe”, ale przed wysłaniem ich na serwer WWW musiałby je przetłumaczyć. Ten proces nazywa się kodowaniem adresów URL lub kodowaniem procentowym.

Znaki specjalne

Musimy tłumaczyć znaki specjalne, ponieważ wszystkie adresy URL muszą być zgodne ze składnią określoną w specyfikacji identyfikatora zasobów (Uniform Resource Identifier). Oznacza to, że adresy URL mogą zawierać tylko specjalny podzbiór znaków ASCII: znane symbole alfanumeryczne i niektóre znaki zarezerwowane, które mogą być używane jako znaki kontrolne w adresach URL. W tej tabeli podsumowano te znaki:

Podsumowanie prawidłowych znaków adresu URL
UstawznakówWykorzystanie adresów 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 s 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 Ciągi tekstowe, użycie schematu (http), port (8080) itp.
Niezarezerwowane – _ . ~ Ciągi tekstowe
Zarezerwowano ! * ' ( ) ; : @ & = + $ , / ? % [ ] Znaki kontrolne i ciągi tekstowe

Podczas tworzenia prawidłowego adresu URL musisz się upewnić, że zawiera on tylko znaki widoczne w tabeli Podsumowanie prawidłowych znaków adresu URL. Stworzenie adresu URL tak, aby użyć tego zestawu znaków, zwykle prowadzi do 2 problemów: pominięcia i zastąpienia:

  • Znaki, które chcesz obsługiwać, znajdują się poza powyższym zestawem. Na przykład znaki w językach obcych, takich jak 上海+中國, muszą być zakodowane przy użyciu powyższych znaków. Zgodnie z popularną konwencją spacje (które nie są dozwolone w adresach URL) są często oznaczane również znakiem plusa '+'.
  • W powyższym zestawie znaków występują znaki zarezerwowane, ale muszą być używane dosłownie. Na przykład znak ? jest używany w adresach URL do wskazywania początku ciągu zapytania. Jeśli chcesz użyć ciągu „?” i wiersza Mysterions, musisz zakodować znak '?'.

Wszystkie znaki przeznaczone do kodowania URL są kodowane za pomocą znaku '%' i dwuznakowej wartości szesnastkowej odpowiadającej ich znakowi UTF-8. Na przykład tekst 上海+中國 w UTF-8 będzie zakodowany na potrzeby adresu URL jako %E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B. Ciąg znaków ? and the Mysterians będzie zakodowany na potrzeby adresu 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 Zakodowana wartość
Spacja %20
%22
< %3C
> %3E
# %23
% %25
| %7C

Konwertowanie adresu URL uzyskanego z danych wejściowych użytkownika jest czasem trudne. Na przykład użytkownik może wpisać adres „ul. Główna i Piąta”. Adres URL zasadniczo powinien składać się z części, traktując wszystkie dane wejściowe użytkownika jako znaki literackie.

Dodatkowo adresy URL w usługach internetowych Google Maps Platform i statycznych internetowych interfejsach API mogą mieć maksymalnie 16 384 znaki. W przypadku większości usług ten limit znaków rzadko jest zbliżany do osiągnięcia tego limitu. Pamiętaj jednak, że niektóre usługi mają kilka parametrów, które mogą powodować długie adresy URL.

grzecznościowe korzystanie z interfejsów API Google,

Źle zaprojektowane klienty interfejsu API mogą powodować nadmierne obciążenie zarówno na internecie, jak i na serwerach Google. Ta sekcja zawiera kilka sprawdzonych metod dla klientów interfejsów API. Te sprawdzone metody pomogą Ci uniknąć zablokowania aplikacji z powodu nieumyślnego nadużywania interfejsów API.

Exponential Backoff

W rzadkich przypadkach podczas przetwarzania żądania może coś pójść nie tak. Może się zdarzyć, że otrzymasz kod odpowiedzi HTTP 4XX lub 5XX albo połączenie TCP między Twoim klientem a serwerem Google nie powiedzie się. Często warto spróbować ponownie wysłać żądanie, bo kolejne mogą okazać się skuteczne, gdy pierwotna próba się nie powiedzie. Ważne jest jednak, aby nie zapętlać powtarzających się żądań do serwerów Google. Takie zapętlenie może spowodować przeciążenie sieci między Twoim klientem a Google, powodując problemy u wielu stron.

Lepszym rozwiązaniem jest ponawianie próby przy rosnących opóźnieniach między próbami. Zazwyczaj opóźnienie jest zwiększane przez mnożnik przy każdej próbie. Jest to tzw. wykładniczy czas ponowienia.

Przyjrzyjmy się na przykład aplikacji, która chce wysłać to żądanie do interfejsu Time Zone API:

https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510&timestamp=1331161200&key=YOUR_API_KEY

Poniższy przykład w Pythonie pokazuje, jak utworzyć żądanie z wykładniczym ponawianiem:

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}")

Zwróć też uwagę na to, że w łańcuchu wywołań aplikacji nie może znajdować się wyżej kod ponawiania, który prowadzi do powtarzających się żądań w krótkich odstępach czasu.

Zsynchronizowane żądania

Duża liczba zsynchronizowanych żądań wysyłanych do interfejsów API Google może przypominać atak DDoS na infrastrukturę Google i być odpowiednio traktowany. Aby tego uniknąć, sprawdź, czy żądania do interfejsu API nie są synchronizowane między klientami.

Weźmy na przykład aplikację, która wyświetla godzinę w bieżącej strefie czasowej. Ta aplikacja prawdopodobnie ustawi w systemie operacyjnym klienta alarm, który będzie ją wybudzać na początku minuty, aby można było zaktualizować wyświetlany czas. Podczas przetwarzania powiązanego z alarmem aplikacja nie powinna wykonywać żadnych wywołań interfejsu API.

Wywołania interfejsu API w odpowiedzi na ustalony alarm są błędne, ponieważ powodują synchronizację wywołań interfejsu API na początku minuty, nawet między różnymi urządzeniami, a nie równomiernie rozłożone w czasie. Źle zaprojektowana aplikacja, która robi to w taki sposób, spowoduje wzrost natężenia ruchu przy 60-krotnie normalnych poziomach ruchu na początku każdej minuty.

Drugim dobrym rozwiązaniem jest ustawienie w drugim alarmu na dowolną godzinę. Po uruchomieniu tego drugiego alarmu aplikacja wywołuje potrzebne interfejsy API i zapisuje wyniki. Gdy aplikacja chce zaktualizować widok na początku minuty, korzysta z zapisanych wcześniej wyników, zamiast ponownie wywoływać interfejs API. Przy tym podejściu wywołania interfejsu API są równomiernie rozkładane w czasie. Co więcej, wywołania interfejsu API nie opóźniają renderowania podczas aktualizowania wyświetlacza.

Pamiętaj, że oprócz początku minuty nie ustawiaj kierowania na początek godziny, a na początek każdego dnia o północy.

Przetwarzanie odpowiedzi

W tej sekcji omówiono sposób dynamicznego wyodrębniania tych wartości z odpowiedzi usługi internetowej.

Usługi internetowe Map Google udzielają odpowiedzi, które są łatwe do zrozumienia, ale nie są zrozumiałe dla użytkownika. Zamiast wyświetlać zbiór danych podczas wykonywania zapytania, warto wyodrębnić kilka określonych wartości. Warto przeanalizować odpowiedzi z usługi internetowej i wyodrębnić tylko te wartości, które Cię interesują.

Schemat analizy, którego użyjesz, zależy od tego, czy zwracasz dane wyjściowe w formacie XML czy JSON. Odpowiedzi JSON, które mają już formę obiektów JavaScript, mogą być przetwarzane w obrębie JavaScriptu po stronie klienta. Odpowiedzi XML należy przetwarzać za pomocą procesora XML oraz języka zapytań XML, aby adresować elementy w formacie XML. W poniższych przykładach używamy XPath, ponieważ jest on zwykle obsługiwany w bibliotekach przetwarzania kodu XML.

Przetwarzanie XML za pomocą XPath

XML to stosunkowo dojrzały format uporządkowanych informacji używany do wymiany danych. Chociaż nie jest tak lekki jak JSON, XML zapewnia większą obsługę języków i bardziej wydajne narzędzia. Kod do przetwarzania XML na przykład w Javie jest wbudowany w pakiety javax.xml.

Podczas przetwarzania odpowiedzi XML należy używać odpowiedniego języka zapytań do wyboru węzłów w dokumencie XML zamiast zakładać, że elementy znajdują się na bezwzględnych pozycjach w znacznikach XML. XPath to składnia języka służąca do jednoznacznie opisywania węzłów i elementów w dokumencie XML. Wyrażenia XPath pozwalają zidentyfikować określoną treść w dokumencie odpowiedzi XML.

Wyrażenia XPath

Niewielka znajomość XPath znacznie ułatwia opracowanie solidnego schematu analizy składni. W tej sekcji skupimy się na sposobie, w jaki XPath należy adresować elementy w dokumencie XML, co pozwoli Ci adresować wiele elementów i tworzyć złożone zapytania.

XPath używa wyrażeń do wybierania elementów w dokumencie XML przy użyciu składni podobnej do składni stosowanej w przypadku ścieżek do katalogów. Te wyrażenia identyfikują elementy w drzewie dokumentów XML, które jest hierarchicznym drzewem podobnym do DOM. Wyrażenia XPath są zachłanne, co oznacza, że pasują do wszystkich węzłów spełniających podane kryteria.

Do zilustrowania naszych przykładów użyjemy poniższego 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łów w wyrażeniach

Wybory XPath wybierają węzły. Węzeł główny obejmuje cały dokument. Ten węzeł wybierasz za pomocą wyrażenia specjalnego „/”. Pamiętaj, że węzeł główny nie jest węzłem najwyższego poziomu w dokumencie XML. W rzeczywistości znajduje się on o jeden poziom powyżej tego elementu najwyższego poziomu i zawiera go.

Węzły elementów reprezentują różne elementy w drzewie dokumentów XML. Na przykład element <WebServiceResponse> reprezentuje element najwyższego poziomu zwrócony w naszej przykładowej usłudze powyżej. Poszczególne węzły wybierasz za pomocą ścieżek bezwzględnych lub względnych, które wskazują na obecność lub brak 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 są dziedziczone z węzła głównego „/”).
  • Ścieżka względna z bieżącego kontekstu: wyrażenie „result” pasuje do wszystkich elementów <result> w bieżącym kontekście. Nie musisz się przejmować kontekstem, ponieważ zwykle przetwarzasz wyniki z usługi internetowej za pomocą jednego wyrażenia.

Każde z tych wyrażeń można ulepszyć przez dodanie ścieżki z symbolem wieloznacznym oznaczonej podwójnym ukośnikiem („//”). Ten symbol wieloznaczny wskazuje, że w interwencji ścieżki może znaleźć się 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 będzie pasować do wszystkich elementów <lat>, które mogą śledzić <viewport> jako element nadrzędny.

Domyślnie wyrażenia XPath pasują do wszystkich elementów. Możesz ograniczyć dopasowanie wyrażenia do określonego elementu, podając predicate umieszczony w nawiasach kwadratowych ([]). Wyrażenie XPath „/GeocodeResponse/result[2]” zawsze zwraca drugi wynik.

Rodzaj wyrażenia
Węzeł główny
XPath Expression:/
Wybór:
    <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
XPath Expression:/WebServiceResponse/result
Wybór:
    <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
XPath Expression:/WebServiceResponse//location
Wybór:
    <location>
     <lat>37.4217550</lat>
     <lng>-122.0846330</lng>
    </location>
    
Ścieżka z predykatem
XPath Expression:/WebServiceResponse/result[2]/message
Wybór:
    <message>The secret message</message>
    
Wszystkie bezpośrednie elementy podrzędne pierwszego result
XPath Expression:/WebServiceResponse/result[1]/*
Wybór:
     <type>sample</type>
     <name>Sample XML</name>
     <location>
      <lat>37.4217550</lat>
      <lng>-122.0846330</lng>
     </location>
    
Wartość name elementu result, którego tekst type to „sample”.
XPath Expression:/WebServiceResponse/result[type/text()='sample']/name
Wybór:
    Sample XML
    

Pamiętaj, że przy wybieraniu elementów wybierasz węzły, a nie tylko tekst w tych obiektach. Ogólnie zalecamy iterację ze wszystkimi pasującymi węzłami i wyodrębnienie tekstu. Węzły tekstowe możesz też dopasowywać bezpośrednio. Zobacz Węzły tekstowe poniżej.

XPath obsługuje także węzły atrybutów, jednak 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 określa się 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 wewnątrz elementów <formatted_address>.

Rodzaj wyrażenia
Wszystkie węzły tekstowe (w tym odstępy)
XPath Expression://text()
Wybór:
    sample
    Sample XML

    37.4217550
    -122.0846330
    The secret message
    
Zaznaczanie tekstu
XPath Expression:/WebServiceRequest/result[2]/message/text()
Wybór:
    The secret message
    
Wybór zależny od kontekstu
XPath Expression:/WebServiceRequest/result[type/text() = 'sample']/name/text()
Wybór:
    Sample XML
    

Możesz też ocenić wyrażenie i zwrócić zbiór węzłów, a potem wykonać iterację na podstawie tego „zestawu węzłów”, wyodrębniając tekst z każdego węzła. Wykorzystaliśmy ją w poniższym przykładzie.

Więcej informacji o XPath znajdziesz w specyfikacji XPath W3C.

Obliczanie wartości XPath w Javie

Java oferuje szerokie możliwości analizowania składni XML i używania wyrażeń XPath w pakiecie javax.xml.xpath.*. Dlatego przykładowy kod w tej sekcji pokazuje, jak obsługiwać kod XML i analizować dane z odpowiedzi usługi XML.

Aby użyć XPath w kodzie Java, musisz najpierw utworzyć wystąpienie instancji XPathFactory i wywołać w tej fabryce funkcję newXPath() w celu utworzenia obiektu XPath . Ten obiekt może następnie przetwarzać przekazane wyrażenia XML i XPath, korzystając z metody evaluate().

Przy ocenianiu wyrażeń XPath sprawdź, czy stosujesz wszystkie możliwe „zbiory węzłów”, które mogą zostać zwrócone. Te wyniki są zwracane jako węzły DOM w kodzie Javy, dlatego należy przechwycić takie wartości w obiekcie NodeList i powielać obiekt, aby wyodrębnić tekst lub wartości z tych węzłów.

Poniższy kod pokazuje, jak utworzyć obiekt XPath, przypisać go do pliku XML i wyrażenia XPath oraz ocenić wyrażenie, aby wyświetlić 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");
    }
  }
}