Geocoding API ウェブサービスを使用する際のベスト プラクティス

Google Maps Platform ウェブサービスは、Google 地図アプリケーションに地理データを提供するサービス。

このガイドでは、リソースの設定に役立つ一般的な手順について説明します。 ウェブサービス サービスレスポンスの処理に 使用されますデベロッパー ガイドをご覧ください。 をご覧ください。

ウェブサービスとは

Google Maps Platform ウェブサービスは、Maps API のデータをリクエストするためのインターフェースです。 マップ アプリケーション内でデータを使用する方法について説明します。これらのサービスは、 使用できるように設計されているため、 ライセンスの制限 をご覧ください。

Maps API ウェブサービスは、特定の URL に HTTP(S) リクエストを使用して、URL パラメータや サービスの引数としての JSON 形式の POST データ。通常、これらのサービスは、 JSON または XML 形式のレスポンス本文を解析できます。 アプリケーションによる処理です。

一般的な Geocoding API リクエストは、 フォーム:

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

ここで、output はレスポンスの形式を示します(通常、 jsonxml など)。

: すべての Geocoding API アプリケーションには認証が必要です。 認証情報の詳細を確認する。

SSL/TLS アクセス

API キーを使用する、またはユーザーを含む Google Maps Platform リクエストには、すべて HTTPS が必須です。 できます。機密データを含む HTTP 経由で送信されたリクエストは拒否される場合があります。

有効な URL の作成

「有効」な URL とは何か、説明の必要はないと考えられるかもしれませんが、それほど単純なことではありません。ブラウザのアドレスバーに入力される URL には特殊文字("上海+中國" など)が含まれている場合があります。このような特殊文字は、ブラウザで別のエンコードに内部的に変換してから送信する必要があります。同様に、UTF-8 入力を生成または受け付けるコードでは、UTF-8 の文字が使用された URL を「有効」な URL として扱うことがありますが、それらの文字はウェブサーバーに送信する前に変換する必要があります。このプロセスは、URL エンコードまたはパーセント エンコードと呼ばれます。

特殊文字

すべての URL は URI(Uniform Resource Identifier)仕様で規定されている構文に従う必要があるため、特殊文字を変換する必要があります。つまり、URL には、ASCII 文字の特別なサブセット(よく使用される英数記号および URL 内で制御文字として使用される予約文字)のみを含める必要があります。次の表は、こうした特殊記号をまとめたものです。

有効な URL 文字の概要
セット文字URL での使用法
英数字 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 テキスト文字列、スキームでの使用(http)、ポート(8080)など
未予約 - _ . ~ テキスト文字列
予約済み ! * ' ( ) ; : @ & = + $ , / ? % # [ ] 制御文字やテキスト文字列

有効な URL を作成するときは、 表しますしかし、URL での使用がこの文字セットだけに制限された場合、通常は 2 つの問題が発生します。1 つは省略、もう 1 つは置き換えです。

  • 処理する文字が上記のセットに含まれない場合。たとえば、「上海+中國」のような英語以外の文字は、上記の文字を使用してエンコードする必要があります。一般的な命名規則では、URL 内で使用できないスペースもプラス記号 '+' を使用して表します。
  • 上記のセットに予約文字として含まれる文字を、リテラル文字として使用する必要がある場合。たとえば、「?」は URL 内でクエリ文字列の先頭を示すために使用されます。文字列「? and the Mysterions」を使用する場合は、文字 '?' をエンコードする必要があります。

URL エンコードが必要なすべての文字を、'%' と、UTF-8 文字に対応する 2 文字の 16 進数値を使用してエンコードします。たとえば、UTF-8 の「上海+中國」は、「%E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B」として URL エンコードされます。文字列「? and the Mysterians」は、「%3F+and+the+Mysterians」または「%3F%20and%20the%20Mysterians」として URL エンコードされます。

エンコードが必要な一般的な文字

エンコードする必要がある一般的な文字は次のとおりです。

危険な文字 エンコードされた値
スペース %20
" %22
< %3C
> %3E
# %23
% %25
| %7C

ユーザー入力から受け取った URL の変換には、場合によって注意が必要です。たとえば、ユーザーが住所を「5th&Main St.」と入力することも考えられます。通常は、ユーザー入力をリテラル文字として処理して、URL をパーツから作成する必要があります。

さらに、URL は、すべての Google Maps Platform ウェブサービスと Static Web API で 16,384 文字に制限されています。ほとんどのサービスでは、この文字制限に達することはめったにありません。ただし、複数のパラメータを持つ特定のサービスでは、URL が長くなる可能性があります。

Google API の適切な使用

API クライアントの設計に問題があると、インターネットと Google のサーバー。このセクションでは、API のクライアントのベスト プラクティスについて説明します。フォロー中 ベスト プラクティスを 1 つにまとめれば、誤って 説明します。

エラーの対処と再試行

Google API からの UNKNOWN_ERROR または OVER_QUERY_LIMIT レスポンス コードについて詳しくは、 Geocoding API については、エラーと再試行の管理をご覧ください。

指数関数的バックオフ

まれに、リクエストの処理中になんらかの問題が生じることがあります。4XX または 5XX HTTP エラーが返されることがあります レスポンス コードを返さなかった場合、クライアントと Google のネットワークの間で単に TCP 接続が あります。多くの場合、次のようにリクエストを再試行する価値はあります。 元のリクエストが失敗した場合でも、フォローアップ リクエストが成功する可能性があります。ただし、単に Google Cloud の Google のサーバーにリクエストを繰り返し行うループが発生します。このループ動作により、パイプラインに クライアントと Google の間のネットワークが原因で、多くの関係者に問題が生じることがあります。

よりよいアプローチは、試行間の遅延を増加させながら再試行することです。通常、 試行のたびに乗法係数で増加します。この手法は 指数バックオフ

たとえば、アプリケーションでこのリクエストを Time Zone API:

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

次の Python の例では、指数関数的バックオフを使用してリクエストを実行する方法を示しています。

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

また、アプリケーション呼び出しの上位に再試行コードを置かないように注意する必要があります。 連続してリクエストが繰り返されるチェーンです。

同期されたリクエスト

Google の API への大量の同期リクエストは、分散リクエストのように Google のインフラストラクチャに対するサービス拒否(DDoS)攻撃を受けたため、適切に対処する宛先 API リクエストが同期されていないことを確かめるために、 通信できます。

たとえば、現在のタイムゾーンで時刻を表示するアプリケーションについて考えてみましょう。 このアプリケーションはおそらく、クライアント オペレーティング システムでアラームをセットし、 表示されている時刻を更新できるようにします。アプリケーションは、 しないでください。

固定されたアラームに応答して API 呼び出しを行うと、API 呼び出しが 異なるデバイス間でも同期されずに、 均等に分散されます。アプリケーションの設計が不適切だと、このような処理を行うと、 通常の 60 倍のレベルでトラフィックが 下がります

その代わりに、ランダムに選択された時間に 2 つ目のアラームを設定するのが良い設計の 1 つです。 この 2 番目のアラームを起動すると、アプリケーションは必要な API を呼び出し、 表示されます。アプリケーションで 0 分の開始時に表示を更新する場合は、 API を再度呼び出す必要はありません。この方法では 時系列で均等に分散されます。さらに、API 呼び出しによってレンダリングが遅延することはありません。 更新されます。

時刻の初め以外の一般的な同期時刻については、 ターゲットにしない時間を指定できます。

レスポンスの処理

このセクションでは、これらの値をウェブサービス レスポンスから動的に抽出する方法について説明します。

Google マップ ウェブサービスが提供するレスポンスは、 理解していても、まったくユーザー フレンドリーではありません。クエリを実行するときは 単に特定のデータセットだけを 使用できます。通常は、ウェブからのレスポンスを解析し、 関心のある値のみを抽出できます

使用する解析スキームは、レスポンスを返すかどうかによって XML または JSON で出力できますJSON レスポンスは、すでに次の形式になっています。 JavaScript オブジェクト(JavaScript 自体で処理可能) 必要ありません。 XML レスポンスは、XML プロセッサを使用して処理する必要があります XML 形式の要素に対応する XML クエリ言語を使用できます。 ここでは、XPath を使用します。 XML 処理で一般的にサポートされているため、次の例をご覧ください。 使用できます。

XPath による XML の処理

XML は比較的成熟した構造化情報形式であり、 学びました。JSON ほど軽量ではありませんが、XML は より多くの言語サポートとより 堅牢なツールが提供されますコード たとえば、Java で XML を処理する API は、 javax.xml パッケージ。

XML レスポンスを処理する場合は、 XML ドキュメント内のノードを選択するためのクエリ言語 要素が内部の絶対位置にあると仮定した場合よりも XML マークアップ。XPath ノードと要素を一意に記述するための言語構文 XML ドキュメント内で使用できますXPath 式を使用すると、 XML レスポンス ドキュメント内の特定のコンテンツを識別します。

XPath 式

XPath についてある程度の知識があると、開発に大いに役立つ 構築できます。このセクションでは、Google Chat で XPath でアドレス指定されるため、1 つの XML ドキュメント 複数の要素に対処し、複雑なクエリを作成できます。

XPath では式を使用して XML 内の要素を選択する ディレクトリ パスに使用されている構文と同様の構文で記述します。 これらの式は、XML ドキュメント内の要素を識別します。 これは、DOM のツリーに似た階層ツリーです。 一般に、XPath 式は欲張り(greedy)なので、 指定した条件に一致するすべてのノードに一致します。

次の抽象 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>

式でのノード選択

XPath の選択では、ノードを選択します。ルートノード ドキュメント全体が含まれます。このノードの選択には、 特殊な式「/」。なお、ルート モジュールは node は、XML ドキュメントの最上位ノードではありません。実際には この最上位の要素の 1 レベル上に存在し、次の要素が含まれます。 できます。

要素ノードは XML 内のさまざまな要素を表す ドキュメント ツリーです。<WebServiceResponse> 要素 たとえば、 見てみましょう。個別のノードを選択するには パス(絶対パスまたは相対パス)を指定します。 先頭に「/」がありませんあります。

  • 絶対パス: 「/WebServiceResponse/result」 この式では、一致しているすべての <result> ノードが <WebServiceResponse> の子である あります。(これらの要素は両方ともルートの ノード「/」。
  • 現在のコンテキストからの相対パス: 式 「result」任意の <result> に一致 要素を返します。一般的に、 コンテキストを気にする必要はありません。 サービスの結果だけを表現できます。

これらの式のいずれかを足し算で拡張できます。 二重スラッシュ(「//」)で示されます。 このワイルドカードは、0 個以上の要素が あります。XPath 式「//formatted_address」は、 たとえば は、現在のドキュメント内でその名前を持つすべてのノードに一致します。 式 //viewport//lat はすべてに一致します。 <viewport> をトレースできる <lat> 要素 。

デフォルトでは、XPath 式はすべての要素に一致します。また、 述語を指定して特定の要素に一致する式 角かっこ([])で囲まれています。XPath 式「/GeocodeResponse/result[2] は常に 2 回目の結果を出力するだけです

式のタイプ
ルートノード
XPath 式: "/"
選択:
    <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>
    
絶対パス
XPath 式: "/WebServiceResponse/result"
選択:
    <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>
    
ワイルドカードが指定されたパス
XPath 式: "/WebServiceResponse//location"
選択:
    <location>
     <lat>37.4217550</lat>
     <lng>-122.0846330</lng>
    </location>
    
述語が指定されたパス
XPath 式: "/WebServiceResponse/result[2]/message"
選択:
    <message>The secret message</message>
    
最初の result の直接の子すべて
XPath 式: "/WebServiceResponse/result[1]/*"
選択:
     <type>sample</type>
     <name>Sample XML</name>
     <location>
      <lat>37.4217550</lat>
      <lng>-122.0846330</lng>
     </location>
    
nametype のテキストが「sample」の result
XPath 式: "/WebServiceResponse/result[type/text()='sample']/name"
選択:
    Sample XML
    

要素を選択するときは、ノードを選択して オブジェクト内のテキストだけでなく通常、 一致したすべてのノードを反復処理してテキストを抽出します。マイページ テキストノードと直接照合することもできます。テキストノード をご覧ください。

XPath は属性ノードもサポートしています。ただし、 すべての Google マップ ウェブサービスでは、属性のない要素が提供されるため、 属性の照合は不要です。

式でのテキスト選択

XML ドキュメント内のテキストは XPath 式で指定します テキストノード演算子で結合します。この演算子「text()」 は、指定されたノードからテキストが抽出されたことを示します。たとえば XPath 式「//formatted_address/text()<formatted_address> 内のすべてのテキストを返す あります。

式のタイプ
すべてのテキスト ノード(スペースを含む)
XPath 式: "//text()"
選択:
    sample
    Sample XML

    37.4217550
    -122.0846330
    The secret message
    
テキスト選択
XPath 式: "/WebServiceRequest/result[2]/message/text()"
選択:
    The secret message
    
コンテキスト依存の選択
XPath 式: "/WebServiceRequest/result[type/text() = 'sample']/name/text()"
選択:
    Sample XML
    

または、式を評価して その「ノードセット」を反復処理します。抽出 テキストが生成されます。次の例でこの方法を使用します。

XPath の詳細については、コースのリソース XPath W3C 仕様

Java での XPath の評価

Java は XML の解析と XPath 式の使用を幅広くサポート javax.xml.xpath.* パッケージ内で作成します。 そのため、このセクションのサンプル コードでは Java を使用して、 XML を処理し、XML サービス レスポンスのデータを解析する方法を図示します。

Java コードで XPath を使用するには、まず XPathFactory のインスタンスを作成し、 そのファクトリに対して newXPath() を実行して、XPath オブジェクトを作成します。このオブジェクトは、渡された XML を処理できます。 evaluate() メソッドを使用して、XPath 式と XPath 式の両方を作成できます。

XPath 式を評価する場合は、 あらゆる「ノードセット」に対して返されます。これらは Java コードでは DOM ノードとして返されるため、 NodeList オブジェクトに格納し、 そのオブジェクトに対して反復処理を行い、そこからテキストや値を抽出します。 説明します。

次のコードは、XPath の作成方法を示しています。 XML と XPath 式を割り当て、 式を使って関連コンテンツを出力します。

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