Автор — Грэг Валтерс (Greg Walters)
В нашем прошлом уроке мы рассмотрели API сервиса wunderground и написали немного кода для получения текущих погодных условий. Сейчас мы займёмся той частью API, которая отвечает за прогноз. Если вы пропустили два последних выпуска, рассказывающих об XML, в особенности последний, то, возможно, вам потребуется с ними ознакомиться, прежде чем продолжить.
Как и для текущих погодных условий, для прогноза существует свой веб-адрес. Ссылка на XML-страницу прогноза: http://api.wunderground.com/auto/wui/geo/ForecastXML/index.xml?query=80013
Как и раньше, вы можете сменить «80013» на код вашего города или почтовый индекс. В ответ от сервера придёт около 600 строк XML кода. Вы получите корневой элемент «forecast» и затем четыре вложенных элемента: «termsofservice», «txt_forecast», «simpleforecast» и «moon_phase». Для работы нам понадобятся в основном элементы «txt_forecast» и «simpleforecast».
Так как мы уже ознакомились с секциями usage, main и «if name», займитесь ими самостоятельно. Я же сконцентрируюсь на вещах, необходимых в данный момент. Так как я показал вам фрагмент txt_forecast, давайте начнём разбирать его.
Ниже показана небольшая часть txt_forecast для моего региона.
Элемент txt_forecast включает в себя элементы: date, number, forecastday. Контейнер forecastday, в свою очередь, содержит элементы: period, icon, icons, title и что-то под названием fcttext… Далее структура повторяется. Первое, что стоит заметить, — это то, что в txt_forecast элемент date содержит не дату, а время. Оно показывает, когда прогноз был создан. Тег <number> показывает, сколько имеется прогнозов на ближайшие 24 часа. Я не припоминаю, чтобы это значение было меньше 2. Для каждого суточного прогноза указывается номер периода, несколько наборов иконок, заголовок («Днём», «Вечером», «Завтра») и краткое описание. Обычно такой текст содержит обзор прогноза на ближайшие 12 часов.
Прежде чем мы начнём работать над нашим кодом, посмотрим на элемент <simpleforecast> нашего XML-файла. Он показан справа.
Каждый день прогноза (обычно их 6, включая сегодняшний день) представлен тегом <forecastday>. Он содержит информацию о дате в различных форматах (мне лично нравится тег <pretty>), значения температур по Фаренгейту и по Цельсию, общий прогноз, различные иконки, значок неба (показывающий облачность в районе метеорологической станции) и тег <pop>, что расшифровывается как «вероятность осадков» (Probability Of Precipitation). Тег <moon_phase> предоставляет информацию о закате и восходе солнца и фазах луны.
Теперь мы начнём писать код. Вот необходимые директивы импорта:
from xml.etree import ElementTree as ET import urllib import sys import getopt
Теперь нам необходимо разработать наш класс. Создадим процедуру init для настройки и обнуления переменных, как показано вверху справа на следующей странице.
Если вам не требуется представлять данные и по Цельсию, и по Фаренгейту, можете исключить соответствующий набор переменных. Моё решение — использовать оба.
Далее мы реализуем основную функцию для извлечения данных о прогнозе. Код показан внизу справа на следующей странице.
Он практически идентичен коду процедуры, разработанной нами в прошлый раз. Основное отличие — это используемый URL. Дальше начинается интересное. Поскольку у нас есть несколько потомков, которые имеют одинаковые теги внутри родительского элемента, мы должны сделать разбор немного по-другому. Код в левом верхнем углу на следующей странице.
Заметьте, что в этот раз мы используем tree.find, а для обхода данных применяются циклы for. В отличие от других языков, в Python отсутствует конструкция SELECT/CASE. Вместо неё используются выражения IF/ELIF, хоть они и чуть более громоздкие. Начнём разбирать код. Мы присваиваем переменной fcst содержимое тега <txt_forecast>, тем самым* получив все данные этой группы. Затем обращаемся к тегам <date> и <number>, поскольку это метки «первого уровня», и загружаем их в наши переменные. Теперь всё становится немного сложнее. Посмотрите ещё раз на пример XML-ответа. Там два вхождения тега <forecastday>, у каждого есть дочерние элементы: <period>, <icon>, <icons>, <title> и <fcttext>. С помощью цикла мы обходим их и используем оператор IF, чтобы сохранить их в наши переменные.
Далее нам необходимо обработать данные расширенного прогноза на следующие X дней. Мы используем ту же технику, чтобы присвоить значения нашим переменным. Это показано в правом верхнем углу.
Теперь надо создать метод для вывода данных. Как и в прошлый раз, он будет достаточно общим. Код показан справа на следующей странице.
И здесь тоже, если вы не хотите получать информацию и в градусах Цельсия, и в градусах Фаренгейта, измените код для показа того, что вам нужно. В итоге получаем следующий вид метода «DoIt»:
def DoIt(self,Location,US,IncludeToday,Output): self.GetForecastData(Location) self.output(US,IncludeToday,Output)
Теперь мы можем вызвать наш метод следующим образом:
forecast = ForecastInfo() forecast.DoIt('80013',1,0,0) # Укажите ваш собственный индекс
Вот и всё на сегодня. Обработку данных о штормовых предупреждениях я оставляю вам, если вы захотите разобраться с ними.
Здесь можно просмотреть полностью рабочий код: http://pastebin.com/wsSXMXQx
Удачи и до скорого!
Листинги
<txt_forecast> <date>3:31 PM MDT</date> <number>2</number> −<forecastday> <period>1</period> <icon>nt_cloudy</icon> +<icons></icons> <title>Tonight</title> −<fcttext> Mostly cloudy with a 20 percent chance of thunderstorms in the evening...then partly cloudy after midnight. Lows in the mid 40s. Southeast winds 10 to 15 mph shifting to the south after midnight. </fcttext> </forecastday> +<forecastday></forecastday> </txt_forecast> <simpleforecast> −<forecastday> <period>1</period> −<date> <epoch>1275706825</epoch> <pretty_short>9:00 PM MDT</pretty_short> <pretty>9:00 PM MDT on June 04, 2010</pretty> <day>4</day> <month>6</month> <year>2010</year> <yday>154</yday> <hour>21</hour> <min>00</min> <sec>25</sec> <isdst>1</isdst> <monthname>June</monthname> <weekday_short/> <weekday>Friday</weekday> <ampm>PM</ampm> <tz_short>MDT</tz_short> <tz_long>America/Denver</tz_long> </date> −<high> <fahrenheit>92</fahrenheit> <celsius>33</celsius> </high> −<low> <fahrenheit>58</fahrenheit> <celsius>14</celsius> </low> <conditions>Partly Cloudy</conditions> <icon>partlycloudy</icon> +<icons> <skyicon>partlycloudy</skyicon> <pop>10</pop> </forecastday> ... </simpleforecast> #================================= # Get the forecast for today and (if available) tonight #================================= fcst = tree.find('.//txt_forecast') for f in fcst: if f.tag == 'number': self.periods = f.text elif f.tag == 'date': self.date = f.text for subelement in f: if subelement.tag == 'period': self.period=int(subelement.text) if subelement.tag == 'fcttext': self.forecastText.append(subelement.text) elif subelement.tag == 'icon': self.icon.append( subelement.text) elif subelement.tag == 'title': self.Title.append(subelement.text) class ForecastInfo: def __init__(self): self.forecastText = [] # Today/tonight forecast information self.Title = [] # Today/tonight self.date = '' self.icon = [] # Icon to use for conditions today/tonight self.periods = 0 self.period = 0 #============================================== # Extended forecast information #============================================== self.extIcon = [] # Icon to use for extended forecast self.extDay = [] # Day text for this forecast ("Monday", "Tuesday" etc) self.extHigh = [] # High Temp. (F) self.extHighC = [] # High Temp. (C) self.extLow = [] # Low Temp. (F) self.extLowC = [] # Low Temp. (C) self.extConditions = [] # Conditions text self.extPeriod = [] # Numerical Period information (counter) self.extpop = [] # Percent chance Of Precipitation def GetForecastData(self,location): try: forecastdata = 'http://api.wunderground.com/auto/wui/geo/ForecastXML/index.xml?query=%s' % location urllib.socket.setdefaulttimeout(8) usock = urllib.urlopen(forecastdata) tree = ET.parse(usock) usock.close() except: print 'ERROR - Forecast - Could not get information from server...' sys.exit(2) #================================= # Now get the extended forecast #================================= fcst = tree.find('.//simpleforecast') for f in fcst: for subelement in f: if subelement.tag == 'period': self.extPeriod.append(subelement.text) elif subelement.tag == 'conditions': self.extConditions.append(subelement.text) elif subelement.tag == 'icon': self.extIcon.append(subelement.text) elif subelement.tag == 'pop': self.extpop.append(subelement.text) elif subelement.tag == 'date': for child in subelement.getchildren(): if child.tag == 'weekday': self.extDay.append(child.text) elif subelement.tag == 'high': for child in subelement.getchildren(): if child.tag == 'fahrenheit': self.extHigh.append(child.text) if child.tag == 'celsius': self.extHighC.append(child.text) elif subelement.tag == 'low': for child in subelement.getchildren(): if child.tag == 'fahrenheit': self.extLow.append(child.text) if child.tag == 'celsius': self.extLowC.append(child.text)