Сайт Романа ПарпалакаЗаметкиТехнологииВеб-разработкаПравильная организация RSS

Правильная организация RSS

21 апреля 2007 года

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

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

<?xml version="1.0" encoding="windows-1251"?>
<rss version="2.0">
   <channel>
      <title>Название RSS-потока</title>
      <link>http://site.ru</link>
      <description>Описание RSS-потока</description>
      <item>
         <title>Название 1</title>
         <link>http://site.ru/articles/1</link>
         <description>Описание статьи 1</description>
         <author>Автор &lt;author@site.ru&gt;</author>
         <pubDate>Sun, 25 Mar 2007 00:00:00 +0300</pubDate>
      </item>
      <item>
         <title>Название 2</title>
         <link>http://site.ru/articles/2</link>
         <description>Описание статьи 2</description>
         <author>Автор &lt;author@site.ru&gt;</author>
         <pubDate>Sun, 18 Mar 2007 00:00:00 +0300</pubDate>
      </item>
      <item>
         <title>Название 3</title>
         <link>http://site.ru/articles/3</link>
         <description>Описание статьи 3</description>
         <author>Автор &lt;author@site.ru&gt;</author>
         <pubDate>Sun, 11 Mar 2007 00:00:00 +0300</pubDate>
      </item>
   </channel>
</rss>

Для подобных целей подойдет текстовый редактор, или простейший скрипт (недостатки такого подхода очевидны, впрочем, я о них уже писал).

Понятно, что если на сайте есть хотя бы минимальная автоматизация, RSS может генерироваться скриптом, например, на PHP. Можно предложить два разумных способа работы этого скрипта:

  1. Скрипт вызывается при обновлении сайта и результаты своей работы сохраняет в обычный XML-файл, на который вы будете давать ссылки.
  2. Скрипт отрабатывает каждый раз, когда браузер (или иной пользовательский агент) обращается к RSS и отдает результаты браузеру.

Здесь нужно остановиться и обратить внимание на основные факты: зачем вы делаете RSS, большая ли аудитория у сайта и т. д. Различия между первым и вторым способом в основном связаны с особенностями работы протокола HTTP, а именно, с заголовками Last-Modified и If-Modified-Since (ознакомьтесь с описанием работы этих заголовков, повторяться смысла нет, а дальше они активно используются). Различия между этими двумя способами мы посмотрим на примере первого и повторных обращений к RSS-ленте, а потом я приведу одну из возможных реализаций.

Статический XML-файл

При первом обращении содержимое этого файла отдается целиком. При повторных обращениях (когда кто-то подписан на ваш RSS) благодаря механизму If-Modified-Since сервер сообщает, что файл не изменился. В случае обновления файла с RSS он опять отдается полностью.

Динамический скрипт

При первом обращении он отдает информацию обо всех элементах. При повторных обращениях скрипт сообщает, что изменений не было, и ничего не отправляет. Однако если мы добавили, например, еще одну статью, то по содержимому заголовка If-Modified-Since скрипт определяет, когда было последнее обращение к RSS, и выдает только добавившиеся элементы (идея найдена на xpoint.ru). Именно здесь заключается то улучшение, которого можно добиться вторым способом.

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

Описание скрипта

Есть смысл через RSS отправлять информацию о нескольких последних статьях (обычно так и делают). Чем чаще в RSS появляются новые элементы, тем больше информации нужно экспортировать при постоянной частоте обращения, чтобы подписанные пользователи не пропустили каких-нибудь объявлений.

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

Функция get_last_modified() должна возвращать дату и время последней новости, экспортируемой через RSS, и это значение будет установлено в заголовке Last-Modified. Хотя здесь можно отдавать время внесения последних изменений в новости, экспортируемые через RSS в надежде на то, что программы для чтения RSS обновят ранее полученную информацию.

Следует отметить, что в некоторых полях отдаваемого XML, например, в поле description могут присутствовать html-теги, но они должны быть преобразованы в entities (знак < в комбинацию &lt; и т. д.), что делает функция PHP htmlspecialchars().

Итак, сам скрипт:

<?

// Запрет кеширования, см. описание
function no_cache() {
   header("Expires: Mon, 26 Jul 1997 00:00:00 GMT");
   header("Pragma: no-cache");
}

// Перевод даты из формата RFC 2822 в unixstamp
function date2unixstamp($s) {
   $months = array (
      "Jan"=>1, "Feb"=>2, "Mar"=>3,
      "Apr"=>4, "May"=>5, "Jun"=>6,
      "Jul"=>7, "Aug"=>8, "Sep"=>9,
      "Oct"=>10, "Nov"=>11, "Dec"=>12);

   $a = explode(" ", $s);
   $b = explode(":", $a[4]);
   return gmmktime($b[0], $b[1], $b[2],
      $months[$a[2]], $a[1], $a[3]);
}

// Получение массива элементов
function get_recent() {
   ...
}

// Дата и время последнего изменения
function get_last_modified() {
   ...
}

$HTTP_HOST = isset($_SERVER["HTTP_HOST"]) ?
   $_SERVER["HTTP_HOST"] : "site.ru"; // ваш сайт

no_cache();

$recent_articles = get_recent();

// Буферизация для установки заголовка Content-Length
// и сжатия отправляемых данных
ob_start();
ob_start("ob_gzhandler");

echo '<?xml version="1.0" encoding="windows-1251"?>'."\n";
?><rss version="2.0">
   <channel>
      <title>Название RSS</title>
      <link><?=$HTTP_HOST?></link>
      <description>Описание</description>
<?

$last_date = 0;

if (isset($_SERVER["HTTP_IF_MODIFIED_SINCE"])) {
   $last_date = date2unixstamp($_SERVER["HTTP_IF_MODIFIED_SINCE"]);
}

$counter = 0;

foreach($recent_articles as $recent) {
   if ($recent["date"] > $last_date) {
      $counter++;
?>
      <item>
         <title>
            <?=htmlspecialchars($recent["title"])?>
         </title>
         <link><?=$recent["link"]?></link>
         <description>
            <?=htmlspecialchars($recent["desc"])?>
         </description>
         <author>Автор &lt;author@site.ru&gt;</author>
         <guid><?=$recent["link"]?></guid>
         <pubDate><?=date("r", $recent["date"])?></pubDate>
      </item>
<?
   }
}
?>
   </channel>
</rss>
<?

if (!$counter) {
   header("HTTP/1.1 304 Not Modified");
   exit();
}

ob_end_flush();

header("Content-Length: ".ob_get_length());
header("Last-Modified: ".
   gmdate("D, d M Y H:i:s", get_last_modified())." GMT");
header("Content-Type: text/xml; charset=WINDOWS-1251");
ob_end_flush();

?>

Чтобы скрипт заработал, достаточно на него дать ссылку. Если вы хотите, чтобы у него было «красивое» имя вроде http://site.ru/rss.xml, пропишите в файл .htaccess следующее:

RewriteEngine on
Options +FollowSymlinks
RewriteBase /

RewriteRule ^rss.xml$ some_path/php123/rss.php [L]

some_path/php123/rss.php — это «некрасивый» путь, по которому лежит наш скрипт. Если mod_rewrite не установлен, тогда пропишите туда же вот что:

AddType application/x-httpd-php xml

и поместите скрипт прямо в файл rss.xml. Будьте внимательны! В последнем случае все файлы с расширением xml будут обрабатываться парсером PHP.

Чтобы браузеры смогли определить, что на вашем сайте есть RSS, в разделе описаний страницы следует разместить тег <link>:

<html>
<head>
   <meta http-equiv="Content-Type"
      content="text/html; charset=windows-1251" />
   <title>Заголовок страницы</title>
   <link rel="alternate" type="application/rss+xml"
      title="Название RSS" href="http://site.ru/rss.xml" />
</head>
<body>
   ...
</body>
</html>

На этом всё. Пользуйтесь на здоровье.

Поделиться

Комментарии

#1. 8 мая 2007 года, 18:50. C пишет:
Отличная статья. И код читается хорошо, обычно я чужой код с трудом понимаю. Я давно хотел обмастерить RSS, скачал книжек про него, и думал, что это сильно сложная технология, раз там написано на несколько сот страниц, а это обычный xml! Ну, почитать более внимательно те книжки конечно стоит, но вообще с вашей помощью всё просто. Теперь я его уже не боюсь.

Ещё бы про этот mod_rewrite написали, как вы его понимаете. Я вот регулярки перловские в php замастерил, а вот этот mod_rewrite сильно сложен.
#2. 8 мая 2007 года, 21:06. Роман Парпалак пишет:
Есть отличный перевод документации по mod_rewrite (http://www.egoroff.spb.ru/portfolio/apache … write.html). Первый результат в Яндексе по запросу mod_rewrite.
#3. 15 мая 2007 года, 12:57. Владислав Сычев пишет:
Отлично написаный материал. Прям очень хорошо. В коде разобрался. Хотя он и бех комментариев. Переделывал свой файл выдачи просто выдерая куски. Однакож вот вопрос пришлось ob_start(); отключать? это не смертельно?
#4. 15 мая 2007 года, 13:48. Владислав Сычев пишет:
get_last_modified() функция неопределена ???
#5. 15 мая 2007 года, 15:22. Роман Парпалак пишет:
Не совсем понятно, почему Вам пришлось отключить ob_start(), вроде всё должно работать. Если можете, опишите, в чем проблема.

Если выкинуть ob_start(), то данные перед отправкой браузеру не будут сжиматься, и еще возникнут трудности с установкой заголовка Content-Length (нужно будет по-другому считать объем отсылаемых данных). В принципе, конечно, всё останется работоспособным и без этого заголовка.

get_last_modified() должна возвращать дату последнего изменения информации, отсылаемой через RSS (например, дату последней новости). Вы правы, я про нее не написал. Исправлю.
#6. 15 мая 2007 года, 18:41. Владислав Сычев пишет:
ну какие то проблемы с сжатием. убрал аргумент во втором вызове od_start() и все стало нормально отдаваться. да я понимал что заголовок и прочее. в общем то у меня и так РСС работал но у вас именно упор на заголовок в статье собственно он позволяет снижать нагрузку на сайт и отдавать только обновленное. с датой да я так и поступил поставил дату последней новости.
#7. 15 июня 2007 года, 12:29. Андрей пишет:
Здравствуйте! Пытался разобраться, все сделал, как вы написали, только вот какая ошибка получилась
"Не удается отобразить страницу XML
Не удается просмотреть ввод XML с использованием списка стилей . Исправьте ошибку и затем нажмите кнопку «Обновить»или повторите попытку позднее. "


#8. 15 июня 2007 года, 14:21. Роман Парпалак пишет:
Как вы думаете, что можно понять из вашего описания проблемы? Правильно, очень немногое.

Насколько я понял, ваш RSS не открывается в браузере (каком, кстати?). Попробуйте проверить его, например, на feedvalidator.org. Если RSS доступен из Интернета, дайте ссылку на него, я посмотрю, что там не в порядке.
#9. 15 июня 2007 года, 17:01. FreeZ пишет:
А я нифига не понял... Мне нужен скрипт, чтобы разместить его на странице и он показывал рсс поток, который прописать в нём же можно, но такого скрипта я не увидел...
#10. 15 июня 2007 года, 19:28. Роман Парпалак пишет:
Я тоже нифига не понял, что вам нужно. Честно.
#11. 9 июня 2008 года, 08:46. Sprin пишет:
Роман, в рсс имею (сам генерю) тег линк:
<link>http://la.sprin.ru/?pid=8&id=1212953573</link>
Любой парсер xml ругается на знак &, который в моем случае необходим для передачи доп. параметров. Как можно это разрулить именно в xml (rss), если учесть, что параметры передавать необходимо обязательно?
#12. 9 июня 2008 года, 18:38. пишет:
По идее, в атрибуте href ссылок знак '&' должен заменяться своим html-эквивалентом '&amp;'. Это должно сработать и для тега <link>. Если не поможет, попробуйте провести такую замену еще раз, то есть написать туда '&amp;amp;'. Разумеется, надо будет проверить работоспособность в rss-читалках (я сам сейчас проверить не могу).
#13. 7 января 2009 года, 18:13. пишет:
И эта статья актуальна, мне кажется...
Я помещу у себя со ссылкой на Ваш сайт.
#14. 16 сентября 2010 года, 12:44. Виталий пишет:
Спасибо большое! http://kerzoll.org.ua — применил ваш пример здесь ))) Штатный РСС как-то не очень подходил, а этот в самый раз.

Оставьте свой комментарий

Ваше имя:

Комментарий:

Для выделения используйте следующий код: [i]курсив[/i], [b]жирный[/b].
Цитату оформляйте так: [q = имя автора]цитата[/q] или [q]еще цитата[/q].
Ссылку начните с http://. Других команд или HTML-тегов здесь нет.

Сколько будет 21+4?