Автор — Грэг Валтерс (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)