В статье «Сбор данных с сайтов с помощью веб-служб на базе SOAP и REST» (опубликованной в Windows IT Pro/RE № 11 за 2015 год) я рассказывал о сборе данных с веб-серверов без необходимости анализа экранных данных. Мы знаем, что веб-службы по методу получения данных подразделяются на две категории: веб-службы на основе более старого протокола SOAP и более новые и быстро развивающиеся веб-службы на базе REST. Рассмотрим задачу извлечения данных реального времени конкретной веб-службы.

Запрос баз данных службы геологической съемки США (USGS) о текущем уровне воды (в футах) реки Рейнбоу (шт. Флорида) — задача не более сложная, чем ввод в адресной строке браузера URL-адреса (http://waterservices.usgs.gov/nwis/iv/? sites=02313098¶meterCd=00065). В результате открывается страница плотного текста в формате XML, которая, помимо прочего, содержит нужные нам данные:

6.23

Данная запись означает, что уровень реки в последний раз измерялся в 12:45 и на тот момент составлял 6,23 фута.

Этот пример наглядно демонстрирует извлечение полезных данных веб-службы, но порождает три закономерных вопроса: как сформирован URL-адрес? Как получить эти данные с помощью команды Invoke-WebRequest? Как с помощью PowerShell извлечь значение 6.23 из XML-текста?

Построение URL-адресов REST

Чтобы сформировать такой URL-адрес, на сайте службы геологической съемки США (USGS) я выполнил поиск по контексту RESTful services и вышел на страницу (http://waterservices.usgs.gov/rest/), где перечислены все возможности, реализуемые с помощью этой веб-службы. Здесь пользователю предлагается инструмент, который после выбора нужного элемента меню формирует URL-адрес запроса для получения необходимой информации. Это отличный пример удобной документации, открывающей доступ к данным. Следует, однако, понимать, что некоторые веб-службы имеют более сложный интерфейс (например, требуют аутентификации), а у некоторых просто плохая документация.

Запрос состоит из двух частей. Сначала идет указатель на информационный ресурс (http://waterservices.usgs.gov/nwis/iv/), за которым следует строка (? sites=02313098¶meterCd=00065). Знатокам веб-технологий уже известно, что это HTTP-запрос типа GET, содержащий две пары ‘имя=значение’: sites=02313098 (указатель на запрашиваемый измеряемый ресурс) и parameterCd=00065 (в данном случае означает вопрос «какова глубина реки?», а не «какова температура?» или «какова скорость течения?» и т. д.). Обратите внимание на первую строчную букву p. Вспомним, что XML различает верхний и нижний регистры.

Мы видим, что первой паре ‘имя=значение’ предшествует знак вопроса (?), а следующей паре — символ (&). Если бы у нас была третья пара ‘имя=значение’, например format=XML, то URL-адрес выглядел бы так: http://waterservices.usgs.gov/nwis/iv/? sites=02313098¶meterCd=00065? format=XML.

Передача выходных данных веб-службы переменной с помощью Invoke-WebRequest

Теперь оставим браузер и обратимся к PowerShell. Ранее мы уже применяли Invoke-WebRequest, поэтому синтаксис нам знаком. Запрос к веб-службе построим из двух операторов:

$URI = "http://waterservices.usgs.gov/nwis/iv/?
   sites=02313098¶meterCd=00065"
[string]$MyResult= (Invoke-WebRequest $URI)

Отметим, что имени переменной $MyResult предшествует [string]. Это метод, называемый приведением типа. PowerShell исходит из того, что на входе — текст XML, поэтому по умолчанию будет осуществлять его синтаксический анализ для преобразования в объект XML. Однако в нашем случае этого делать не нужно, поэтому я даю указание сохранить данные как строку, что занимает меньше времени и больше подходит для следующей команды, которую мы будем использовать.

Извлечение данных с помощью XPath и Select-XML

Значение переменной $MyResult представляет собой сложный текст XML, который я всегда перенаправляю в бесплатную утилиту XML Explorer для преобразования в более удобную для чтения форму. Чтобы с помощью PowerShell выделить из текста всего одно значение (в нашем случае 6.23), воспользуемся языком запросов к элементам XML текста под названием XPath и командой Select-XML.

XPath позволяет с минимальными затратами выделить нужные данные из сложного текста XML, каковым в нашем случае является возвращаемый результат запроса. Главное, что нам известно имя элемента, содержащего нужные данные, а именно value, которому предшествует пространство имен (namespace) по имени ns1. Приведенный ниже оператор PowerShell с помощью XPath находит элемент value независимо от пространства имен, а затем извлекает текст, содержащийся в этом элементе, который и представляет собой искомое значение:

$WaterHeight = (Select-XML -content $result -XPath
   "//* [local-name ()='value']").node.'#text'

Мы еще вернемся к XPath, но пока просто следует уяснить, что, для того чтобы применить этот оператор, надо всего лишь найти имя элемента и указать его вместо value. Например, если в вашем случае веб-служба помещает интересующее вас значение в элемент по имени OutVal, то оператор будет выглядеть так:

$Webdata = (select-xml -content $result -xpath "//* [local-name ()
   ='OutVal']").node.'#text'

Опробуйте такой способ на нескольких веб-службах, и в следующий раз мы продолжим эту тему.