Sprawdzone metody korzystania z usług internetowych interfejsu Distance Matrix API

Usługi internetowe Google Maps Platform to zbiór interfejsów HTTP do usług Google, które dostarczają danych geograficznych do Twoich aplikacji mapowych.

W tym przewodniku opisano kilka typowych metod przydatnych do konfigurowania żądań usługi internetowej i przetwarzania odpowiedzi usługi. Pełną dokumentację interfejsu Distance Matrix 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ń o dane interfejsu API Map do usług zewnętrznych i używania tych danych w aplikacji Mapy. Te usługi są przeznaczone do użytku w połączeniu z mapą zgodnie z ograniczeniami licencji określonymi w Warunkach korzystania z usługi Google Maps Platform.

Usługi internetowe interfejsów API Map używają żądań HTTP(S) do określonych adresów URL, przekazując parametry adresu URL lub dane w formacie POST w formacie JSON jako argumenty do usług. Zazwyczaj te usługi zwracają dane w ciele odpowiedzi w formacie JSON lub XML do analizy i przetworzenia przez aplikację.

Typowe żądanie do interfejsu Distance Matrix API ma zwykle taki format:

https://maps.googleapis.com/maps/api/distancematrix/output?parameters

gdzie output oznacza format odpowiedzi (zwykle json lub xml).

Uwaga: wszystkie aplikacje korzystające z interfejsu Distance Matrix API wymagają uwierzytelnienia. 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 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żesz sądzić, że „prawidłowy” adres URL jest oczywisty, ale tak nie jest. Adres URL wpisany na pasku adresu w przeglądarce może na przykład zawierać znaki specjalne (np."上海+中國"). Przed przesłaniem przeglądarka musi przetłumaczyć te znaki na inne kodowanie. Z tego samego powodu każdy kod, który generuje lub akceptuje dane wejściowe w UTF-8, może traktować adresy URL zawierające znaki UTF-8 jako „prawidłowe”, ale musi też przetłumaczyć te znaki przed wysłaniem ich na serwer WWW. Ten proces nazywa się kodowaniem URL-a lub kodowaniem procentowym.

Znaki specjalne

Musimy przetłumaczyć znaki specjalne, ponieważ wszystkie adresy URL muszą być zgodne z syntaksą określoną w specyfikacji identyfikatora zasobów (URI). Oznacza to, że adresy URL mogą zawierać tylko specjalny podzbiór znaków ASCII: znane symbole alfanumeryczne oraz niektóre znaki zarezerwowane do użycia jako znaki kontrolne w adresach URL. Te znaki są opisane w tej tabeli:

Podsumowanie prawidłowych znaków w adresach URL
Do startuznakówUżycie 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 - _ . ~ Teksty
Zarezerwowane ! * ' ( ) ; : @ & = + $ , / ? % # [ ] znaki sterujące lub ciągi tekstowe;

Podczas tworzenia prawidłowego adresu URL musisz się upewnić, że zawiera on tylko znaki podane w tabeli. Dostosowanie adresu URL do tego zestawu znaków prowadzi zazwyczaj do 2 problemów: pominięcia i zastąpienia:

  • Znaki, które chcesz obsługiwać, znajdują się poza zestawem znaków wymienionych powyżej. Na przykład znaki obcych języków, takie jak 上海+中國, muszą być zakodowane za pomocą znaków wymienionych powyżej. Zgodnie z popularną konwencją spacje (które są niedozwolone w adresach URL) są często reprezentowane za pomocą znaku plusa '+'.
  • Znaki z powyższego zestawu są znakami zarezerwowanymi, 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 „? and the Mysterions”, musisz zakodować znak '?'.

Wszystkie znaki, które mają być zakodowane w formacie URL, są kodowane za pomocą znaku '%' i 2-znakowej wartości szesnastkowej odpowiadającej ich znakowi UTF-8. Na przykład znak 上海+中國 w UTF-8 zostałby zakodowany w formacie adresu URL jako %E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B. Ciąg tekstowy ? and the Mysterians zostanie zakodowany w formacie URL jako %3F+and+the+Mysterians lub %3F%20and%20the%20Mysterians.

Typowe znaki wymagające kodowania

Oto kilka przykładów 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 może być czasami trudne. Użytkownik może na przykład wpisać adres „5th&Main St.” Zasadniczo adres URL powinien być budowany z pojedynczych części, a wszystkie dane wejściowe użytkownika powinny być traktowane jako znaki dosłowne.

Dodatkowo w przypadku wszystkich usług internetowych i interfejsów API Map Google Platform długość adresów URL jest ograniczona do 16 384 znaków. W przypadku większości usług ten limit znaków jest rzadko osiągany. Pamiętaj jednak, że niektóre usługi mają kilka parametrów, które mogą powodować długie adresy URL.

Uprzejme korzystanie z interfejsów API Google

Źle zaprojektowane aplikacje korzystające z interfejsu API mogą generować niepotrzebnie duże obciążenie zarówno w internecie, jak i na serwerach 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.

Exponential Backoff

W rzadkich przypadkach może wystąpić błąd podczas obsługi żądania. Możesz wtedy otrzymać kod odpowiedzi HTTP 4xx lub 5xx albo połączenie TCP może po prostu nie działać gdzieś między klientem a serwerem Google. Często warto ponownie przesłać prośbę, ponieważ może się udać, gdy pierwotna prośba zakończyła się niepowodzeniem. Ważne jest jednak, aby nie wysyłać wielokrotnie żądań do serwerów Google. Takie działanie może spowodować przeciążenie sieci między klientem a Google, co może powodować problemy dla wielu stron.

Lepszym podejściem jest ponowne próbowanie z rosnącymi opóźnieniami między próbami. Zwykle opóźnienie jest zwiększane o wielokrotnik przy każdej próbie. Jest to podejście znane jako wzrastający czas do ponowienia.

Rozważ na przykład aplikację, 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

Ten przykład w języku Python pokazuje, jak wysłać żądanie z wykładniczym zmniejszaniem częstotliwości:

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

Upewnij się też, że w łańcuchu wywołań aplikacji nie ma kodu prób ponownego połączenia, który powoduje powtarzające się żądania w szybkiej kolejności.

Synchronizowane żądania

Duża liczba synchronizowanych żądań do interfejsów API Google może wyglądać jak rozproszony atak typu DoS na infrastrukturę Google i być traktowana odpowiednio. Aby tego uniknąć, musisz zadbać o to, aby żądania interfejsu API nie były synchronizowane między klientami.

Weźmy na przykład aplikację, która wyświetla godzinę w bieżącej strefie czasowej. Aplikacja prawdopodobnie ustawi alarm w systemie operacyjnym klienta, który będzie ją budził na początku minuty, aby zaktualizować wyświetlany czas. Aplikacja nie powinna wywoływać żadnych interfejsów API w ramach przetwarzania związanego z tym alarmem.

Wykonywanie wywołań interfejsu API w odpowiedzi na ustalony alarm jest niekorzystne, ponieważ powoduje, że wywołania interfejsu API są synchronizowane z początkiem minuty, nawet na różnych urządzeniach, zamiast być rozłożone równomiernie w czasie. Źle zaprojektowana aplikacja spowoduje wzrost natężenia ruchu o 60 razy większy niż normalnie na początku każdej minuty.

Zamiast tego można ustawić drugi alarm na losową godzinę. Gdy ten drugi alarm się włączy, aplikacja wywołuje wszystkie potrzebne interfejsy API i zapisują wyniki. Gdy aplikacja chce zaktualizować wyświetlanie na początku minuty, korzysta z wcześniej zapisanych wyników zamiast ponownie wywoływać interfejs API. W tym przypadku wywołania interfejsu API są rozłożone równomiernie w czasie. Co więcej, wywołania interfejsu API nie opóźniają renderowania podczas aktualizowania wyświetlacza.

Oprócz początku minuty inne typowe momenty synchronizacji, których nie należy dobierać, to początek godziny oraz początek każdego dnia o północy.

Przetwarzanie odpowiedzi

W tej sekcji omawiamy wyodrębnianie tych wartości dynamicznie z odpowiedzi usług internetowych.

Usługi internetowe Map Google dostarczają odpowiedzi, które są łatwe do zrozumienia, ale niekoniecznie przyjazne użytkownikowi. Podczas wykonywania zapytania zamiast wyświetlać zbiór danych prawdopodobnie chcesz wyodrębnić kilka konkretnych wartości. Zazwyczaj będziesz chciał przeanalizować odpowiedzi z usługi internetowej i wyodrębnić tylko te wartości, które Cię interesują.

Schemat analizowania zależy od tego, czy zwracasz dane w formacie XML czy JSON. Odpowiedzi JSON są już w formie obiektów JavaScriptu, więc mogą być przetwarzane w samym języku JavaScript na kliencie. Odpowiedzi XML powinny być przetwarzane za pomocą procesora XML i języka zapytań XML, aby adresować elementy w formacie XML. W następujących przykładach używamy XPath, ponieważ jest on powszechnie obsługiwany w bibliotekach do przetwarzania XML.

Przetwarzanie danych XML za pomocą XPath

XML to stosunkowo dojrzały format informacji uporządkowanych, używany do wymiany danych. Chociaż nie jest tak lekki jak JSON, XML zapewnia obsługę większej liczby języków i bardziej zaawansowane narzędzia. Kod do przetwarzania XML w języku Java jest np. 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 pozycji bezwzględnej w oznaczeniu 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

Znajomość XPath znacznie ułatwia opracowanie solidnego schematu analizowania. W tej sekcji omówimy, jak za pomocą XPath adresować elementy w dokumencie XML, co pozwala adresować wiele elementów i tworzyć złożone zapytania.

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 hierarchicznym drzewem podobnym do DOM. Ogólnie wyrażenia XPath są ekspansywne, co oznacza, że będą pasować do wszystkich węzłów, które odpowiadają podanym kryteriom.

Do zilustrowania przykładów 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

Wybrane atrybuty XPath to węzły. Węzeł główny obejmuje cały dokument. Wybierasz go za pomocą wyrażenia specjalnego „/”. Pamiętaj, że węzeł główny nie jest węzłem najwyższego poziomu dokumentu XML. Właściwie znajduje się on o jeden poziom wyżej tego elementu najwyższego poziomu i go obejmuje.

Węzły elementów reprezentują różne elementy w drzewie dokumentu XML. Na przykład element <WebServiceResponse> reprezentuje element najwyższego poziomu zwrócony w naszej przykładowej usłudze. Poszczególne węzły możesz wybierać za pomocą ścieżek bezwzględnych lub względnych, co jest sygnalizowane obecnością lub brakiem znaku „/” na początku.

  • Ścieżka bezwzględna: wyrażenie „/WebServiceResponse/result” wybiera wszystkie węzły <result>, które są 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 dowolnych elementów <result> w bieżącym kontekście. Zazwyczaj nie musisz się martwić kontekstem, ponieważ zwykle przetwarzasz wyniki usługi internetowej za pomocą pojedynczego wyrażenia.

Każde z tych wyrażeń może zostać rozszerzone o ścieżkę z symbolem wieloznacznym, oznaczoną podwójnym ukośnikiem („//”). Ten symbol wskazuje, że na ś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 będzie pasować do wszystkich elementów <lat>, które mogą być śledzone jako elementy nadrzędne <viewport>.

Domyślnie wyrażenia XPath pasują do wszystkich elementów. Możesz ograniczyć wyrażenie do dopasowania do określonego elementu, podając predykat, który jest ujęty w kwadratowe nawiasy ([]). Wyrażenie XPath „/GeocodeResponse/result[2]” zawsze zwraca 2. wynik.

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

Pamiętaj, że podczas zaznaczania elementów wybierasz węzły, a nie tylko tekst w tych obiektach. Zazwyczaj warto przejrzeć wszystkie dopasowane węzły i wyodrębnić tekst. Możesz też dopasowywać bezpośrednio węzły tekstowe (patrz sekcja Węzły tekstowe ).

Pamiętaj, że XPath obsługuje też węzły atrybutów, ale wszystkie usługi internetowe Map Google udostępniają 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. Operator „text()” wskazuje na wyodrębnienie tekstu z wskazanego węzła. Na przykład wyrażenie XPath „//formatted_address/text()” zwróci cały tekst zawarty w elementach <formatted_address>.

Typ wyrażenia
Wszystkie węzły tekstowe (w tym znaki odstępu)
Wyrażenie XPath:  „//text()
Wybór:
    sample
    Sample XML

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

Możesz też przeanalizować wyrażenie i zwrócić zbiór węzłów, a następnie przejść przez ten „zbiór 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.

Ocenianie XPath w Javie

Java obsługuje analizowanie XML-a i używanie wyrażeń XPath w ramach pakietu javax.xml.xpath.*. Z tego powodu przykładowy kod w tej sekcji używa Javy, aby pokazać, 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ć metodę newXPath() w tej fabryce, aby utworzyć obiekt XPath . Obiekt ten może następnie przetwarzać przekazane wyrażenia XML i XPath za pomocą metody evaluate().

Podczas przetwarzania wyrażeń XPath sprawdź wszystkie możliwe „zbiory węzłów”, które mogą zostać zwrócone. Ponieważ te wyniki są zwracane jako węzły DOM w kodzie Java, należy przechwycić te wartości w obiekcie NodeList i przeszukać ten obiekt, aby wyodrębnić tekst lub wartości z tych węzłów.

Poniższy kod pokazuje, jak utworzyć obiekt XPath, przypisać do niego wyrażenie XML i XPath, a następnie wykonać to wyrażenie, aby wydrukować odpowiednie treści.

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