Создание настраиваемого агрегатора RSS-каналов на PHP

Почувствуйте мощь RSS в приложениях Ajax и Web 2.0

Технология RSS (Rich Site Summary, RDF Site Summary или Really Simple Syndication) стала популярной с середины 1990-х годов. В последующие годы появилось несколько вариантов формата RSS, а некоторые организации заявляли, что данная технология принадлежит именно им. Несмотря на эти различия, RSS всегда была полезным средством распределения Web-контента одного Web-сайта по множеству других. Популярность RSS привела к росту популярности нового класса Web-ПО под названием "программы чтения каналов", также известные как "агрегаторы каналов". Хотя на рынке имеется несколько агрегаторов каналов, нетрудно разработать и свой собственный, который можно интегрировать с вашими Web-приложениями. Вы оцените приведённые в нашей статье полнофункциональные фрагменты PHP-кода, в которых демонстрируется использование основанных на PHP серверных функций для разработки настраиваемого агрегатора RSS-каналов. Кроме того, применяя полнофункциональный код агрегатора RSS-каналов, который можно загрузить из данной статьи, вы получите отдачу немедленно.

Сентил Натан, старший инженер-программист, IBM

Сентил Натан (Senthil Nathan) работает старшим инженером-программистом в IBM T.J. Watson Research Center в Hawthorne, New York. Имеет 21-летний опыт создания программного обеспечения для корпоративных приложений различных типов. В настоящее время в сферу его профессиональных интересов входят технологии SOA, Web-сервисы, Java 2 Platform, Enterprise Edition (J2EE), PHP, Web 2.0 и Ajax.



24.02.2010

В то время как такие технологии как Ajax и Web 2.0 вносят свой вклад в экспоненциальный рост числа Web-приложений, само содержимое этих приложений играет важную и центральную роль. Как провайдерам, так и потребителям контента требуются эффективные способы публикации/синдикации и подписки/чтения контента, соответственно. Именно в этой сфере простота и мощь RSS делают эту технологию, изобретённую во времена Web 1.0, важной составляющей эры Web 2.0 и ближайшего будущего.

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

Как уже говорилось, RSS состоит из двух частей: синдикации и агрегации каналов. Основное преимущество RSS-каналов для потребителей – это возможность их агрегации. Именно об этом и пойдёт речь в статье. Вы узнаете подробности о разработке настраиваемого агрегатора RSS-каналов, отвечающего определённым потребностям потребителей контента.

Необходим опыт работы с XML и PHP

В статье предполагается, что вы знакомы с XML и PHP и обладаете опытом работы с этими технологиями. Если нет, посетите разделы XML и PHP сайта developerWorks. Содержащаяся там информация поможет вам лучше понять данную статью.

Введение

По мере повсеместного распространения размещённых в Интернете источников информации, перед контент-провайдерами и потребителями контента возникают некоторые проблемы. Основной задачей контент-провайдеров является каким-то образом выделиться на фоне множества конкурентов и привлечь к своему контенту максимальное количество его потребителей. Напротив, основная задача потребителей контента – быстро добраться до информации, распределённой по несметному количеству категорий и предлагаемой множеством контент-провайдеров. На потребителях контента также лежит бремя фильтрации различных отвлекающих внимание объектов, например, анимированной графики или рекламных flash-баннеров, которые могут помешать получению информации, которую они пытаются найти.

Вследствие широкого проникновения Интернета в нашу жизнь, разработчики должны сконцентрироваться на том, чтобы помочь потребителям быстро получать доступ к информации и избежать помех при получении желаемого контента. У контент-провайдеров всё в большей степени возникает необходимость нацеливать поставку динамического контента на несколько различных пользовательских платформ, от Web-браузеров до мобильных устройств. Как упоминалось в начале статьи, RSS снимает многие проблемы, связанные с синдикацией и агрегацией контента. Если вы новичок в RSS, вы можете получить некоторые базовые знания, прочитав статьи об RSS, ссылки на которые приводятся в разделе Ресурсы.

RSS придаёт Интернету такой большой потенциал, что эту технологию можно использовать множеством разных (и подчас неожиданных) способов. Не приходится и говорить, что руководители многих основных компаний, выпускающих программное обеспечение, в последнее время внесли коррективы в свой курс в области Интернета с учётом мощи RSS. (С примером подобного обсуждения можно ознакомиться в ясной и краткой статье Диона Хинчклиффа (Dion Hinchcliffe), основателя и главного редактора уважаемого издания Web 2.0 Journal и AjaxWorld Magazine, в разделе Ресурсы). На рисунке 1 показано, как, по мнению г-на Хинчклиффа, RSS обеспечивает информационную экосистему Web 2.0. Все основные строительные блоки Web 2.0, например, wiki, блог, сервис новостей, сервис агрегации, сервис гибридных приложений и поисковые механизмы используют RSS в качестве своего рода "клея", соединяющего их вместе и воплощающего в реальность идеал Web 2.0 – социальный компьютинг.

Рисунок 1. Роль RSS в Web 2.0 по Хинчклиффу
Роль RSS в Web 2.0 по Хинчклиффу

Сейчас используется несколько версий формата RSS. В RSS версий 0.91, 0.92 и 2.0 применяется специфический XML-формат, в котором описываются RSS-каналы. В RSS версии 1.0 используется несколько отличный от других версий формат XML. Эту небольшую разницу следует принимать во внимание при разработке агрегаторов RSS-каналов. Прочитать об интересной истории RSS можно в одной из статей из раздела Ресурсы.

Осознав важность и полезность формата RSS, рассмотрим подробно разработку настраиваемого агрегатора RSS-каналов. Для этого мы будем использовать язык PHP. В PHP имеется несколько встроенных Web- и XML-функций, которые ускорят разработку. Кроме того, написанный на PHP код можно легко запустить на PHP-сервере, например, на Zend Core, в который входит Zend Framework. Для разработки агрегатора каналов необходимо установить на машину PHP, а также настроить его на работу с пакетами cURL (Client URL) и XML. Я рекомендую установить PHP-сервер Zend Core, он доступен бесплатно, и его легко устанавливать и конфигурировать.

Рассмотрим вкратце основы формата RSS.

Основы RSS

В то, что представляет собой RSS сегодня, внесли свой вклад несколько компаний и групп, в том числе Apple, Microsoft®, Netscape, Userland, а также рабочая группа RSS-DEV. Поскольку в процессе принимали участие разные компании, сейчас мы имеем различные версии формата RSS. Это в основном касается RSS версии 1.0 (разработанной RSS-DEV), в которой используется немного другой формат, в отличие от других версий (0.91, 0.92 и 2.0 – у них у всех обратно совместимый формат).

В листинге 1 показан высокоуровневый формат RSS версии 1.0. Важно помнить, что в этом формате корневой элемент называется <rdf> и имеет один элемент <channel> и один или более элементов <item> в качестве дочерних. Помните, что в этом формате элементы <channel> и <item> являются элементами одного уровня.

Листинг 1. Скелет формата RSS версии 1.0
<rdf>
     <channel>
          .....
          .....
     </channel>

     <item>
          <title>My Title</title>
          <link>My URL</link>
          <description>My Description</description>
          .....
          .....
     </item>
</rdf>

В листинге 2 показан высокоуровневый формат RSS версий 0.91, 0.92 и 2.0. Можно видеть, что в этом формате корневой элемент называется <rss>, а не <rdf>. Корневой элемент <rss> имеет только один дочерний элемент под названием <channel>, а все элементы <item> являются потомками элемента <channel>. В этом состоит существенное различие с RSS 1.0. Эту небольшую разницу следует принимать во внимание при написании кода, анализирующего эти различные форматы RSS. В нашей статье особо важны три потомка элемента <item>. Хорошо, что эти три дочерних элемента одинаковы во всех форматах RSS.

Листинг 2. Скелет формата RSS версий 0.91 и 2.0
<rss>
     <channel>
          .....
          .....

          <item>
               <title>My Title</title>
               <link>My URL</link>
               <description>My Description</description>
               .....
               .....
          </item>
     </channel>
</rss>

Подробные спецификации RSS см. в соответствующих статьях из раздела Ресурсы. В следующих разделах речь пойдёт о компонентах нашего агрегатора RSS-каналов.

Функциональные компоненты

Функция RSS, описываемая в данной статье, состоит из следующих компонентов, объясняемых в следующем порядке:

  • Программа чтения каналов
  • Компонент ввода источников каналов
  • Агрегатор каналов
  • Компонент вывода результатов каналов

Вместе эти простые компоненты составляют мощную функцию агрегатора RSS-каналов, которую можно интегрировать с другими приложениями многими способами. В следующих разделах мы подробно рассмотрим фрагменты PHP-кода, составляющие эти компоненты. На рисунке 2 показан обзор этих функциональных компонентов.

Рисунок 2. Обзор функциональных компонентов RSS
Обзор функциональных компонентов RSS

На рисунке 2 обратите внимание на следующее.

  • Компонент чтения каналов выполняет основную работу и занимается в основном получением каналов, предоставляемых данными источниками каналов. Источник каналов – это всего лишь URL-адрес, по которому определённый контент-провайдер периодически публикует содержимое по данной информационной категории. Например, источник каналов может указывать на URL, по которому New York Times публикует все последние заголовки новостей в категории (канале) "бизнес" с помощью основанного на XML формата RSS.
  • Компонент агрегатора каналов принимает на вход несколько определённых пользователем источников каналов, а затем вызывает компонент чтения каналов, чтобы получить все элементы канала из каждого настроенного источника каналов.
  • Компонент ввода источников каналов определяет и считывает сведения об определённых пользователями источниках каналов. Сведения об источнике каналов могут предоставляться в виде строки, хранящейся в системной памяти, через входной файл или как запись в базе данных.
  • Компонент вывода результатов каналов сохраняет объединённые результаты элементов RSS-канала, полученные от определённого источника каналов. Он может сохранять результаты в виде строки в системной памяти, в файл или в таблицу базы данных.

Чтобы упростить нашу работу, мы объединим компоненты ввода источников каналов и вывода результатов каналов, сделав их частью компонента агрегатора каналов. (Набор исходных PHP-файлов для этих компонентов и полный набор документов к ним можно получить в разделе Загрузка). Эти файлы можно извлечь на вашу машину и запустить их с помощью ваших собственных настроенных наборов источников RSS-каналов. Как объяснялось в предыдущем разделе, среда PHP с поддержкой библиотек XML и cURL является обязательным требованием. (Обратитесь к разделу Заключение, из которого можно узнать о множестве способов использования кода для создания агрегатора RSS-каналов).

Теперь мы готовы вникнуть в устройство и работу этих компонентов.

Программа чтения каналов

Компонент чтения каналов представляет собой базовый механизм, выполняющий необходимые сетевые коммуникации с источниками канала и анализирует основанные на XML RSS-каналы, возвращаемые источниками канала. Он использует, главным образом, следующие три простых, но мощных функций среды PHP:

  • cURL
  • SimpleXML
  • PHP-массивы

cURL – это библиотека с отрытым исходным кодом, позволяющая PHP-программе подключаться к удалённому серверу с помощью различных протоколов, например, HTTP, FTP, Telnet и т.д. Она предоставляет набор функций-упаковщиков, выполняющих такие действия как HTTP POST, HTTP GET и HTTP PUT. Чтобы использовать функции cURL, необходимо включить пакет libcurl в среде PHP. PHP-сервер, например Zend Core, предоставляет лёгкий способ включения cURL одним щелчком мыши через программу администрирования Zend Core.

SimpleXML – ещё одно расширение PHP. Как видно из названия, это расширение облегчает работу с XML, будь то чтение XML-документа или запись в него. В PHP версии 5 и выше это расширение включено по умолчанию.

Массивы являются частью базового языка PHP и значительно облегчают работу с коллекциями структур данных. С помощью PHP-массивов мы будем собирать результаты элементов RSS-канала.

Исходный код компонента чтения каналов представляет собой PHP-файл с именем rss_feed_reader.php. Он состоит из следующих трёх специальных функций:

  • get_rss_feeds
  • perform_curl_operation
  • parse_rss_feed_xml

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

Как показано в листинге 3, get_rss_feeds – это главная функция бизнес-логики, которая вызывается из компонента агрегатора каналов. Эта функция принимает на вход три следующих исходных значения у вызывающей функции:

  • имя RSS-провайдера
  • URL-адрес RSS-провайдера
  • максимальное количество элементов RSS-канала, которое вызывающая функция хочет получить у данного провайдера каналов

Логика этой функции вызывает другую функцию для выполнения сетевых операций cURL, который доставляет содержимое основанного на XML RSS-канала с удалённого URL-адреса провайдера каналов. Он создаёт три массива, каждый из которых содержит коллекцию одного из трёх элементов (title, link, description), которые мы будем собирать с каждого элемента канала в полученном контенте RSS-канала. Из листинга 1 следует, что они являются дочерними элементами внутри элементов <item> в полученном контенте RSS-канала. Далее, логика функции передаёт строку полученного RSS-контента и три массива другой функции, которая анализирует XML-контент и собирает элементы RSS-канала. Если в результате анализа контента RSS-канала мы получаем значение false, это означает, что в полученном контенте RSS-канала была ошибка. В случае ошибки эта функция возвращает пустой массив вызывающей функции. в результате анализа мы получаем значение true, это означает, что элементы канала были проанализированы успешно и в трёх массивах теперь содержится заголовок (title), адрес (URL) и описание (description) каждого полученного элемента канала. Затем эта функция создаёт новый массив результатов, который должен быть возвращён вызывающей функции. В каждом из верхних пяти индексов массива результатов эта функция хранит соответственно имя провайдера, общее количество полученных элементов канала, массивы заголовка, URL и описания. Наконец, она возвращает этот массив результатов вызывающей функции.

Листинг 3. Функция get_rss_feeds
function get_rss_feeds(& $rss_provider_name, & $rss_provider_url,
   & $max_rss_items_required) {	
   // Проверяем, равно ли max_rss_items_required нулю
   if ($max_rss_items_required <= 0) {
      // Возвращаем пустой массив.
      $empty_array = array();
      return($empty_array); 			
   } // Конец условия ($max_rss_items_required <= 0)
	
   // Идём далее и извлекаем RSS-контент из данного RSS-провайдера.
   $received_rss_feeds = perform_curl_operation($rss_provider_url);

   // Иногда XML-данные неправильно кодируются при помощи utf8,
   // Это может привести к проблемам при анализе. Кодируем данные правильно.
   $received_rss_feeds = utf8_encode($received_rss_feeds);	
	
   // Канал пуст?
   if (empty($received_rss_feeds)) {
      // Возвращаем пустой массив.
      $empty_array = array();
      return($empty_array); 	
   } // Конец условия (empty($received_rss_feeds))
	
   // Мы получили непустой результат от провайдера RSS-каналов.
   // Создаём три пустых массива, содержащих значения из полученных элементов RSS-канала.
   $rss_feed_title_array = array();
   $rss_feed_url_array = array();
   $rss_feed_description_array = array();

   // Теперь можно выполнить анализ отдельных элементов RSS-канала.
   $parser_result = parse_rss_feed_xml($received_rss_feeds, $max_rss_items_required,
      $rss_feed_title_array, $rss_feed_url_array, $rss_feed_description_array);
	
   // Проверяем, удалось ли выполнить анализ XML-контента RSS-канала.
   if ($parser_result == true) {
      // Мы успешно проанализировали результаты RSS-канала.
      // Создаём массив и наполняем его результатами, как 
      // описывается выше в комментариях по описанию функции.
      $result_array = array();
      // Отправляем имя RSS-провайдера.
      $result_array[0] = $rss_provider_name;
      // Сообщаем, сколько элементов RSS-канала возвращается. 
      $result_array[1] = sizeof($rss_feed_title_array);
      // Отправляем массив, содержащий различные заголовки RSS-канала.
      $result_array[2] = $rss_feed_title_array;
      // Отправляем массив, содержащий различные URL-адреса RSS-канала.
      $result_array[3] = $rss_feed_url_array;
      // Отправляем массив, содержащий различные описания RSS-канала.
      $result_array[4] = $rss_feed_description_array;
      // Теперь возвращаем массив результатов.
      return($result_array);		
   } else {
      // Не удалось проанализировать элементы RSS-канала.
      // Возвращаем в качестве результата пустой массив.
      $empty_array = array();
      return($empty_array); 			
   } // Конец условия ($parser_result == true)
} // Конец функции get_rss_feeds

Как показано в листинге 4, функция perform_curl_operation выполняет операцию HTTP GET для извлечения содержимого удалённого URL; это происходит так изящно благодаря библиотеке PHP cURL. Данная функция принимает значение удалённого URL в качестве входного аргумента от вызывающей функции. В нашем случае вызывающей является функция get_rss_feeds, о которой мы говорили ранее. Логика функции perform_curl_operation инициализирует новый сеанс cURL. Затем она задаёт различные опции cURL, например, удалённый URL, опцию не включать HTTP-заголовки в ответ, опцию перехода по определённому адресу, если имеется HTTP-заголовок адреса, а также опцию, заставляющую cURL возвращать HTTP-ответ в виде строки из функции curl_exec. Потом она вызывает функцию curl_exec, которая подключается к удалённому URL и извлекает контент RSS-каналов, доступный в этот момент. В ходе этой сетевой операции функция curl_exec будет заблокирована до завершения HTTP-операции. Далее, она закрывает сеанс cURL и возвращает полученный контент RSS-канала вызывающей функции.

Листинг 4. Функция perform_curl_operation function
function perform_curl_operation(& $remote_url) {
   $remote_contents = ";
   $empty_contents = ";

   // Инициализируем сеанс cURL и получаем дескриптор.
   $curl_handle = curl_init();

   // Открыт ли сеанс cURL?
   if ($curl_handle) {
      // Задаём необходимые опции CURL.
      // Задаём опцию URL.
      curl_setopt($curl_handle, CURLOPT_URL, $remote_url);
      // Задаём опцию HEADER. В выходных данных нам не нужны заголовки HTTP.
      curl_setopt($curl_handle, CURLOPT_HEADER, false);
      // Задаём опцию FOLLOWLOCATION. Выполнение будет продолжено,
      // если присутствует заголовок адреса.
      curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true);
      // Вместо использования обратного вызова WRITEFUNCTION, мы собираемся получить
      // удалённое содержимое как возвращаемое значение функции curl_exec.
      curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);

      // Пытаемся извлечь содержимое удалённого URL.
      // Эта функция блокируется, пока не будет получено содержимое.
      $remote_contents = curl_exec($curl_handle);
      // Выполняем очистку CURL.
      curl_close($curl_handle);
      
      // Теперь проверяем результат CURL.
      if ($remote_contents != false) {
         return($remote_contents);
      } else {
         return($empty_contents);
      } // Конец условия ($remote_contents != false)  	
   } else {
      // Невозможно инициализировать cURL.
      // Без этого ничего не получится.
      return($empty_contents);
   } // Конец условия ($curl_handle)
} // Конец функции perform_curl_operation

Как показано в листинге 5, функция parse_rss_feed_xml отвечает за получение отдельных элементов канала из полученного контента RSS-канала. Эта функция принимает все свои входные аргументы как контрольные значения. Среди её входных аргументов – строка полученного контента RSS-канала, максимальное количество элементов канала, которые желает получить пользователь, а также три массива, где заголовок, URL и описание всех элементов канала будет возвращено вызывающей функции. Если вы ещё не сталкивались с простым в применении расширением PHP SimpleXML, вы узнаете об этом на примере этой функции. В отличие от сложных методик анализа XML, применяемых в других моделях, SimpleXML в PHP позволяет управлять структурой XML как объектной структурой PHP.

Самый первый шаг – просто загрузить строку полученного контента RSS-канала и получить эквивалентную объектную структуру. Прежде чем начинать анализ, нужно определить, закодирован ли полученный контент RSS-канала с помощью RSS версии 1.0 или любой из других версий RSS. Сделать это можно, определив, является ли <item> дочерним элементом корневого элемента, или же дочерним элементом элемента <channel>. Возможно, вам потребуется обратиться к разделу Основы RSS, чтобы вспомнить небольшие различия форматов разных версий RSS. После определения версии формата RSS и подтверждения правильности полученного XML-контента, будет выполняться итерация по всем элементам <item> (в действительности, в данном случае это PHP-объекты) и анализ полей title, link и description. Каждое из этих трёх значений будет добавлено к соответствующим массивам, переданным этой функции как входные аргументы, на которые делается ссылка. После завершения итерации по максимуму необходимых элементов RSS-канала, эта функция возвращает значение true. Если не удалось проанализировать хотя бы один элемент RSS-канала, функция возвращает значение false.

Листинг 5. Функция parse_rss_feed_xml
function parse_rss_feed_xml(& $received_rss_feeds, 
   & $max_rss_items_required, & $rss_feed_title_array, 
   & $rss_feed_url_array, & $rss_feed_description_array) {
   /*
   Мы задействуем возможности PHP SimpleXML API для
   анализа этих RSS-каналов, закодированных в формате XML.

   Существует несколько версий RSS, а именно 0.91, 0.92, 1.0 и 2.0
   Основное различие между этими версиями сводится к одному 
   из следующих двух форматов.
    
   1) <rss><channel><item>...</item><item>...</item><item>...</item></channel></rss>
   2) <rdf><channel>...</channel><item>...</item><item>...</item><item>...</item></rdf>
    
   В формате 1 элементы <item> являются потомками элемента <channel>.
   В формате 2 элементы <item> являются прямыми потомками
   корневого элемента <rss> или <rdf>.
   Другими словами, в формате 2 все элементы <item> имеют
   общий родительский элемент <channel>. 
   В RSS версии 1.0 используется формат 2, в то время как
   во всех других версиях – формат 1.
   В обоих этих форматах нас интересуют только дочерние элементы между 
   <item>...</item>.
   Наша логика, выполняющая анализ, должна обрабатывать оба эти формата.	
   */	
   // Для начала загрузим строку XML для получения представления объекта SimpleXML.
   $xml = simplexml_load_string($received_rss_feeds);

   // Это валидный XML-документ.
   if ((is_object($xml) == false) || (sizeof($xml) <= 0)) {	
      // Ошибка отладки XML. Возврат.
      return(false);
   } // Конец условия ((is_object($xml) == false) ...
	
   // Теперь нужно определить, являются ли элементы <item> 
   // потомками элемента <channel>, т. е. используется вышеуказанный формат 1 или
   // элементы <item> являются прямыми потомками корневого элемента 
   // <rss> или <rdf>, т. е. используется формат 2.
   $obj1 = $xml->item;
	
   if ((is_object($obj1) == false) || (sizeof($obj1) <= 0)) {
      // Элементы <item> не являются прямыми потомками корневого элемента документа.
      // В данном случае это не формат 2. Следовательно, это формат 1.
      // Перемещаемся к элементу <channel>, чтобы он был новым корневым элементом.
      $xml = $xml->channel;
   } // Конец условия ((is_object($obj1) == false) ...
	
   // Проверяем правильность XML ещё раз.
   if ((is_object($xml) == false) || (sizeof($xml) <= 0)) {
      // Ошибка отладки XML. Возврат.
      return(false);
   } // Конец условия ((is_object($xml) == false) ...
	
   // Инициализируем переменную для подсчёта извлечённых элементов <item>.
   $count_of_rss_items_retrieved = 0;
	
   // Остаёмся в цикле и собираем сведения из элементов <item>.
   foreach ($xml->item as $item) {
      // На данном этапе мы обращаемся к элементам <item> по очереди.
      // Мы не знаем, сколько всего элементов <item>. 
      // Прочитаем элементы title, link и description.
      $rss_feed_title = trim(strval($item->title));
      $rss_feed_url = trim(strval($item->link));
      $rss_feed_description = trim(strval($item->description));
      // Теперь добавим эти значения к имеющимся ссылкам на массивы.
      array_push($rss_feed_title_array, $rss_feed_title);
      array_push($rss_feed_url_array, $rss_feed_url);
      array_push($rss_feed_description_array, $rss_feed_description);
      // Нужно отфильтровать определённое количество элементов <item> 
      // в соответствии с требованиями пользователя. 
      Попробуем сделать это сейчас.		
      $count_of_rss_items_retrieved++;    	
      
      if ($count_of_rss_items_retrieved >= $max_rss_items_required) {
	      // Теперь выходим из этого цикла.
	      break;
      } // Конец условия ($count_of_rss_items_retrieved >= $max_rss_items_required)   	
   } // Конец foreach ($xml->item as $item)
	
   if ($count_of_rss_items_retrieved > 0) {
      // Наконец-то всё получилось.
      return(true);
   } else {
      // Вся эта трудная работа ни к чему ни привела.
      // В следующий раз повезёт больше.
      return(false);
   } // Конец условия ($count_of_rss_items_retrieved > 0)	
} // Конец функции parse_rss_feed_xml

Мы завершили изучение основных задач, выполняемых компонентом чтения каналов; переходим к компоненту ввода источников каналов.

Компонент ввода источников каналов

Компонент ввода источников каналов – простейший компонент в нашей системе. Его задача состоит в получении списка источников канала, которые были настроены пользователем. Этот компонент имеет одну функцию, и она вызывается агрегатором каналов в самом начале запуска программы. Как обсуждалось в начале настоящей статьи, информацию об источниках канала можно указывать в структурах данных программы, в таблице БД или в файле. В нашем случае мы предполагаем, что источники канала будут поставляться при помощи XML-файла. Логика компонента ввода источников каналов просто выполняет чтение из файла, имя которого передаётся как входной аргумент функции. Она считывает содержимое XML-файла и возвращает строку вызывающей функции. Как упоминалось ранее, этот компонент находится в том же исходном файле (rss_feed_aggregator.php) вместе с компонентом агрегатора каналов. В листинге 6 показана простая логика, предназначенная для чтения содержимого входного файла источников каналов:

Листинг 6. Функция get_list_of_rss_feed_sources
function get_list_of_rss_feed_sources($input_xml_file) {
   // Читаем XML-содержимое из входного файла.
   file_exists($input_xml_file) or die("Could not find file " . $input_xml_file);
   $xml_string_contents = file_get_contents($input_xml_file); 
   // Теперь возвращаем XML-содержимое вызывающей функции.
   return($xml_string_contents);
} // Конец функции get_list_of_rss_feed_sources

Во входном файле источников каналов должен содержаться один или более XML-элементов, предоставляющих информацию об имени провайдера каналов, его URL и максимальном количестве элементов RSS-канала, которое пользователь желает получать от провайдера каналов. В листинге 7 показан формат входного XML-файла источников каналов:

Листинг 7. Формат XML-файла ввода источников каналов
<?xml version="1.0" encoding="UTF-8"?>
<ListOfRssFeedSources>
   <!-- Это набор данных для провайдера RSS-каналов 1 -->
   <RssFeedSourceInfo>
      <rssFeedProviderName>Barron's: Markets</rssFeedProviderName>
      <rssFeedProviderUrl>
         http://online.barrons.com/xml/rss/3_7517.xml
      </rssFeedProviderUrl>
      <maximumRssItemsToBeReturned>5</maximumRssItemsToBeReturned>
   </RssFeedSourceInfo>

   <!-- Здесь можно определить дополнительные элементы провайдера RSS-канала. -->
</ListOfRssFeedSources>

Агрегатор каналов

Агрегатор каналов представляет собой компонент, упаковывающий компонент чтения каналов с целью достижения главной цели настоящей статьи - создания настраиваемой функции агрегатора RSS-каналов. В нём используется расширение SimpleXML PHP, о котором шла речь в предыдущем разделе. Кроме того, в нём применяется набор специальной логики, выполняющей агрегацию RSS-каналов. Исходный код этого компонента находится в PHP-файле rss_feed_aggregator.php.

Как показано в листинге 8, компонент агрегатора каналов имеет одну функцию под названием aggregate_rss_feeds. Она принимает в качестве аргумента функции имя входного файла, в котором указаны сведения об источниках RSS-каналов. Если она вызывается без входного аргумента, будет использоваться имя файла по умолчанию – rss_feed_sources.xml. Сначала она вызывает компонент ввода источников каналов для получения сформатированной в виде строки структуры XML, в которой указываются сведения об источниках RSS-каналов. Затем она использует расширение SimpleXML для конвертации сформатированного в виде строки XML-содержимого в объект PHP. Далее, она выполняет итерацию по каждому из имеющихся источников каналов и извлекает имя провайдера каналов, его URL и максимальное количество элементов RSS-канала, которое пользователь желает получать от этого провайдера. Затем она вызывает одну из функций компонента чтения каналов под названием get_rss_feeds, что объяснялось выше. Если результат получения элементов RSS-канала от провайдера будет успешным, то она вызовет компонент вывода результатов каналов. После выполнения итерации по всем источникам каналов эта функция завершает работу, выводя отчёт о процессе агрегации каналов.

Листинг 8. Функция aggregate_rss_feeds
function aggregate_rss_feeds($input_xml_file = RSS_FEED_SOURCES_FILE_NAME) {
   // Декларируем переменную для отслеживания текущего 
   // обрабатываемого источника RSS-каналов.
   $feed_source_sequence_number = 0;
   // Получаем список источников RSS-каналов.
   // В нашем случае мы будем считывать их из входного файла.
   $xml_string_contents = get_list_of_rss_feed_sources($input_xml_file);

   /*
   Мы задействуем возможности PHP SimpleXML API для
   анализа этих RSS-каналов, закодированных в формате XML.	
   */
   // Для начала загрузим строку XML для получения представления объекта SimpleXML.
   $xml = simplexml_load_string($xml_string_contents);

   // Это валидный XML-документ.
   if ($xml == false) {
      print ("Sorry. Your RSS feed sources input file contains invalid data.\n");
      // Ошибка отладки XML. Возврат.
      return;
   } // Конец условия ($xml == false)	
	
   print ("\n");
	
   /*
   Остаёмся в цикле и получаем RSS-каналы из каждого источника.
   Корневой элемент документа входного XML-файла – <ListOfRssFeedSources>
   Внутри корневого элемента имеется один или более блоков данных, имеющих
   следующий формат.

   <RssFeedSourceInfo>
      <rssFeedProviderName>....</rssFeedProviderName>
      <rssFeedProviderUrl>....</rssFeedProviderUrl>
      <maximumRssItemsToBeReturned>....</maximumRssItemsToBeReturned>
   </RssFeedSourceInfo>	

   Мы собираемся выполнить итерацию по всем
   элементам <RssFeedSourceInfo>.
   */
   foreach ($xml->RssFeedSourceInfo as $feed_source) {
      // Считываем сведения о следующем источнике каналов из входного файла.
      $feed_source_sequence_number++;		
      $rss_provider_name = trim(strval($feed_source->rssFeedProviderName));
      $rss_provider_url = trim(strval($feed_source->rssFeedProviderUrl));
      $max_rss_items_required =
         trim(strval($feed_source->maximumRssItemsToBeReturned));
      print ("Getting RSS feeds from $rss_provider_name ...\n");
		
      // Теперь получаем RSS-каналы из этого источника каналов.
      $rss_feeds_result_array = 
         get_rss_feeds($rss_provider_name, $rss_provider_url, $max_rss_items_required);
			
      if (empty($rss_feeds_result_array) == false) {
         // Мы будем выполнять сохранение, только если получим
         // один или более результатов RSS-каналов.
         // Формат массива результатов объясняется
         // в разделе о функции сохранения, о чём речь ниже.
         store_rss_feed_results($feed_source_sequence_number, 
            $rss_feeds_result_array);
      } // Конец условия (empty($rss_feeds_result_array) == false)
   } // Конец foreach ($xml->RssFeedSourceInfo as $feed_source)

   print ("\nFinished getting RSS feeds from $feed_source_sequence_number " .
      "feed sources.\n\n");
   print ("You can view the received feed items in the .\feed_results directory.\n\n");
   print ("Feeds from each active feed source are stored in separate files.\n\n");
   print ("These files are named NNN_rss_feed_items.txt, where NNN corresponds to\n" .
      "the sequence number of the order in which the feed source is\n" .
      "listed in your $input_xml_file file.\n");
} // Конец функции aggregate_rss_feeds

Компонент вывода результатов каналов

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

Как показано в листинге 9, единственная функция компонента вывода результатов каналов под названием store_rss_feed_results, принимает два входных аргумента. Первый аргумент – это порядковый номер файла, который будет использоваться для формирования имени файла результатов. Формат файла результатов – NNN_rss_feed_items.txt, где NNN будет замещён значением первого аргумента функции. Данная функция принимает второй аргумент, который представляет собой вложенный PHP-массив, содержащий все элементы RSS-канала, полученные от определённого провайдера RSS-каналов. Этот массив результатов будет иметь пять элементов, каждый из которых будет содержать одно из следующих значений:

a[0] = имя провайдера RSS-каналов
a[1] = количество элементов канала, полученных от провайдера каналов
a[2] = (rss_feed_title_array) массив заголовков элементов RSS-канала
a[3] = (rss_feed_url_array) массив URL элементов RSS-канала
a[4] = (rss_feed_description_array) массив описаний элементов RSS-канала

Содержимое каждого индекса этих трёх массивов, a[2], a[3] и a[4], взятых вместе, предоставит всю информацию, относящуюся к одному конкретному элементу RSS-канала, присутствующую в полученном RSS XML. Например, объединённая информация из rss_feed_title_array[0], rss_feed_url_array[0] и rss_feed_description_array[0] соответствует первому элементу RSS-канала в полученном XML-контенте RSS.

Сначала логика этой функции создаёт подкаталог feed_results, если его ещё не существует. Далее создаётся файл с уникальным именем для каждого провайдера RSS-каналов. Затем выполняется итерация по массиву результатов и запись в файл со сведениями об элементах RSS-канала для всех элементов канала в массиве результатов. Как вы уже, наверное, знаете, в элемент RSS-канала входит заголовок канала, его URL с подробной информацией и краткое описание. Получив элементы канала, потребители контента могут быстро просмотреть заголовок и описание выбранного канала. Выбрав несколько заинтересовавших их элементов канала, они также могут перейти по URL и получить подробную информацию об этом канале. Таковы преимущества использования агрегации RSS-каналов.

Листинг 9. Функция store_rss_feed_results
function store_rss_feed_results($file_sequence_number, $result_array) {
   // Сначала проверяем, существует ли подкаталог с именем "feed_results".
   if (file_exists(RSS_FEED_RESULTS_DIRECTORY) == false) {
      // Каталог не существует. Создадим его сейчас.
      mkdir(RSS_FEED_RESULTS_DIRECTORY);		
   } // Конец условия (file_exists(RSS_FEED_RESULTS_DIRECTORY) == false)
	
   // Формируем имя файла.
   $result_file_name = sprintf("%s/%03d%s", RSS_FEED_RESULTS_DIRECTORY,
      $file_sequence_number, RSS_FEED_RESULTS_FILE_NAME_SUFFIX);
	
   // Если файл уже был создан в ходе предыдущих запусков, просто удаляем его.
   // Мы перезапишем в него последнюю информацию о канале.
   if (file_exists($result_file_name) == true) {
      unlink($result_file_name);
   }

   // Создаём и открываем файл.
   $handle = fopen($result_file_name, FILE_CREATE_WRITE_FLAG);
	
   if ($handle == false) {
      // Создать файл не удалось. Возврат.
      return;
   } 
	
   // Можно начинать запись полученных RSS-каналов в этот файл.
   // Записываем порядковый номер провайдера канала.
   $feed_provider_number = FEED_PROVIDER_SEQUENCE_NUMBER . 
      $file_sequence_number . NEW_LINE;
   fwrite($handle, $feed_provider_number);
   // Записываем имя провайдера канала.
   $feed_provider_name = RSS_FEED_PROVIDER_NAME . $result_array[0] . NEW_LINE;
   fwrite($handle, $feed_provider_name);
   // Записываем количество полученных элементов канала.
   $number_of_received_rss_feeds = RECEIVED_RSS_FEEDS_CNT . 
      $result_array[1] . NEW_LINE;
   fwrite($handle, $number_of_received_rss_feeds);

   $rss_feed_title_array = $result_array[2];
   $rss_feed_url_array = $result_array[3];
   $rss_feed_description_array = $result_array[4];

   // Остаёмся в цикле и записываем заголовок, URL и описание.
   for($cnt=0; $cnt < sizeof($rss_feed_title_array); $cnt++) {
      $feed_item_separator = FEED_ITEM_SEPARATOR_LINE . NEW_LINE;
      fwrite($handle, $feed_item_separator);
      $feed_item_sequence_number = FEED_ITEM_SEQUENCE_NUMBER . 
         ($cnt+1) . NEW_LINE;
      fwrite($handle, $feed_item_sequence_number);
      $feed_item_title = FEED_ITEM_TITLE . 
         $rss_feed_title_array[$cnt] . NEW_LINE;
      fwrite($handle, $feed_item_title);
      $feed_item_url = FEED_ITEM_URL .
         $rss_feed_url_array[$cnt] . NEW_LINE;
      fwrite($handle, $feed_item_url);
      $feed_item_description = FEED_ITEM_DESCRIPTION . NEW_LINE .
         $rss_feed_description_array[$cnt] . NEW_LINE;
      fwrite($handle, $feed_item_description);		
   } // Конец for($cnt=0; $cnt < sizeof($rss_feed_title_array), $cnt++)

   $feed_item_separator = FEED_ITEM_SEPARATOR_LINE . NEW_LINE;
   fwrite($handle, $feed_item_separator);	
   fclose($handle);
} // Конец функции store_rss_feed_results

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

Запуск агрегатора RSS-каналов

Из раздела Загрузка можно загрузить архив, содержащий следующие файлы:

  • rss_feed_aggregator\rss_feed_reader.php
  • rss_feed_aggregator\rss_feed_aggregator.php
  • rss_feed_aggregator\rss_feed_sources.xml

Распакуйте все три файла на ваш компьютер. Убедитесь, что у вас есть среда PHP, например, среда Zend Core, настроенная на работу cURL и SimpleXML. Один из входящих в состав архива файл (rss_feed_sources.xml) представляет собой пример входного XML-файла источников каналов, содержащий 22 разных источника RSS-каналов. Эти конкретные источники каналов предоставляют RSS-каналы в категории "Бизнес и финансы". При желании можно определить собственный входной XML-файл.

Для запуска агрегатора RSS-каналов нужно использовать следующую команду (обратите внимание, что последний элемент в конце приведённой ниже команды является дополнительным аргументом командной строки):

php -f rss_feed_aggregator.php <Feed sources input XML filename>

Ресурсный центр developerWorks по Ajax
Посетите ресурсный центр по Ajax, единый сайт, на котором размещены бесплатные инструменты, код и информация по разработке Ajax-приложений. На форуме Ajax-сообщества, который ведёт эксперт по Ajax Джек Херрингтон, можно связаться с коллегами – у них могут быть ответы на вопросы, которые возникли у вас в данный момент.

Как обсуждалось ранее, если не задавать аргумент командной строки, приложение будет использовать входной XML-файл источников каналов по умолчанию (rss_feed_sources.xml). При использовании входного XML-файла источников каналов по умолчанию и при условии, что всё работает правильно, вы должны увидеть результаты, как на рисунке 3.

Рисунок 3. Результаты запуска агрегатора RSS-каналов
Результаты запуска агрегатора RSS-каналов

В каталоге, из которого запускается агрегатор RSS-каналов, вы должны увидеть новый подкаталог с именем feed_results. В этом подкаталоге должно находиться несколько файлов, содержащих элементы RSS-канала, полученные от каждого из провайдеров RSS-каналов, которые указал пользователь во входном XML-файле источников каналов.

Заключение

RSS существует уже несколько лет. Её популярность в настоящее время растёт благодаря появлению технологий Web 2.0, например, wiki, блогов, гибридных приложений, порталов социальных сетей и других сервисов агрегации информации. Статья рассказывает о том, насколько прост формат RSS, что делает его отличным выбором для информационной интеграции с другими появляющимися Web-технологиями. Поскольку Интернет – это, прежде всего, информация, RSS продолжит играть главную роль в определении мощных и полезных способов синдикации и распространения этой информации.

В статье представлена информация по разработке агрегатора каналов с помощью полнофункциональных PHP-скриптов. Использовать исходный PHP-код из данной статьи можно разными способами: как автономный инструмент, общую библиотеку для использования с существующей PHP-программой, размещённой на сервере или в виде функции Web-сервиса SOAP/REST, принимающего участие в корпоративной сервис-ориентированной архитектуре (SOA).

Многие великие вещи в жизни (Интернета) очень просты. RSS – просто одна из них. И это не каламбур!


Загрузка

ОписаниеИмяРазмер
Код агрегатора RSS-каналовwa-aj-rssphp.zip10119 Kб

Ресурсы

Научиться

  • Оригинал статьи: Build a customizable RSS feed aggregator in PHP (EN).
  • Посетите раздел PHP на developerWorks, в котором можно найти исчерпывающие ресурсы по PHP-проектам (EN).
  • Узнайте всё, что вы хотели знать об XML, посетив раздел XML на developerWorks.
  • Прочтите документацию по PHP API в Руководстве по PHP (EN).
  • Узнайте больше о Zend Core (EN).
  • Прочтите публикацию Что такое RSS? от O'Reilly Media (EN).
  • Ознакомьтесь с историей RSS, рассказанной разработчиком большинства форматов, посетив ресурс История RSS на сайте юридического факультета Гарвардского университета (EN).
  • Ещё один взгляд на историю RSS (EN).
  • Просмотрите этот пример файла RSS 2.0 на сайте юридического факультета Гарвардского университета (EN).
  • Посетите раздел Как использовать RSS сайта UserLand RSS Central (EN).
  • Так как Web 2.0 весьма популярен в кругах разработчиков, в разделе Web-разработка сайта developerWorks вы найдёте постоянно обновляющуюся коллекцию ресурсов.
  • В блоге Диона Хинчклиффа по Web 2.0 подробно рассматривается множество вопросов, в том числе Роль RSS в Web 2.0 (EN).
  • В статье Введение в RSS-каналы (Джеймс Левин, developerWorks, ноябрь 2000 г.) рассматривается формат RSS и некоторые модули Perl с открытым исходным кодом, которые позволят вам без труда работать с RSS-файлами (EN).
  • Узнайте больше об RSS, Atom и программах для чтения каналов в статье Введение в синдикацию (Винсент Лауриа, developerWorks, март 2006 г.) (EN).
  • В статье Каналы содержимого с RSS 2.0 (Джеймс Левин, developerWorks, декабрь 2003 г.) делается обзор RSS 2.0, рассматриваются новые RSS-разработки и подробно разбирается этот важный формат (EN).
  • Статья Реализация синдикации новостей с помощью RSS и Atom (Ин Ин Линь, developerWorks, сентябрь 2006 г.) показывает, как реализовать стандартную архитектуру публикации новостей с помощью форматов синдикации RSS и Atom для облегчения процесса и снижения вероятности человеческой ошибки (EN).
  • Ознакомьтесь с методикой, помогающей посетителям вашего сайта читать каналы RSS и Atom и понимать, как они работают, в статье Создание более дружелюбных каналов RSS и Atom (Бенуа Маршал, developerWorks, октябрь 2006 г.) (EN).
  • Создание программы для чтения RSS-каналов на Ajax рассматривается в статье Ajax-программа чтения RSS-каналов (Джек Д. Херрингтон, developerWorks, апрель 2007 г.) (EN).
  • В руководстве Расширяя границы RSS (Джонатан Левин, developerWorks, декабрь 2007 г.) представлен новаторский подход, в котором свойства ассоциативности известного формата RSS используются для эмуляции функциональности простой реляционной базы данных (EN).
  • Читайте Web 2.0 Journal, ведущий в мире ресурс по Web 2.0 (EN).

Получить продукты и технологии

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы в первый раз заходите в developerWorks. Выберите данные в своем профиле (имя, страна/регион, компания) которые будут общедоступными и будут отображаться, когда вы публикуете какую-либо информацию. Вы можете изменить данные вашего ИБМ аккаунта в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Web-архитектура, Open source, XML
ArticleID=469506
ArticleTitle=Создание настраиваемого агрегатора RSS-каналов на PHP
publish-date=02242010