Веб-службы платформы Google Карт представляют собой набор HTTP-интерфейсов для служб Google, предоставляющих географические данные для ваших картографических приложений.
В этом руководстве описываются некоторые распространенные методы, полезные для настройки запросов веб-службы и обработки ответов службы. Полную документацию по Directions API см. в руководстве разработчика .
Что такое веб-сервис?
Веб-службы платформы Google Карт – это интерфейс для запроса данных API Карт из внешних служб и использования этих данных в ваших приложениях Карт. Эти сервисы предназначены для использования вместе с картой в соответствии с лицензионными ограничениями в Условиях обслуживания платформы Google Maps.
Веб-службы Maps API используют запросы HTTP(S) к определенным URL-адресам, передавая параметры URL и/или данные POST в формате JSON в качестве аргументов для служб. Как правило, эти службы возвращают данные в тексте ответа в формате JSON или XML для синтаксического анализа и/или обработки вашим приложением.
Типичный запрос Directions API обычно имеет следующую форму:
https://maps.googleapis.com/maps/api/directions/output?parameters
где output
указывает формат ответа (обычно json
или xml
).
Примечание . Все приложения Directions API требуют аутентификации. Получите дополнительную информацию об учетных данных для аутентификации .
SSL/TLS-доступ
HTTPS требуется для всех запросов платформы Google Карт, которые используют ключи API или содержат пользовательские данные. Запросы, сделанные по протоколу HTTP и содержащие конфиденциальные данные, могут быть отклонены.
Создание действительного URL-адреса
Вы можете подумать, что «действительный» URL-адрес самоочевиден, но это не совсем так. Например, URL-адрес, введенный в адресной строке браузера, может содержать специальные символы (например "上海+中國"
); браузер должен внутренне перевести эти символы в другую кодировку перед передачей. Точно так же любой код, который генерирует или принимает ввод UTF-8, может рассматривать URL-адреса с символами UTF-8 как «действительные», но также должен переводить эти символы перед их отправкой на веб-сервер. Этот процесс называется URL-кодированием или процентным кодированием .
Специальные символы
Нам необходимо перевести специальные символы, поскольку все URL-адреса должны соответствовать синтаксису, указанному в спецификации универсального идентификатора ресурса (URI) . Фактически это означает, что URL-адреса должны содержать только специальное подмножество символов ASCII: знакомые буквенно-цифровые символы и некоторые зарезервированные символы для использования в качестве управляющих символов в URL-адресах. В этой таблице приведены эти символы:
Набор | персонажи | Использование URL |
---|---|---|
буквенно-цифровой | abcdefghijklm nopqrstuvwxyz ABCDEFGHIJKLM NOPQRSTUVWXYZ 0 1 2 3 4 5 6 7 8 9 | Текстовые строки, использование схемы ( http ), порт ( 8080 ) и т. д. |
Незарезервировано | - _ . ~ | Текстовые строки |
Сдержанный | ! * ' ( ) ; : @ & = + $ , / ? % # [ ] | Управляющие символы и/или текстовые строки |
При создании допустимого URL-адреса необходимо убедиться, что он содержит только те символы, которые указаны в таблице «Сводка допустимых символов URL-адреса». Согласование URL-адреса с использованием этого набора символов обычно приводит к двум проблемам: пропуску и замене:
- Символы, с которыми вы хотите работать, существуют за пределами указанного выше набора. Например, символы иностранных языков, такие как
上海+中國
необходимо кодировать с использованием указанных выше символов. По популярному соглашению, пробелы (которые не допускаются в URL-адресах) часто также представляются с помощью символа плюс'+'
. - Символы существуют в указанном выше наборе как зарезервированные символы, но их необходимо использовать буквально. Например,
?
используется в URL-адресах для указания начала строки запроса; если вы хотите использовать строку «? и Мистерионы», вам нужно будет закодировать'?'
характер.
Все символы, которые должны быть закодированы в URL-адресе, кодируются с использованием символа '%'
и двухсимвольного шестнадцатеричного значения, соответствующего их символу UTF-8. Например,上海+中國
в UTF-8 будет кодироваться URL как %E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B
Строка ? and the Mysterians
будут закодированы в URL как %3F+and+the+Mysterians
или %3F%20and%20the%20Mysterians
.
Общие символы, которые нуждаются в кодировании
Некоторые общие символы, которые должны быть закодированы:
Небезопасный персонаж | Закодированное значение |
---|---|
Космос | %20 |
" | %22 |
< | %3C |
> | %3E |
# | %23 |
% | %25 |
| | %7C |
Преобразование URL-адреса, который вы получаете от пользовательского ввода, иногда бывает сложной задачей. Например, пользователь может ввести адрес как «5th&Main St.» Как правило, вы должны создавать свой URL-адрес из его частей, рассматривая любой ввод пользователя как буквальные символы.
Кроме того, URL-адреса ограничены 8192 символами для всех веб-служб платформы Google Карт и статических веб-API. Для большинства служб это ограничение редко достигается. Однако обратите внимание, что у некоторых служб есть несколько параметров, которые могут привести к длинным URL-адресам.
Вежливое использование API Google
Плохо спроектированные клиенты API могут создавать большую нагрузку, чем необходимо, как для Интернета, так и для серверов Google. В этом разделе приведены некоторые рекомендации для клиентов API. Следование этим передовым методам может помочь вам избежать блокировки вашего приложения из-за непреднамеренного злоупотребления API.
Экспоненциальный откат
В редких случаях что-то может пойти не так при обслуживании вашего запроса; вы можете получить код ответа HTTP 4XX или 5XX, или соединение TCP может просто прерваться где-то между вашим клиентом и сервером Google. Часто имеет смысл повторить запрос, так как последующий запрос может быть выполнен успешно, если первоначальный потерпел неудачу. Однако важно не зацикливаться на повторных запросах к серверам Google. Такое зацикливание может привести к перегрузке сети между вашим клиентом и Google, что вызовет проблемы у многих сторон.
Лучшим подходом является повторная попытка с увеличением задержки между попытками. Обычно задержка увеличивается на мультипликативную величину с каждой попыткой, этот подход известен как экспоненциальная отсрочка .
Например, рассмотрим приложение, которое хочет сделать этот запрос к API часовых поясов:
https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510×tamp=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}")
Вы также должны быть осторожны, чтобы в цепочке вызовов приложения не было кода повторных попыток, который приводит к повторным запросам в быстрой последовательности.
Синхронизированные запросы
Большое количество синхронизированных запросов к API Google может выглядеть как атака распределенного отказа в обслуживании (DDoS) на инфраструктуру Google и рассматриваться соответствующим образом. Чтобы избежать этого, вы должны убедиться, что запросы API не синхронизируются между клиентами.
Например, рассмотрим приложение, которое отображает время в текущем часовом поясе. Это приложение, вероятно, установит будильник в клиентской операционной системе, чтобы разбудить его в начале минуты, чтобы отображаемое время можно было обновить. Приложение не должно выполнять никаких вызовов API в рамках обработки, связанной с этим сигналом тревоги.
Выполнение вызовов API в ответ на фиксированный сигнал тревоги — это плохо, так как это приводит к тому, что вызовы API синхронизируются с началом минуты даже между разными устройствами, а не равномерно распределяются по времени. Плохо спроектированное приложение, делающее это, будет производить всплеск трафика в шестьдесят раз выше обычного уровня в начале каждой минуты.
Вместо этого один из возможных хороших вариантов — установить второй будильник на случайно выбранное время. Когда срабатывает этот второй сигнал тревоги, приложение вызывает любые необходимые ему API и сохраняет результаты. Когда приложение хочет обновить свое отображение в начале минуты, оно использует ранее сохраненные результаты, а не снова вызывает API. При таком подходе вызовы API равномерно распределяются по времени. Кроме того, вызовы API не задерживают рендеринг при обновлении дисплея.
Помимо начала минуты, другие распространенные моменты синхронизации, на которые следует обратить внимание , — это начало часа и начало каждого дня в полночь.
Обработка ответов
В этом разделе обсуждается, как динамически извлекать эти значения из ответов веб-службы.
Веб-сервисы Google Maps предоставляют ответы, которые легко понять, но не совсем удобно для пользователя. При выполнении запроса вместо отображения набора данных вы, вероятно, захотите извлечь несколько конкретных значений. Как правило, вам потребуется анализировать ответы веб-службы и извлекать только те значения, которые вас интересуют.
Используемая схема синтаксического анализа зависит от того, в каком формате вы возвращаете выходные данные: XML или JSON. Ответы JSON, уже представленные в виде объектов Javascript, могут обрабатываться внутри самого Javascript на клиенте. Ответы XML должны обрабатываться с использованием процессора XML и языка запросов XML для адресации элементов в формате XML. В следующих примерах мы используем XPath , так как он обычно поддерживается в библиотеках обработки XML.
Обработка XML с помощью XPath
XML — относительно зрелый формат структурированной информации, используемый для обмена данными. Хотя XML не такой легкий, как JSON, он обеспечивает большую языковую поддержку и более надежные инструменты. Например, код для обработки XML в Java встроен в пакеты javax.xml
.
При обработке XML-ответов следует использовать соответствующий язык запросов для выбора узлов в XML-документе, а не предполагать, что элементы находятся в абсолютных позициях в XML-разметке. XPath — это синтаксис языка для уникального описания узлов и элементов в XML-документе. Выражения XPath позволяют идентифицировать конкретное содержимое в документе ответа XML.
Выражения XPath
Некоторое знакомство с XPath имеет большое значение для разработки надежной схемы синтаксического анализа. В этом разделе основное внимание будет уделено тому, как с помощью XPath осуществляется обращение к элементам в XML-документе, что позволяет обращаться к нескольким элементам и создавать сложные запросы.
XPath использует выражения для выбора элементов в XML-документе, используя синтаксис, аналогичный тому, который используется для путей к каталогам. Эти выражения идентифицируют элементы в дереве документов XML, которое представляет собой иерархическое дерево, подобное дереву DOM. Как правило, выражения XPath являются жадными, что указывает на то, что они будут соответствовать всем узлам, которые соответствуют предоставленным критериям.
Мы будем использовать следующий абстрактный 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 выбирает узлы . Корневой узел охватывает весь документ. Вы выбираете этот узел, используя специальное выражение " /
". Обратите внимание, что корневой узел не является узлом верхнего уровня вашего XML-документа; на самом деле он находится на один уровень выше этого элемента верхнего уровня и включает его.
Узлы элементов представляют различные элементы в дереве документа XML. Элемент <WebServiceResponse>
, например, представляет элемент верхнего уровня, возвращенный в нашем примере службы выше. Вы выбираете отдельные узлы либо с помощью абсолютных, либо относительных путей, обозначенных наличием или отсутствием ведущего символа « /
».
- Абсолютный путь: выражение "
/WebServiceResponse/result
" выбирает все узлы<result>
, которые являются дочерними элементами узла<WebServiceResponse>
. (Обратите внимание, что оба эти элемента происходят от корневого узла "/
".) - Относительный путь из текущего контекста: выражение «
result
» будет соответствовать любым элементам<result>
в текущем контексте. Как правило, вам не нужно беспокоиться о контексте, поскольку вы обычно обрабатываете результаты веб-службы с помощью одного выражения.
Любое из этих выражений может быть дополнено путем добавления подстановочного пути, обозначенного двойной косой чертой (" //
"). Этот подстановочный знак указывает, что в промежуточном пути может совпадать ноль или более элементов. Например, выражение XPath " //formatted_address
" будет соответствовать всем узлам с таким именем в текущем документе. Выражение //viewport//lat
будет соответствовать всем элементам <lat>
, которые могут отслеживать <viewport>
как родителя.
По умолчанию выражения XPath соответствуют всем элементам. Вы можете ограничить выражение, чтобы оно соответствовало определенному элементу, указав предикат , заключенный в квадратные скобки ( []
). Например, выражение XPath " /GeocodeResponse/result[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> |
name result с type текста «sample». | Выражение XPath: " /WebServiceResponse/result[type/text()='sample']/name " Выделение: Sample XML |
Важно отметить, что при выборе элементов вы выбираете узлы, а не только текст внутри этих объектов. Как правило, вам нужно перебрать все совпадающие узлы и извлечь текст. Вы также можете напрямую сопоставлять текстовые узлы; см. текстовые узлы ниже.
Обратите внимание, что XPath также поддерживает узлы атрибутов; однако все веб-службы Google Maps обслуживают элементы без атрибутов, поэтому сопоставление атрибутов не требуется.
Выделение текста в выражениях
Текст в 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 .
Оценка XPath в Java
Java имеет широкую поддержку анализа XML и использования выражений XPath в пакете javax.xml.xpath.*
. По этой причине пример кода в этом разделе использует Java, чтобы проиллюстрировать, как обрабатывать XML и анализировать данные из ответов службы XML.
Чтобы использовать XPath в коде Java, вам сначала нужно создать экземпляр XPathFactory
и вызвать newXPath()
на этой фабрике, чтобы создать объект XPath
. Затем этот объект может обрабатывать переданные XML- и XPath-выражения с помощью метода evaluate()
.
При оценке выражений XPath убедитесь, что вы перебираете все возможные «наборы узлов», которые могут быть возвращены. Поскольку эти результаты возвращаются в виде узлов DOM в коде Java, вы должны захватить такие множественные значения в объекте 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"); } } }