Журнал LinuxFormat - перейти на главную

LXF120:Python

Материал из Linuxformat
Перейти к: навигация, поиск
Python Заставим Web доставлять нужное содержимое вам на блюдечке

Содержание

Python: Сеть на ваш вкус

Часть 1: Недовольны, что контент раскидан где попало и как попало? Ник Вейч приступает к объяснению, как подчинить сеть вашим целям.

Что бы вам ни захотелось узнать, оно, вероятно, найдется в Сети. Это загадочное облако – иногда белое и пушистое, а иногда и полное грязных инсинуаций – содержит знания любого сорта, хотя до них надо еще докопаться. Готовы поспорить, что отыщется даже точная дата крещения «Наболиона Буонапарте», если порыться старательно.

Но не вся информация в сети статична, соединена разумным образом или вообще настолько хороша, насколько должно быть. Вот почему одним из заметных шагов пост-web 2.0 является мэшап [мэшап, mashup – технология построения web-сайта с объединением возможностей группы сторонних web-приложений, см. http://ru.wikipedia.org/wiki/Мэшап_(веб), – прим. пер.] – преобразование web-содержимого в новые удивительные формы. Добро пожаловать в мир панк-данных.

Куда дует ветер

Для начала нашего путешествия сделаем что-нибудь попроще, типа изменения фона рабочего стола в зависимости от погоды. Приложения и виджеты, предоставляющие такой сервис, уже имеются, а значит, данные должно быть легко добыть в Интернете. Быстрый поиск по словосочетанию «weather API data» непременно выдаст множество ссылок. Наши критерии: API должны быть понятны, охватывать как можно большую часть мира и иметь приличную документацию. Под них подпадает лишь пара из предложенных источников, в основном в связи с документацией – ну да, можно потратить время на распутывание данных или работы API, но зачем, если есть кому предоставить и данные, и руководство по их использованию?


Пошарив там и сям, мы решили, что нас устраивает служба погоды Yahoo. Она проста и обладает достаточной документацией, чтобы начать без особых усилий. Еще чуток порывшись, мы обнаружили http://develoPer.yahoo.com/weather, где предоставлено множество деталей и пара примеров использования службы. Очко в пользу Yahoo!

Метод Yahoo состоит в добавлении идентификатора местности в конец URL. В ответ служба генерирует RSS-ленту данных о погоде в указанном регионе. Это удобно во многих отношениях, поскольку означает, что даже код писать не придется. Идентификатор местности найти легко – согласно документации, достаточно пройти на главную страницу Yahoo! Weather, ввести город, и URL ваш (иногда, правда, при этом выскакивает название не города, а ближайшей к нему метеостанции).

«St. Petersburg» даст нам http://weather.yahoo.com/forecast/RSXX0091.html – значит, код нашей местности RSXX0091. Теперь у нас есть информация о местоположении. Инструкция также говорит, что можно добавить параметр для выбора температуры по Фаренгейту или по Цельсию, и мы добавили к URL опцию по Цельсию u=c. Теперь протестируем URL, чтобы увидеть как он работает (о написании кода речи все еще нет).

Приличный браузер, вроде Firefox, способен отобразить RSS-ленту в удобной форме – надо всего лишь ввести ее URL: http://weather.yahooapis.com/forecastrss?p=RSXX0091&u=c.

Обработчик ленты

Теперь, зная, что нужная нам информация доступна в виде RSS-ленты, как заполучить ее в скрипт Python и декодировать? Скопировав указанный выше URL в Firefox и выбрав в меню Вид > Исходный код, вы увидите кучу информации плюс всякие заголовки и прочее. Можно создать обработчик, который возьмет эти сырые данные и выудит из них соответствующие куски – но, как обычно и бывает, кто-то его уже создал. Для Python имеется библиотечный модуль под названием Feedparser, создающий объект Python из RSS-потока. Проще всего установить его через менеджер пакетов: другие способы малость утомительны.

После установки Feedparser наконец-то настает время кодирования. Сначала запустите Python из командной строки, чтобы понимать, с чем мы имеем дело. Вы окажетесь в интерактивной оболочке Python, где мы и введем:


 >>> import feedparser
 >>> url = “http://weather.yahooapis.com/forecastrss?p=RSXX0091&u=c”
 >>> data = feedparser.parse(url)
 >>> data


Результатом действия последней строки будет выдача вереницы символов, которая представляет собой ленту. К счастью, хотя она и похожа на набор случайных данных, пронизанных частоколом скобок, на деле это хорошо структурированный объект. Чтобы убедиться в этом, попробуйте ввести в оболочке Python следующее:

 >>>for x in data: 
 ... print x
 ...
 feed
 status
 version
 encoding
 bozo
 headers
 etag
 href
 namespaces
 entries

Это цикл, пробегающий по всем объектам внутри data. Одно из замечательных свойств Python – легкость работы с объектами, и даже возможность заставить их рассказать о себе. Например, пусть мы хотим точно знать, с каким типом объектов работаем:

 >>>type(data)
 <class 'feedparser.FeedParserDict'>

Ну, отчасти нам это помогло. Если вы уже сталкивались с Python, то, вероятно, слышали про объекты-словари (ассоциативные массивы) – они просто хранят данные в виде пар ключ = значение. Объекты, список которых мы только что выводили, являются в данном случае ключами. Обратившись к документации Feedparser, мы получим несколько более внятное представление, что хранят такие ключи, поскольку они стандартны для объектов Feedparser. Из них для нас важен ключ entries. Он хранит объект-список отдельных записей RSS-ленты, которые и составляют ее реальное содержимое.

Списки Python нумеруются с индекса 0, поэтому, чтобы сослаться на объект первой записи, следует использовать:

 >>> data.entries[0]
 {'updated': u'Thu, 28 May 2009 2:00 am MSD', 'yweather_condition': u'', 'updated_parsed': ...
 >>> for x in data.entries[0]
 ... print x
 ...
 updated
 yweather_condition
 updated_parsed
 links
 title
 summary_detail
 geo_lat
 summary
 guidislink
 title_detail
 link
 geo_long
 yweather_forecast
 id

И вновь мы имеем объект-словарь с парами ключей и значений. На сей раз они определяются самой XML-структурой ленты, поэтому для данного объекта нет модуля с голубой каемочкой; зато его элементы документированы на сайте Yahoo. После недолгого изучения становится ясно, что полезна нам будет сводка (summary) текущих погодных условий, включая температуру.

Налицо небольшая проблема: необходимые данные отнюдь не сосредоточены в одном поле нам на радость. Эта часть ленты – HTML-код для отображения на web-странице, а нам нужен только текст.

Регулярные выражения


От регулярных выражений (также известных как «регекспы» [regex]) никуда не денешься – они будут все чаще встречаться на вашем пути переустройства мира. Регулярные выражения – это просто способ выполнения поиска и замены, и хотя они смахивают на криптограммы, разобраться в них не так уж трудно. В данном случае нам надо всего лишь избавиться от нудных HTML-тэгов. Мы, вероятно, и без того извлекли бы нужные данные, но подобный опыт пригодится нам в будущем, если мы захотим приспособить наш скрипт к работе с различными источниками.

Мы импортируем Python-модуль re (он включен в качестве одной из стандартных библиотек, так что ничего загружать и устанавливать не придется) и привлечем его к работе с этим текстом. На нашем уроке нет места на детальное объяснение работы регулярных выражений, а за краткими сведениями обратитесь ко врезке Регулярные выражения или статье из LXF80.

Выражением мы хотим охватить все, что обрамлено знаками «больше» и «меньше», используемыми для обозначения HTML-тэгов. Это очень легко; выражение будет выглядеть как <, за которым следует шаблон любого символа, повторенный любое количество раз, то есть .+? и в конце >. +? – то же, что и +, но это «ленивое» соответствие: оно отвечает наикратчайшей корректной строке. А нам того и надо – все, что находится между <>; других вариантов нет.

 >>>summary = data.entries[0].summary
 >>>import re
 >>>pattern = '<.+?>'
 >>>temp = re.sub(pattern,'',summary)
 >>>temp
 u'\nCurrent Conditions:\nLight Rain Shower, 12 C\nForecast:\nThu – Partly Cloudy. High: 25 Low:12\nFri – Mostly Sunny. High: 19 Low:
 9\n\nFull Forecast at Yahoo! Weather\n(provided by The Weather Channel)’

Теперь текст очищен от тэгов HTML; но в нем остались переводы строк. Вообще-то есть стандартный модуль для преобразования такой строки в список, но у нас уже загружен модуль re, и мы вполне можем им обойтись. Для поиска символов новой строки надо объяснить Python, что мы хотим использовать чистое (raw) значение строки – поместив перед ней символ r.


 >>> temp = re.split(r'\n',temp)
 >>> temp
 [u'', u'Current Conditions:', u'Light Rain Shower, 12 C', u'Forecast:', u'Thu – Partly Cloudy. High: 25 Low: 12', u'Fri – Mostly Sunny. High:
 19 Low: 9', u'', u'Full Forecast at Yahoo! Weather', u'(provided by The Weather Channel)']
 >>>temp[2]
 u'Light Rain Shower, 12 C'

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

 >>>temp = re.findall(u'[0‑9]+',temp[2])[0]
 >>>temp
 u'12'
 >>>>>> temp = int(temp)
 >>> temp
 12

Финальный шаг – употребить встроенное в Python преобразование типов, превратив строку со значением температуры в целое число: числа проще сравнивать. (В реальном скрипте мы можем, эффективности ради, объединить некоторые из приведенных этапов, но это не даст большого выигрыша для приложения, а ясность пошаговой записи будет утрачена.) Осталось только создать то, что мы посулили в начале урока: обои рабочего стола, меняющиеся в зависимости от погоды

Перемена погоды

Как переделать обои? А для этого в Python есть вызов внешних команд. В Gnome можно изменить заставку командой gconftool-2, устанавливающей переменную окружения, в которой хранится размещение и имя файла обоев. Оно бы и достаточно. Но поскольку у нас возможны варианты, лучше оформить это в виде функции.

Несложная функция не особо напряжет вам мозг. Python обладает простой конструкцией: описание функции и ее параметров, за которым следует текст с отступом. Ее даже можно создать в интерактивной оболочке:

 >>> def change_wallpaper(filename):
 ... cmd = 'gconftool-2 -s /desktop/gnome/background/picture_filename -t string "' + filename + '"'
 ... os.system(cmd)
 ...
 >>> change_wallpaper('plop.jpg')

Первая строка функции создает команду оболочки, а вызов os.system выполняет ее. Вызов функции меняет переменную окружения, и загружается новое изображение. Подставленное нами имя файла filename' – просто параметр; в реальности, хорошо бы хранить ваши изображения где-нибудь внутри вашего домашнего каталога, допустим, в подкаталоге с именем weather, и называть их согласно погодным условиям.

Сейчас Gnome допускает в качестве обоев SVG-изображения, и вы можете создать этакую изысканную масштабируемую картину для вашего рабочего стола. Не знаем, как по-вашему, но мы думаем, что здесь должно быть пять изображений: freezing [мороз], cold [холод], mild [средне], warmish [тепло] и hot [жарко]. Диапазоны температур выбирайте в соответствии с вашим климатом, а мы присвоим следующие: ниже 0°C – мороз, 0–8 – холод, 9–15 – средне, 16–25 – тепло, выше – жарко. В некоторых языках для реализации проверки предусмотрена конструкция case/switch. В Python ее нет, и мы нагородим огород из конструкций if/elif/else, вот так:


 >>> if (temp <0):
 .. change_wallpaper('/home/evilnick/weather/freezing.svg')
 ... elif (temp<9):
 .. change_wallpaper('/home/evilnick/weather/snow.svg')
 ... elif (temp<16):
 .. change_wallpaper('/home/evilnick/weather/mild.svg')
 ... elif (temp<26):
 .. change_wallpaper('/home/evilnick/weather/warm.svg')
 ... else:
 .. change_wallpaper('/home/evilnick/weather/hot.svg')
 ...

Объединив все это в один скрипт, мы получим нечто подобное следующему (остается только разжиться изображениями):

 #!/usr/bin/python
 # -*- coding: utf-8 -*-
 import feedparser,re,os,string
 def change_wallpaper(filename):
  cmd = 'gconftool-2 -s /desktop/gnome/ background/picture_filename -t string "'+filename+'"'
  os.system(cmd)
 url = “http://weather.yahooapis.com/
 forecastrss?p=RSXX0091&u=c”
 data = feedparser.parse(url)
 # extract the summary from the data
 summary = data.entries[0].summary
 temp = re.split(r'\n',re.sub('<.+?>','',summary))
 temp = int(re.search('\d+',temp[2]).group())
 if temp <0:
  change_wallpaper('/home/evilnick/weather/freezing.svg')
 elif temp<9:
  change_wallpaper('/home/evilnick/weather/snow.svg')
 elif temp<16:
  change_wallpaper('/home/evilnick/weather/mild.svg')
 elif temp<26:
  change_wallpaper('/home/evilnick/weather/warm.svg')
 else:
  change_wallpaper('/home/evilnick/weather/hot.svg')


Конечно, даже при буйном воображении это не полноценное приложение, а небольшой сценарий, но его вполне можно взять за основу. На данном уроке мы взяли данные из сети и автоматически совместили их с контекстом рабочего стола. Мы увидели, как просто выглядят RSS-ленты и как работать с объектами в Python; постигли ужасы регулярных выражений; и рассмотрели совершение из Python системных вызовов для выполнения внешних команд. Вот так мы и можем, исследуя сервисы всемирной паутины, брать что угодно и ставить себе на службу.

Скрипт легко расширить (например, позволив пользователям выбирать местоположение) или преобразовать его в апплет. Покамест он завершился бы с ошибкой при отсутствии подключения к Интернету, что не есть хорошо, но на следующих уроках мы изучим, как с этим справиться. Оставайтесь с нами! LXF

Персональные инструменты
купить
подписаться
Яндекс.Метрика