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

LXF108:Да услышьте же RSS!

Материал из Linuxformat
Перейти к: навигация, поиск
Hardcore Linux Проверь себя на крутом проекте для продвинутых пользователей

Содержание

Говорящие новости

Всемирная сеть может говорить с тобой! Грэм Моррисон озвучит новостную ленту RSS, используя простой скрипт Python и речевой синтезатор Festival.

На первый взгляд, написание скрипта/программы для скачивания и разбора RSS-ленты и последующая отправка новостей в синтезатор речи – задача амбициозная, даже для наших продвинутых уроков. Но, как выясняется, на самом деле это довольно просто, в основном благодаря трем технологиям: Python, Festival и Linux. Python – доминирующий в мире скриптовый язык – позволяет легко создавать сценарии, особо не задумываясь и не прилагая чрезмерных усилий. Система синтеза речи Festival Speech Synthesis System с открытым исходным кодом – фантастическая штука, и ее можно установить парой щелчков мыши из менеджера пакетов вашего дистрибутива. Да и сам Linux – без его мощных каналов и планирования процессов у нас ушло бы куда больше времени на реализацию всего этого функционала в нашей программе, и мы также должны были бы сами добавить к ней графический интерфейс, чтобы сделать ее легко доступной. К счастью, все, что надо сделать – это накропать небольшой скрипт на Python и немного поколдовать с командной строкой для увязывания всего этого вместе. Мы напишем сценарий Python, выводящий сообщение в текстовом формате, а затем подадим текст на вход Festival, который озвучит новость через динамики или наушники. Это дает максимальную гибкость. Такой двуединый подход (Python-скрипт, передающий вывод в Festival) можно применять к практически любой цели. Менее чем через час вы сможете сидеть сложа руки и слушать нежный женский голос синтезатора, читающего последние события с вашего любимого сайта.

Часть 1 Говорящие головы

Для начала нужно заставить синтезатор речи работать. Может показаться, что мы начинаем с конца, но ведь без настроенного синтезатора речи мы не сможем построить и опробовать нашу программу. Пакеты Festival включены в большинство популярных дистрибутивов Linux, и это прекрасно, по сравнению с запутанными требованиями и номенклатурой, используемыми на web-сайте проекта. Festival может «говорить» на различных языках и диалектах, от хинди и маратхи до чешского и итальянского; это означает, что нужный для установки пакет будет зависеть от вашей локали [русский также доступен, – прим.ред.]. Для классического английского [UK-English], например, вам нужно установить два пакета: ‘Festival lexicon from the Oxford Advanced Learners’ Dictionary’ и ‘Part of speech lexicons and ngram from English’, а также основной пакет Festival и пакет «диктора» (голос) по вашему выбору. В зависимости от популярности вашего языка, вы можете найти мужские и женские голоса, с низкой (8000) и высокой (16000) частотой дискретизации.


Тем, кто никогда раньше не пользовался Festival, он покажется немного загадочным. Заставить программу заговорить, похоже, было для разработчиков побочной целью. Наберите festival в командной строке, и вы увидите приглашение интерфейса интерпретатора. Вместо «здрасьте», напечатайте (SayText “Hello World!”), включая скобки. Если все установлено и работает правильно, вы услышите утробный голос синтезатора речи Festival, изрекающий слова «hello» и «world». Голос всецело зависит от установленных вами пакетов. Кто желает ужаснуться количеству предлагаемых Festival вариантов, просто наберите help для их просмотра. Нам же нужно, чтобы Festival вещал из командной строки, и для этого есть два разных метода. Первый использует аргумент --tts. Эта команда будет читать либо файл (дословно!), либо стандартный вывод с терминала. То есть мы можем канализовать выход любой команды в Festival. Например, набрав echo “Hello world” | festival --tts, вы получите результат, аналогичный предыдущему: здесь мы перенаправляем вывод команды echo в Festival без запуска интерпретатора, а он затем передает звук в ваши динамики. Но у этой тактики есть проблемы. Если звуковая карта уже используется, то вы получите ошибку: “Linux: can’t open /dev/dsp”, потому что Festival не взаимодействует со звуковой подсистемой Linux – он просто посылает необработанные аудиоданные на /dev/dsp, а устройство занято, и это не работает. Большинство окружений рабочего стола любят захватывать /dev/dsp, так что при получении нового сообщения вместо новости вы услышите разве что звук «пинг».

Решение немного запутанно. Во-первых, запустим Festival как сервер, набрав festival --server. Это заставит приложение работать в фоновом режиме, молча ожидая от клиента Festival просьбы перевести текст в речь. Такая схема более эффективна, чем запуск Festival при каждой необходимости что-то озвучить, и это также открывает возможности для дистанционных монологов. Для подключения к серверу и генерации речи используем команду festival_client в другом терминале, а также аргумент --ttw. Вместо того, чтобы отправлять звук прямо на аудиоустройство (и сбоить, если устройство уже занято), опция ttw будет передавать необработанные аудиоданные на консоль. Нам надо перенаправить их в команду, способную прочитать этот вывод и отправить его в аудиоподсистему. Простейший вариант – команда aplay; скорее всего, она установлена по умолчанию. Aplay общается с ALSA напрямую, а значит, вы можете поделить свое аудиоустройство между приложениями. Полная версия команды, использующая клиент и сервер и посылающая вывод на aplayecho “Hello World” | festival_client --ttw | aplay. Набрав ее в командной строке, вы должны услышать голос речевого синтезатора Festival. Теперь мы готовы всерьез взяться за программирование.

Часть 2 Декодируем RSS

Решив вопрос с генерацией звука, можно заняться программированием; начнем с загрузки и разборки RSS-ленты. Оно сперва звучит пугающе, но осуществляется очень легко, благодаря штуке под названием ‘Feedparser ‘ – модулю Python, который умеет загружать и разбирать все наиболее распространенные форматы лент RSS и Atom. Так как это модуль, его необходимо установить отдельно, но в силу его универсального великолепия он, скорее всего, поставляется вместе с Python в менеджере пакетов вашего дистрибутива. Если вы не пользовались Python раньше, он приятно поразит вас на фоне мира зависимостей, библиотек, заголовков и наследований, которыми страдают другие, более «корпоративные» языки программирования. Так, например, для импорта Feedparser в свою программу нужно просто начать ее с import feedparser. Только эта строка и нужна для поддержки RSS-лент в нашей Python-программе. Чтобы доказать это, мы добавим несколько строк, извлекающих заголовки первой ленты с нашего собственного сайта. Откройте ваш любимый текстовый редактор и добавьте следующий текст в файле ниже команды import:

 import feedparser
 rss_url = “http://www.linuxformat.co.uk/backend.php”
 feed = feedparser.parse( rss_url )
 print feed.entries[0].title

Сохраните текст в файле с расширением py и запустите его из командной строки, набрав python имяфайла.py. Вы должны увидеть заголовок последней новости с сайта Linux Format UK, напечатанный в консоли. За это надо благодарить Feedparser: он скачивает ленту с http://linuxformat.co.uk и возвращает данные как Unicode-строку Python, которую мы связываем с переменной feed. Отсюда и происходят все другие данные RSS/Atom. В приведенном примере мы вывели заголовок первой новости. Каждая новость является элементом в массиве записей entries, и мы свободно можем запросить практически любое сообщение ленты. Заменив title на description, мы получили бы тело новости, а не один заголовок. Есть много элементов, подобных этим, и мы можем использовать их в зависимости от версии и формата канала.

Попробуйте добавить print feed для печати всего содержимого выбранного RSS-источника и поищите в выводе элементы, которые могут пригодиться. Общие элементы включают feed.title для заголовка ленты, feed.link, возвращающий URL сайта, и feed.date, содежащий дату ленты. Но мы еще раз предупреждаем, что все каналы отличаются друг от друга в своем представлении. Лента Linux Format UK, например, не включает поля даты. Чтобы сделать процесс более гладким, скрипты Python можно запускать автоматически; поместите это в первой строке исходного кода:

 #!/usr/bin/python
     Сделайте файл исполняемым с помощью
 chmod +x filename.py

в терминале. Ваш скрипт теперь можно запустить, набрав ./filename.py, и интегрировать его в вашу установку Festival, напечатав:

 ./filename.py | festival_client --ttw | aplay

Вы должны услышать заголовок нашей главной новости, читаемый вам синтезатором речи Festival, и это наиболее сложная часть всего процесса. Теперь можно адаптировать наш простой сценарий для выбора статей, которые вы хотите услышать, организовать поддержку нескольких лент и ввести любые другие улучшения. Но прежде всего нужно создать каркас для скрипта. Начнем снова с пустого файла и создадим более гибкое решение.

Часть 3 Программирование на Python

Вершиной хит-парада дружелюбия программы является умение разбирать параметры командной строки. Если мы добавим его в нашу программу, то сможем использовать различные аргументы для загрузки альтернативных лент, указания числа выводимых новостей для чтения, а также других режимов работы. С внедрением этих опций мы сможем автоматизировать нашу программу через cron или через другие сценарии, используя внутренние аргументы для изменения режима работы скрипта – точно так же, как мы делаем с другими утилитами. Анализ аргументов Python (так это называется) очень похож на используемый в языках C и C++ и использует модуль getopt. Его необходимо добавить к инструкции import в верхней части нашего скрипта, вместе с другим модулем по имени sys и ранее добавленным Feedparse. Sys обеспечивает базовую функциональность, зависящую от системы, и является обязательным для всех скриптов, кроме самых простейших. Теперь мы должны разделить наш сценарий на функции, чтобы мы могли вызывать каждую из них по отдельности в зависимости от аргументов командной строки. Как и в C/C++, мы начнем с функции main, где живет основная составляющая программной логики.

 def main():
     try:
       opts, args = getopt.getopt(sys.argv[1:], “hu:c:t”, [help”, “url=”, “count=”, “title”])
    except getopt.GetoptError, err:
       print str(err)
       usage()
       sys.exit(2)

В первой строке функция main заявляет о своем существовании, def main():, затем разбирает аргументы командной строки внутри пары операторов try и except. Они представляют собой простейшую форму обработки исключений. Выражение с try анализирует переданные параметры, выискивая однобуквенные ключи h, u:, c: или t и их длинные аналоги help, url=, count= и title. Мы будем использовать эти аргументы, чтобы добавить функциональность в скрипт. Ключ -h или --help, например, заставит его выводить информацию об использовании, url=“http://feedurl” сообщит скрипту адрес ленты для скачивания, а -с 3 или --count=3 велят ему принять во внимание только три последних новости. Наконец, аргументы -t and --title выступают в качестве переключателя. Если они присутствуют, скрипт будет выводить только заголовок каждой новости, а не заголовок вместе с текстом. Если какие-либо аргументы не распознаны, запускается сегмент исключения, выводя стандартную ошибку перед выполнением функции usage() и выходом. Теперь, определив и разобрав аргументы командной строки, используемые для запуска скрипта, мы должны сделать с ним что-нибудь полезное. Используя цикл for, переберем аргументы командной строки и назначим их значения переменным, которые мы сможем использовать в части скрипта, отвечающей за работу с RSS. Ниже обработчика исключений добавьте следующий кусок кода:

 url = “http://www.linuxformat.co.uk/backend.php”
  title = False
  count = 1
  for o, a in opts:
      if o in (“-h”, “--help):
          noargument()
          sys.exit()
      elif o in (“-u”, “--url”):
          url = a
      elif o in (“-c”, “--count”):
          count = int(a)
      elif o in (“-t”, “--title”):
          title = True
      else:
          assert False, “invalid option”

Мы создаем значения по умолчанию для трех переменных, которые будем использовать: url для месторасположения ленты, title для выбора, следует ли включать описание каждой новости или просто вывести название, и count для указания количества читаемых новостей.

В условиях if мы проверяем каждую опцию и аргумент командной строки (o и a в коде). Если определены -h или --help, будет вызвана функция noargument, и скрипт завершит работу. Если определены -u или --url=, адрес ленты (аргумент) будет присвоен переменной url. При обнаружении -c или --count=, функция count = int(a) преобразует строковый аргумент в целое число и запишет его в переменную count. Наконец, если будут найдены -t или --title, то флаг title будет установлен в True (истина).

Понять смысл этих настроек очень легко с помощью кода Feedparser, использованного в нашем первоначальном скрипте. Мы просто используем еще один цикл for для извлечения каждой новости в зависимости от значения переменной count (которая по умолчанию равна 1), и решаем, брать ли только описание новости или заодно и ее содержание. Вот код для этого – он должен идти следом за предыдущим куском:

  feed = feedparser.parse( url )
  feed[‘feed’][‘title’]
  for i in range(0, count):
      if title:
          print feed[‘entries’][i][‘title’]
      else:
          print feed[‘entries’][i][‘title’]
          print feed[‘entries’][i][‘description’]

Здесь мы просто обрабатываем каждую новость в соответствии со значением count и передаем текст на стандартный вывод. Это будет терминал, и уже оттуда можно перенаправить текст в синтезатор речи Festival. Вот и все, что надо включить в функцию main. Осталось только чуть-чуть причесать код. Под нашим фрагментом кода добавьте следующее дополнение, необходимое для любого скрипта Python с функцией main():

  if __name__ == “__main__”:
      sys.exit(main())

Взглянув на вызываемые нами функции, вы заметите, что используются две еще неописанные: usage(), ответ на запуск обработчика исключений при отсутствии правильных аргументов, и noargument, вызываемая, когда пользователь запрашивает --help. Обе они должны слегка подтолкнуть пользователя в нужном направлении, предлагая ему, во-первых, ввести скрипт с опцией --help, а во-вторых, описывая каждый из ключей, принимаемых скриптом. Для выполнения обе- их функций добавьте нечто похожее на следующий код в верхнюю часть вашего скрипта, между оператором import и началом функции

def main():.
 def usage():
   print “Try ‘rss2voice --helpfor more information.”
 def noargument():
   print “Download text from an RSS feed and send it to the standard output.”
   print “-h, --help prints this information”
   print “-u, --url=the URL for the RSS feed”
   print “-c --count= number of stories you need”
   print “-t,       --title output only the title of each story”

Вот и весь код. Вы сейчас написали полностью функциональный сценарий разбора ленты RSS/Atom, пригодный для отправки текста в синтезатор речи Festival. Как обычно, при ограниченном пространстве для публикации, мы опустили проверку на ошибки – хуже всего то, что мы не проверяем правильность URL’а, передаваемого в скрипт. Если ленты не существует, это приведет к проблемам, и может вызвать ошибки в скрипте. В остальном все готово к работе. Просто замените новым скриптом старый в первоначальной команде, которую мы использовали для озвучивания речи в Festival, и вы услышите мелодичные голоса, рассказывающие вам последние новости из Башен Linux Format. LXF

Куда двигаться дальше...

  • Создать запись cron для автоматического запуска скрипта.
  • Добавить режим ожидания для просмотра новых историй каждый час.
  • Отмечать прочитанные и непрочитанные статьи, чтобы избежать дублирования.
  • Настроить различные синтезаторы речи или голоса.
  • Интегрировать речь Festival непосредственно в скрипт.
Персональные инструменты
купить
подписаться
Яндекс.Метрика