Processing LiveJournal RSS standard means PHP

RSS - this is a standard XML-document, originally developed by Netscape, and further developed under the auspices of W3C. The task of the standard - to pass a brief distillation of updated information as the so-called "channel» (channel). Most often in the RSS format are provided the latest news or announcements of informational materials. Interest of developers, facing the exchange of information in RuNet gradually increases, while in the foreign segment of the web, this standard is already being used quite frequently, and finds more and more adherents.

In order to understand what's inside your RSS 2.0, quite familiar with standard specification and to view examples. Are urged to read this document.

Задача.

Ныне популярный сервис  LiveJournal  предоставляет возможность доступа к RSS-каналам дневников. Именно это и натолкнуло меня на мысль о том, что дневник можно «прикрутить» к любому сайту, даже будучи бесплатным пользователем. Оговорюсь, разработчики Живого Журнала предоставляют относительно удобные средства интегрирования журнала и сайта только платным пользователям.

LiveJournal RSS.

http://www.livejournal.com/users/bikman/data/rss — по этому адресу располагается RSS-канал дневника. Конечно, имя пользователя (в данном случае bikman) нужно изменить на то, которое интересует читателя. Я же во всех примерах буду использовать свой жж. Думаю, большой проблемой смена имени пользователя не станет.

Лучшим способом понять, как устроен RSS, автоматически генерируемый Живым Журналом, является анализ примера, приведенного ниже:

<?xml version="1.0" encoding="utf-8" ?>
<rss version="2.0">
<channel>
<title>bikman's</title>
<link>http://www.livejournal.com/users/bikman/</link>
<description>bikman's - LiveJournal.com</description>
<lastBuildDate> Wed, 14 Jan 2004 01:20:41 GMT </lastBuildDate>
<generator>LiveJournal / LiveJournal.com</generator>
<image>
<url>http://userpic.livejournal.com/8412362/1106951</url>
<title>bikman's</title>
<link>http://www.livejournal.com/users/bikman/</link>
<width>100</width>
<height>100</height>
</image>
<item>
<guid isPermaLink="true">
http://www.livejournal.com/users/bikman/94200.html</guid>
<pubDate> Wed, 14 Jan 2004 01:10:34 GMT </pubDate>
<title>Заголовок второго поста.</title>
<link>http://www.livejournal.com/users/bikman/94200.html</link>
<description>
Здесь находится текст второго поста.
</description>
</item>
<item>
<guid isPermaLink="true">
http://www.livejournal.com/users/bikman/94200.html</guid>
<pubDate> Wed, 14 Jan 2004 01:10:34 GMT </pubDate>
<title>Заголовок второго поста.</title>
<link>http://www.livejournal.com/users/bikman/94200.html</link>
<description>
Здесь находится текст второго поста.
</description>
</item>
</channel>
</rss>


Согласно  стандарту RSS 2.0 элементов может быть сколь угодно много. Я умышленно привожу только два, будучи уверенным, что этого вполне достаточно для понимания устройства данного конкретного документа. Стандартно  LiveJournal генерирует 25 элементов .

PHP и XML.

Поскольку одним из самых распространенных языков в Сети является PHP, я как раз и опишу, как воспользоваться этим интерпретатором для «сращивания» дневника и сайта.

Широко распространенных способов обработки XML-документов существует два — Event-based APIs и Document Object Model (DOM) APIs. В PHP стандартная поддержка XML организована с помощью Event-based API (основана на событиях). Это диктует принципы кодирования. Я не стану подробно останавливаться на вопросе обработки XML-документов. Полагаю, читателю, знакомому с вопросом, это покажется неинтересным, а для непосвященных в таинства XML, напечатаны тонны книг высоко профессиональных авторов.

Функции PHP предназначенные для обработки XML.

  • xml_parser_create()

    Создает экземпляр обработчика XML.

  • xml_set_element_handler(parser, startElementFunction, endElementFunction)

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

    startElementFunction определяет функцию для открывающих тэгов,  endElementFunction соответственно для закрывающих.  parser — это объект, возвращенный функцией xml_parser_create().

  • xml_set_chracter_data_handler(parser, characterDataFunction)

    Данная функция определяет обратный вызов, осуществляемый при обработке события, связанного с нахождением данных, содержащихся между XML-тэгами. Назначение параметров аналогично  xml_set_element_handler().

  • xml_parse(parser, data, endOfDocument)

    Эта функция инициализирует анализ переданного ей XML-кода. Параметр  endOfDocument должен быть равен  true, если параметр  data содержит конец документа, или же  false, если  data не включает в себя документ до конца. Это позволяет обработчику корректно обрабатывать незавершенные тэги и прочие ошибки форматирования документа, возникающие в связи с тем, что переменная  data не может содержать данные длиной более 4-х килобайт.

  • xml_parser_free(parser)

    Функция освобождает память, занятую объектом  parser.

Описание данных функций — это всего лишь краткий ликбез. Более подробную информацию по данной теме можно получить на официальном сайте PHP.

Основные принципы и алгоритмы.

Прежде чем приводить пример реализации задачи, кратко опишу основные идеи и алгоритмы.

Естественно, сначала стоит создать HTML-страницу, на которой будет отображаться форматированная информация, полученная из RSS-канала.

Сперва создадим класс RSSParser, внутри которого будет выполняться вся работа по разбору XML, и который будет выводить на печать (или же в поток вывода от сервера к браузеру) форматированные HTML-данные.

После создания класса, получим RSS-данные от сервиса  LiveJournal  и инициализируем обработчик XML, который будет использовать для событийной обработки (Event-based API) класс  RSSParser.

Пример реализации.

Подробные разъяснения приведены после примера.

<html>
<head>
<title>Заголовок сайта</title>
</head>
<body>
<?php
class RSSParser {
var $insideItem = false;
var $tag = "";
var $title = "";
var $description = "";
var $originalLink = "";
var $dt = "";
function startElement($parser, $tagName, $attrs)
{
if($this->insideItem)
{
$this->tag = $tagName;
}
elseif($tagName == "ITEM")
{
$this->insideItem = true;
}
}
function endElement($parser, $tagName)
{
if($tagName == "ITEM")
{
printf("<h2>%s</h2>", $this->title);
printf("<h3>%s</h3>", $this->dt);
printf("<p>%s</p>", $this->description);
printf("<p><a href=\"%s\" target=\"_blank\">%s</a></p>",
trim($this->originalLink), " Коментарии ");
$this->title = "";
$this->originalLink = "";
$this->description = "";
$this->dt = "";
$this->insideItem = false;
}
}
function characterData($parser, $data)
{
if($this->insideItem)
{
switch($this->tag)
{
case "TITLE":
$this->title .= $data;
break;
case "DESCRIPTION":
$this->description .= $data;
break;
case "LINK":
$this->originalLink .= $data;
break;
case "PUBDATE":
$this->dt .= $data;
break;
}
}
}
}
$xml_parser = xml_parser_create("UTF-8");
$rss_parser = new RSSParser();
xml_set_object($xml_parser, &$rss_parser);
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, "characterData");
$fp = fopen("http://www.livejournal.com/users/bikman/data/rss", "r")
or die("Error reading RSS data!");
while($data = fread($fp, 4096))
{
xml_parse($xml_parser, $data, feof($fp))
or die("Error parsing RSS data!");
}
fclose($fp);
xml_parser_free($xml_parser);
?>
</body>
</html>

 

Стоит учитывать, что чаще всего XML-документы хранятся в Unicode кодировке UTF-8, а в Рунете наиболее часто используемой кодировкой является Windows-1251. В этом раскладе приходится решать проблему перекодировки, или же достаточно сохранить саму страницу в UTF-8 и указать браузеру, что используется именно эта кодировка.

Для перекодировки следует использовать функции  iconv() и mb_convert_encoding(). Поскольку данные функции являются дополнительными для интерпретатора PHP и содержатся в подключаемых модулях, я не могу гарантировать их работоспособность в условиях конкретного сервера и оставляю это на совести читателя, считая, что подал идеи для решения проблемы переокдировки.

Согласно  стандарту RSS 2.0  дата в RSS-документах должна храниться в формате, описанном в RFC 822 (Sat, 07 Sep 2002 00:00:01 GMT). Читателя может не устроить данный формат даты, и ему может потребоваться его изменить. Могу предложить вот такой способ.

Вместо:

printf("<h3>%s</h3>", $this->dt);

Сделать так:

printf("<h3>%s</h3>", date("d.m.y - H.i.s",  strtotime($this->dt));
Чтобы выбрать любой другой необходимый читателю формат, достаточно изменить строку, передаваемую в функцию  date() в качестве формата. За подробными объяснениями следует обратиться к описанию функции на официальном сайте PHP.

Разъяснения.

Я умышленно привел весь код решения задачи целиком, чтобы не сдерживать опытного читателя, коему не требуются дополнительные пояснения и достаточно только просмотра кода. Ниже по тексту я рассмотрю подробно механизм анализа XML-документа, основанного на событиях, и постараюсь максимально понятно разъяснить приведенный код.

Класс RSSParser.

Лично я считаю (и не только я), что использование директивы  global в скриптах является примером «ленивого» программирования. Поэтому предлагаю реализацию функций обратного вызова на события обработчика XML оформить в виде специального класса  RSSParser.

class RSSParser {
var $insideItem = false;
var $tag = "";
var $title = "";
var $description = "";
var $originalLink = "";
var $dt = "";

Класс объявляется стандартной директивой  class и содержит несколько свойств. Свойство  insideItem нужно для того, чтобы отследить момент, когда обработчик находится внутри тэга . В этот момент оно устанавливается в true.

Остальные свойства отвечают за информацию, которую несет в себе запись в дневнике. Это дата (dt), текст (description) и заголовок (title).

Свойство  tag сохраняет в себе тот XML-тэг, внутри которого находится обработчик.

function startElement($parser, $tagName, $attrs)
{
if($this->insideItem)
{
$this->tag = $tagName;
}
elseif($tagName == "ITEM")
{
$this->insideItem = true;
}
}

Функция обратного вызова  startElement($parser, $tagName, $attrs) начинает работать в тот момент, когда обработчик натыкается на открывающий тэг. Параметр  parser — это уже известный объект, созданный функцией  xml_parser_create(). Параметр  tagName — название того тэга, который анализирует обработчик XML, а параметр  attrs — это атрибуты текущего тэга.

Стоит отметить, что имена тэгов в PHP коде должны задаваться только(!) прописными буквами.

function endElement($parser, $tagName)
{
if($tagName == "ITEM")
{
printf("<h2>%s</h2>", $this->title);
printf("<h3>%s</h3>", $this->dt);
printf("<p>%s</p>", $this->description);
printf("<p><a href=\"%s\" target=\"_blank\">%s</a></p>",
trim($this->originalLink), "Коментарии");
$this->title = "";
$this->originalLink = "";
$this->description = "";
$this->dt = "";
$this->insideItem = false;
}
}

 

Функция  endElement($parser, $tagName) отвечает за обработку события, возникающего при обнаружении закрывающего тэга. Параметр  tagName содержит имя этого тэга.

Именно эта функция выводит всю информацию, которую получает класс во время обработки XML-документа. Она же обнуляет все внутренние переменные по завершении вывода.

function characterData($parser, $data)
{
if($this->insideItem)
{
switch($this->tag)
{
case "TITLE":
$this->title .= $data;
break;
case "DESCRIPTION":
$this->description .= $data;
break;
case "LINK":
$this->originalLink .= $data;
break;
case "PUBDATE":
$this->dt .= $data;
break;
}
}
}

Функция  characterData($parser, $data), как было сказано выше, обрабатывает данные, которые находятся внутри XML-тэгов, присваивая свойствам класса соответствующие значения.

Создание класса требуется для того, чтобы передавать параметры из одной функции обратного вызова в другую (их передают свойства класса), не используя директиву  global.

Основной код.

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

$xml_parser = xml_parser_create("UTF-8");
$rss_parser = new RSSParser();
xml_set_object($xml_parser, &$rss_parser);
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, "characterData");
$fp = fopen("http://www.livejournal.com/users/bikman/data/rss", "r")
or die("Error reading RSS data!");
while($data = fread($fp, 4096))
{
xml_parse($xml_parser, $data, feof($fp))
or die("Error parsing RSS data!");
}
fclose($fp);
xml_parser_free($xml_parser);

Заключение.

На этом я заканчиваю статью, посвященную обработке RSS-каналов, которые автоматически генерирует сервис  LiveJournal. Думаю, что мой вариант скрипта можно легко адаптировать под конкретные нужды. Он вполне общий, чтобы не ограничивать фантазию программиста.

 

Примечание:

Хочу выразить благодарность Кэвину Янку (Kevin Yank) за его статью на SitePoint — «PHP and XML: Parsing RSS 1.0», которая послужила вдохновением для моих изысканий. Вы можете  посмотреть результат работы кода и  скачать исходный код.

Источник: http://bikman.ru/texts/techarticle/ljrssphp/  © Дмитрий Бикман 2002—2004

vis_1

Contact

518 4067
220 5532

icq 255018290

info@redsoft.ru

 

 

Redsoft-участник top25 рейтинга рунета

Redsoft - участник Top25
рейтинга студий России