БлогNot. Пишем собственный агрегатор RSS

Пишем собственный агрегатор RSS

Формат новостных каналов RSS "хоронили" уже не раз, но он жив и продолжает использоваться на огромном количестве и новостных, и тематических сайтов.

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

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

Будем исходить из следующих предположений и соглашений:

  • всё работает в современных версиях PHP 7.X, впрочем, в версиях 5.4 и выше тоже должно выполняться;
  • у скрипта на хостинге всё в порядке с правами для доступа к файлу в своей папке - он будет кэшировать в нём контент ленты, чтобы не собирать информацию заново при каждом обращении каждого пользователя. Время, когда пора читать данные из удалённых источников, определим в 10 минут;
  • с каждого сайта будем получать не больше 5 новостей, как правило, сайты сортируют их по убыванию даты;
  • чтобы не усложнять себе жизнь, выводить время публикации новости не будем, а только дату - так как сервера работают в разных часовых поясах, непонятно, какой из них "правильный". Плюс дата-время из формата RSS естественным образом конвертируется в метку времени Unix по Гринвичу. Кроме того, время в ленте RSS тоже выставлено скриптом того сервера, который формировал ленту, и оно совсем не обязательно соответствует времени реальной публикации статьи;
  • не станем "утяжелять" класс лишними проверками корректности входных данных, как видно из листинга описать данные в массиве $feed несложно - добавить заголовок сайта, URL канала и список разрешённых в описании новости тегов;
  • предполагаем, что наши каналы возвращают содержимое в кодировке Юникода UTF-8 и скрипт тоже работает в ней же;
  • предполагаем, что наши каналы имеют 4 общепринятых в RSS свойства каждого элемента - title, link, description, pubDate (см. по ссылке о написании своего канала в начале статьи);
  • новости со всех сайтов выводятся единым списком по убыванию даты (точней, метки времени) публикации;
  • "сам по себе" обновляться агрегатор не будет, а только когда кто-нибудь загружает страницу, где он расположен;
  • агрегатор не содержит стилей или других элементов оформления, не считая того, что каждая новость грузится в свой абзац. На реальном сайте, конечно, можно также прикрутить стили, шапку и подвал к странице с агрегатором, как сделано вот тут, например.

Вот наш агрегатор в работе и листингом на момент написания. Я добавил ленту своего блога и несколько шахматных сайтов, здесь код выполняется в теге <iframe>.

<!DOCTYPE html>
<html lang="ru">
<head>
 <meta charset="utf-8">
</head>
<body>

<?php
 class rssFeed {
  private $file, $sources;
  function __construct ($sources) {
   $this->sources = [];
   foreach ($sources as $source) { $this->sources[] = $source; }
   $this->file = "./feed-cache.txt";
   return $this->getContent();
  }
  function getContent() { //Кэширует в файле, чтоб не лезть за новостями чаще чем раз в 10 минут
   $current_time = time();
   $expire_time = 10 * 60;
   $file_time = (file_exists($this->file) ? filemtime ($this->file) : $current_time);
   if (file_exists($this->file) and $current_time - $expire_time < $file_time) {
    return file_get_contents ($this->file);
   }
   else {
    $content = $this->updateContent();
    file_put_contents($this->file, $content);
    return $content;
   }
  }
  function updateContent () {
   $data = [];
   foreach ($this->sources as $source) {
    $rss = @simplexml_load_file ($source['url']);
    if ($rss === false) {
     $data[] = array ('text'=>'<p>Не могу получить новости с канала "'.
      $source['title'].'" <i>(<a href="'.$source['url'].'" target="_blank">'.
      $source['url'].'</a>)</i></p>','date'=>time());
     continue;
    }
    $count = 0;
    foreach ($rss->channel->item as $item) {
     $count++;
     if ($count > 5) break; //Берём не больше этого количества новостей с каждого сайта
     $date = strtotime($item->pubDate);
     $text = '<p><a href="'.htmlspecialchars($item->link,ENT_QUOTES,'UTF-8').'" target="_blank">'.
      htmlspecialchars($item->title,ENT_QUOTES,'UTF-8').'</a>: '.
      strip_tags($item->description,$source['tags']).' <i>('.$source['title'].', '.
      strftime ("%d.%m.%Y", $date).')</i></p>';
     $data[] = array ('text'=>$text,'date'=>$date);
    }
   }
   $text  = array_column($data, 'text');
   $date  = array_column($data, 'date');
   array_multisort ($date, SORT_DESC, $text, SORT_ASC, $data);
   $text  = array_column($data, 'text');
   return implode("\n",$text);
  }
  function __toString() { return $this->getContent(); }
 }

 $feed = new rssFeed ([ //Собираемые ленты: название, URL-адрес, разрешённые теги
  ['title'=>'Блог Перса' , 'url'=>'http://blog.kislenko.net/rss.php', 'tags'=>'<br><b><i><u><s><a>'],
  ['title'=>'chess-news' , 'url'=>'http://chess-news.ru/rss', 'tags'=>'<br><b><i><u><s><a>'],
  ['title'=>'ФШР' , 'url'=>'https://www.ruchess.ru/news/all/rss/', 'tags'=>'<br><b><i><u><s><a>'],
  ['title'=>'chess24.com' , 'url'=>'https://chess24.com/ru/read/news.rss', 'tags'=>'<br><b><i><u><s><a>'],
  ['title'=>'chess.com' , 'url'=>'https://www.chess.com/ru/rss/news', 'tags'=>'<br><b><i><u><s><a>']
 ]);
 echo $feed;
?>

</body></html>

теги: шахматы программирование php rss время форматы

11.07.2020, 03:23; рейтинг: 109