Автор — Грэг Валтерс (Greg Walters)

Впрошлый раз я обещал вам, что мы будем использовать познания в XML для получения сведений о погоде с веб-сайта и вывода в терминале. Итак, время пришло!

Мы будем использовать API сайта www.wunderground.com. Я слышу возникший у вас вопрос: «Что такое API?». Термин «API» происходит от «Application Programming Interface» (интерфейс программирования приложения). Проще говоря, это способ взаимодействия с другой программой. Возьмём импортируемые библиотеки. Некоторые из них могут быть запущены как самостоятельные приложения, но если импортировать их как библиотеку, то можно использовать множество их функций в нашей собственной программе, то есть мы получаем возможность использовать чужой код. В данном случае, мы будем использовать специально сформированный URL для запроса на сайте wunderground информации о погоде без использования веб-браузера. Можно сказать, что API — это нечто вроде потайного чёрного хода в другую программу, который программист намеренно оставляет для нашего использования. Иными словами, это расширение одной программы, поддерживаемое для её использования в другой.

Звучит интригующе? Продолжай чтение, мой юный падаван.

Запустите ваш браузер и откройте www.wunderground.com. Теперь введите ваш почтовый индекс или город и штат (или страну) в поисковую строку, и получите массу полезной информации. Затем, давайте, перейдём на страницу API: http://wiki.wunderground.com/index.php/API_-_XML.

Первое, что вы заметите, это условия предоставления услуг API. Пожалуйста, прочтите и следуйте им. Они не являются обременительными, и их действительно просто соблюдать. Вот, что нас интересует: вызовы GeoLookupXML, WXCurrentObXML, AlertsXML и ForecastXML. Потратьте время на их изучение.

Я пропущу подпрограмму GeoLookupXML и позволю вам разобраться с ней самостоятельно. Мы сфокусируемся на двух других командах: на WXCurrentObXML (текущие погодные условия) в этот раз и ForecastXML (прогноз) – в другой.

Вот ссылка на WXCurrentObXML: http://api.wunderground.com/auto/wui/geo/WXCurrentObXML/index.xml?query=80013

Замените ZIP-код США 80013 на ваш почтовый индекс или, если вы за пределами США, попробуйте город, страну – например, Chelyabinsk, Russia или London, England.

А вот и ссылка на ForecastXML: http://api.wunderground.com/auto/wui/geo/ForecastXML/index.xml?query=80013

Как и в предыдущем случае, 80013 можно заменить на почтовый индекс или город, страну.

Итак, начнём с текущих погодных условий. Вставьте адрес в браузер, вы увидите значительный объём информации. Что из этой информации для вас важно, решайте сами, но мы рассмотрим несколько элементов.

Например, мы уделим внимание следующим тэгам:

display_location
observation_time
weather
temperature_string
relative_humidity
wind_string
pressure_string

Конечно, вы можете добавить другие тэги, которые вам интересны. Однако, выбранные нами тэги достаточный пример для того, чтобы вы могли двигаться дальше сами.

Выяснив, что мы ищем, давайте приступим к составлению программы. Рассмотрим в общих чертах алгоритм работы программы.

Первым делом проверим, что запросил пользователь. Если он указал местность, мы будем использовать её, в противном случае — местность, установленную по умолчанию. Затем мы передаём управление подпрограмме getCurrents. Местность используется для построения строки запроса и отправки её в сеть. Ответ из сети мы получаем с помощью urllib.urlopen, помещаем его в объект и передаём его в функцию анализа из библиотеки ElementTree. Затем мы закрываем соединение и приступаем к поиску нужных тэгов. Найдя интересующий нас тэг, мы сохраняем его текст в переменную, которую можно использовать для последующего вывода данных. Получив все сведения, мы отображаем их. Довольно простая концепция.

Назовём файл с кодом w_currents.py. Здесь часть кода с импортируемыми библиотеками:

from xml.etree import ElementTree as ET

import urllib

import sys

import getopt

Затем добавим строки помощи над импортированием (вверху справа).

Удостоверьтесь, что используете три двойные кавычки. Это позволит делать комментарии из нескольких строк. Мы обсудим эту часть немного подробнее.

Теперь мы создадим класс (внизу справа) и основную часть программы (на следующей странице).

Вспомните из прошлой статьи строку «if name». Если программа запускается как отдельное приложение, то выполняется подпрограмма main, в противном случае мы можем использовать её как часть библиотеки. В подпрограмме main мы можем проверить, было ли что-либо передано ей.

Если пользователь использует параметры «-h» или «–help», будут выведены строки помощи, заключённые в тройные кавычки вначале программного кода. К этому приводит вызов подпрограммы, указывающей приложению вывести doc.

Если пользователь использует параметры «-l» (местность) или «-z» (почтовый индекс), то это перепишет установленные по умолчанию значения. Передавая значение местности, удостоверьтесь, что используете двойные кавычки, чтобы заключить в них строку, и не используете пробелы. Например, чтобы получить текущие погодные условия в Далласе, Техас введите -l «Dallas, Texas».

Внимательный читатель заметит, что параметры -z и -l проверяются практически одинаково. Параметр -l можно дополнительно проверить на наличие пробелов и переформатировать строку перед тем, как передавать её в функцию. Это вы уже можете сделать сами.

Наконец, мы создаём экземпляр класса CurrentInfo. Назовём его currents и передадим информацию о местности в метод «DoIt». Напишем такой код:

def DoIt(self,Location):

self.getCurrents(1,Location)

self.output()

Всё очень просто. Мы передаём местоположение и уровень отладки методу getCurrents, после чего вызываем метод output. Вывод данных можно было организовать и внутри метода getCurrents, но так мы добиваемся гибкости, чтобы выводить данные любым нужным нам способом.

Код метода getCurrents приведён на странице коды.

У метода есть параметр debuglevel. Если программа будет работать не так, как нам нужно, он поможет получить полезную информацию. Также он полезен, когда мы только начинаем писать код. Если вы будете довольны работой программы, то любой код, связанный с debuglevel, можно убрать. Если код программы будет опубликован, например, если программа пишется для кого-то другого, то этот код следует удалить и, прежде чем выпускать программу, её нужно протестировать ещё раз.

Теперь обернём наш код в блок try/except, чтобы в случае, если что-то сломается, программа не вылетела. В блоке try мы подготавливаем URL и устанавливаем время ожидания восемь секунд (urllib.socket.setdefaulttimeout(8)). Это необходимо, потому что иногда сервис wunderground перегружен и не отвечает. А так нам не придётся просто сидеть и ждать ответа из сети. Если хотите изучить urllib подробнее, начните с чтения http://docs.python.org/library/urllib.html.

Если произойдёт что-то непредвиденное, мы попадём в блок except, чтобы вывести сообщение об ошибке и завершить работу приложения (sys.exit(2)).

Если всё отработало как надо, код переходит к поиску тэгов. Во-первых, нужно найти тэг location при помощи команды tree.findall. Помните, что tree — это объект, возвращаемый elementree после синтаксического разбора. Часть того, что возвращает API веб-сайта, показана ниже.

Это первый тэг <full>, в данном случае он содержит «Aurora, CO», что мы и будем использовать в качестве местоположения. После этого нужно найти «observation_time» — время наблюдения текущих погодных условий. Продолжим искать всю нужную нам информацию точно таким же способом.

Наконец, разберёмся с методом output, код которого приведён в странице коды.

Мы просто выводим значения переменных при помощи print.

Вот и всё. Пример вывода программы с моим почтовым индексом и со значением debuglevel равным 1 приведён в левой нижней части следующей страницы.

Заметьте, что я решил использовать тэг, который содержит температуру по шкале Фаренгейта и по шкале Цельсия. Если вы хотите выводить, например, только шкалу Цельсия, используйте тэг <temp_c> вместо тэга <temperature_string>.

Полный код программы можно скачать с http://pastebin.com/4ibJGm74

В следующий раз мы будем работать с API для получения прогноза погоды. А пока, развлекайтесь!

Коды

 
""" w_currents.py
Returns current conditions, forecast and alerts for a given zipcode from WeatherUnderground.com.
Usage: python wonderground.py [options]
Options:
-h, --help Show this help
-l, --location City,State to use
-z, --zip Zipcode to use as location

Examples:
w_currents.py -h (shows this help information)
w_currents.py -z 80013 (uses the zip code 80013 as location)
"""

class CurrentInfo:
"""
This routine retrieves the current condition xml data from WeatherUnderground.com
based off of the zip code or Airport Code...
currently tested only with Zip Code and Airport code
For location,
if zip code use something like 80013 (no quotes)
if airport use something like "KDEN" (use double-quotes)
if city/state (US) use something like "Aurora,%20CO" or “Aurora,CO” (use double-quotes)
if city/country, use something like "London,%20England" (use double-quotes)
"""
def getCurrents(self,debuglevel,Location):
pass

def output(self):
pass
def DoIt(self,Location):
pass

#=========================================
# END OF CLASS CurrentInfo()
#=========================================


def usage():
print __doc__
def main(argv):
location = 80013
try:
opts, args = getopt.getopt(argv, "hz:l:", ["help=", "zip=", "location="])
except getopt.GetoptError:
usage()
sys.exit(2)
for opt, arg in opts:
if opt in ("-h", "--help"):
usage()
sys.exit()
elif opt in ("-l", "--location"):
location = arg
elif opt in ("-z", "--zip"):
location = arg
print "Location = %s" % location
currents = CurrentInfo()
currents.DoIt(location)

#============================================
# Main loop
#============================================
if __name__ == "__main__":

main(sys.argv[1:])

def getCurrents(self,debuglevel,Location):
if debuglevel > 0:
print "Location = %s" % Location
try:
CurrentConditions = 'http://api.wunderground.com/auto/wui/geo/WXCurrentObXML/index.xml?query=%s' % Location
urllib.socket.setdefaulttimeout(8)
usock = urllib.urlopen(CurrentConditions)
tree = ET.parse(usock)
usock.close()
except:
print 'ERROR - Current Conditions - Could not get information from server...'
if debuglevel > 0:
print Location
sys.exit(2)
# Get Display Location
for loc in tree.findall("//full"):
self.location = loc.text
# Get Observation time
for tim in tree.findall("//observation_time"):
self.obtime = tim.text
# Get Current conditions
for weather in tree.findall("//weather"):
self.we = weather.text
# Get Temp
for TempF in tree.findall("//temperature_string"):
self.tmpB = TempF.text
#Get Humidity
for hum in tree.findall("//relative_humidity"):
self.relhum = hum.text
# Get Wind info
for windstring in tree.findall("//wind_string"):
self.winds = windstring.text
# Get Barometric Pressure
for pressure in tree.findall("//pressure_string"):
self.baroB = pressure.text


<display_location>
<full>Aurora, CO</full>
<city>Aurora</city>
<state>CO</state>
<state_name>Colorado</state_name>
<country>US</country>
<country_iso3166>US</country_iso3166>
<zip>80013</zip>
<latitude>39.65906525</latitude>
<longitude>-104.78105927</longitude>
<elevation>1706.00000000 ft</elevation>
</display_location>

def output(self):
print 'Weather Information From Wunderground.com'
print 'Weather info for %s ' % self.location
print self.obtime
print 'Current Weather - %s' % self.we
print 'Current Temp - %s' % self.tmpB
print 'Barometric Pressure - %s' % self.baroB
print 'Relative Humidity - %s' % self.relhum
print 'Winds %s' % self.winds

Location = 80013
Weather Information From Wunderground.com
Weather info for Aurora, Colorado
Last Updated on May 3, 11:55 AM MDT
Current Weather - Partly Cloudy
Current Temp - 57 F (14 C)
Barometric Pressure - 29.92 in (1013 mb)
Relative Humidity - 25%
Winds From the WNW at 10 MPH
Script terminated.