RSS для Яндекс.новостей

Мы решили подключить наш сайт к Яндекс.новостям. Яндексу нужна RSS особого вида, та, что livestreet отдаёт из коробки ему не подходит.
За основу был взят ActionRSS, который есть в ливстрите с самого начала. Вообще-то я в php ни в зуб ногой, на работе пишу на c++, и ливстрит увидел в первый раз в жизни, так что работа эта заняла у меня не меньше 4 часов. В том числе потому что документацию к движку я тогда найти не смог.

Вот такие задачи следовало решить:
  • добавить пространство имён yandex в RSS
  • сменить описание (<description>) канала на что-то более информативное, чем «Новости по-ростовски / RSS Channel»
  • добавить логотип сайта размером «не более 100 пикселей по максимальной стороне».
  • добавить в каждую новость элемент <yandex:full-text>, содержащий текст новости без единого HTML-тега.
  • заменить элементы <category>, описывающие набор тегов, на один элемент <category>, описывающий рубрику (в нашем случае — блог публикации)
  • выпилить все ненужные Яндексу элементы из RSS
  • сделать систему фильтрации новостей для Яндекса. У нас сайт гражданской журналистики, который помимо новостей содержит анонсы всяких культурных штук, крики души пользователей, непроверенные сообщения — в общем, всякие штуки, за которые на Яндекс-новостях могут забанить


А такие задачи решить было желательно, но не обязательно:
  • добавить картинки и прочие media-элементы из новости в виде элементов <enclosure>
  • добавить ссылки по теме новости в виде элемента <yandex:related>
  • добавить элемент <author> с именем-фамилией автора сообщения

Проблему фильтрации договорились решить следующим образом — в RSS попадают только посты с главной, помеченные определенным тегом. Чтобы он не мозолил глаз огромным шрифтом в облаке тегов, я отключил его показ в облаке. Это, конечно, костыль, и рано или поздно я эту радость заменю чем-нибудь поприличнее — например, отдельным булевым полем в базе и галочкой «Пустить на Яндекс» у администратора. Или хотя бы выделю в плагин.

Очень не хотелось писать парсер, который бы вылавливал медиа-элементы из новости и добавлял их в RSS как элементы <enclosure>. Тем более, что вообще-то не всякую картинку из статьи стоит добавлять к новости. Зато у нас шаблон simple, который позволяет к каждому посту добавить картинку-preview. Вот её мы и решили запихивать в RSS. На всякий случай я сделал эту функциональность отключаемой.

RSS-лента будет располагаться по адресу /yandex_rss. Полученная в результате RSS-лента была подключена к Яндексу без малейших нареканий.

А вот получившийся код.
/classes/actions/YandexRss.class.php
<?php
/*-------------------------------------------------------
*
*   LiveStreet Engine Social Networking
*   Copyright © 2008 Mzhelskiy Maxim
*
*--------------------------------------------------------
*
*   Official site: www.livestreet.ru
*   Contact e-mail: rus.engine@gmail.com
*
*   GNU General Public License, version 2:
*   http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
---------------------------------------------------------
*/

/**
 * Делает RSS для Яндекса
 * Автор класса vsh, базируется на классе vovazol(http://livestreet.ru/profile/vovazol/)
 *
 */
class ActionYandexRss extends Action {

	public function Init() {		
		$this->SetDefaultEvent('index');
		Router::SetIsShowStats(false);
	}

	/**
	 * Указывает браузеру правильный content type в случае вывода RSS-ленты
	 */
	protected function InitRss() {
		header('Content-Type: application/rss+xml; charset=utf-8');	
	}
	
	protected function RegisterEvent() {
		$this->AddEvent('index','RssYandex');
	}
	
	protected function RssYandex() {							
		$aResult=$this->Topic_GetTopicsGood(1,Config::Get('module.topic.per_page')*2,false);
		$aTopics=$aResult['collection'];
		
		$aChannel['title']=Config::Get('view.name');
		$aChannel['link']=Config::Get('path.root.web');
		$aChannel['description']=htmlspecialchars(Config::Get('yandex_rss.description')); 
		$aChannel['language']='ru';
		$aChannel['managingEditor']=Config::Get('general.rss_editor_mail');
		$aChannel['generator']=Config::Get('view.name');
		$aChannel['image']=Config::Get('yandex_rss.image'); 
		$bPrintPreview=Config::Get('yandex_rss.print_preview'); 
		$sAllowTag=Config::Get('yandex_rss.allow_tag'); 

		
		$topics=array();
		foreach ($aTopics as $oTopic){
			if (in_array($sAllowTag, $oTopic->getTagsArray()))
			{
				$item['title']=$oTopic->getTitle();
				$item['guid']=$oTopic->getUrl();
				$item['link']=$oTopic->getUrl();
				$item['pubDate']=$oTopic->getDateAdd();
				$item['author']=$oTopic->getUser()->getLogin();
				$item['yandexFullText']=$this->getYandexFullTopicText($oTopic);	
				$oBlog=$oTopic->getBlog();
				$item['category']=htmlspecialchars($oBlog->getTitle());
				if($bPrintPreview)
				{
					$item['preview'] = $this->getPreviewUrl($oTopic);
				}
				$topics[]=$item;
			}
		}
		
		$this->InitRss();
		$this->Viewer_Assign('aChannel',$aChannel);
		$this->Viewer_Assign('aItems',$topics);
		$this->SetTemplateAction('index');
	}
	
	/**
	 * выдирает адрес preview в зависимости от типа топика
	 * если preview нет, возвращает ''
	 *
	 */		
	protected function getPreviewUrl($oTopic)
	{
		if ($oTopic->getType()=='photoset')
		{
			$oMainPhoto=$oTopic->getPhotosetMainPhoto();						
			return $oMainPhoto->getWebPath('220crop');
		}
		else if ($oTopic->getPreviewImage())
		{
			return $oTopic->getPreviewImageWebPath('220crop');					
		}					
		else 
		{
			return '';					
		}	
	}

	/**
	 * Формирует текст топика для индексации RSS роботом яндекса
	 *
	 */	
	protected function getYandexFullTopicText($oTopic) {
		$sText=$oTopic->getText();
		return htmlspecialchars(strip_tags($sText));
	}
	
}
?>


/templates/skin/ваш темплейт/actions/ActionYandexRss/index.tpl
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:yandex="http://news.yandex.ru" xmlns:media="http://search.yahoo.com/mrss/">
<channel>
	<title>{$aChannel.title}</title>
	<link>{$aChannel.link}</link>
	<description>{$aChannel.description}</description>
	<image>
	<url>{$aChannel.image}</url>
	<title>{$aChannel.title}</title>
	<link>{$aChannel.link}</link>	
	</image>
	{foreach from=$aItems item=oItem}
		<item>
			<title>{$oItem.title|escape:'html'}</title>
			<link>{$oItem.link}</link>
			<pubDate>{date_format date=$oItem.pubDate format="r"}</pubDate>			
			<category>{$oItem.category}</category>
			<yandex:full-text>{$oItem.yandexFullText}</yandex:full-text>
			{if $oItem.preview}
				<enclosure url="{$oItem.preview}"/>
			{/if}
		</item>
	{/foreach}
</channel>
</rss>


эти строчки нужно добавить в /config/config.local.php
/**
* Настройка rss для Яндекса
*/
$config['router']['page']['yandex_rss']    = 'ActionYandexRss';
$config['yandex_rss']['description'] ='Очень клёвый новостной сайт';
$config['yandex_rss']['image']='http://путь.к/yandex-logo.png';
$config['yandex_rss']['print_preview']=true; //печатать preview или нет
$config['yandex_rss']['allow_tag']='foobar'; //тег, по которому отбираются топики с главной страницы, регистр важен.


а это костыль в /classes/blocks/BlockTags.class.php
<?php
/*-------------------------------------------------------
*
*   LiveStreet Engine Social Networking
*   Copyright © 2008 Mzhelskiy Maxim
*
*--------------------------------------------------------
*
*   Official site: www.livestreet.ru
*   Contact e-mail: rus.engine@gmail.com
*
*   GNU General Public License, version 2:
*   http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
---------------------------------------------------------
*/

/**
 * Обрабатывает блок облака тегов
 *
 */
class BlockTags extends Block {
	
	public function Exec() {			

		/**
		 * Получаем список тегов
		 */
		$aTags=$this->oEngine->Topic_GetOpenTopicTags(70);
		
		/* фильруем тег для яндекс-новостей */
		function FilterYandexTag($oTag)
		{
			$sAllowTag=Config::Get('yandex_rss.allow_tag'); 
			return $oTag->getText() != $sAllowTag;
		}		
		$aTags=array_filter($aTags, "FilterYandexTag");
		
		/**
		 * Расчитываем логарифмическое облако тегов
		 */
		if ($aTags) {
			$iMinSize=1; // минимальный размер шрифта
			$iMaxSize=10; // максимальный размер шрифта
			$iSizeRange=$iMaxSize-$iMinSize;
			
			$iMin=10000;
			$iMax=0;
			foreach ($aTags as $oTag) {
				if ($iMax<$oTag->getCount()) {
					$iMax=$oTag->getCount();
				}
				if ($iMin>$oTag->getCount()) {
					$iMin=$oTag->getCount();
				}
			}			
			
			$iMinCount=log($iMin+1);
			$iMaxCount=log($iMax+1);
			$iCountRange=$iMaxCount-$iMinCount;
			if ($iCountRange==0) {
				$iCountRange=1;
			}
			foreach ($aTags as $oTag) {
				$iTagSize=$iMinSize+(log($oTag->getCount()+1)-$iMinCount)*($iSizeRange/$iCountRange);
				$oTag->setSize(round($iTagSize)); // результирующий размер шрифта для тега
			}
			/**
		 	* Устанавливаем шаблон вывода
		 	*/
			$this->Viewer_Assign("aTags",$aTags);
		}
	}
}
?>

16 комментариев

avatar
Дельно!

Буквально на днях делал тоже самое для своего проекта, чтобы стало человечнее в частности аватарка пользователя в фиде и «о себе» из профиля в дискрипшн фида, тоже самое для коллективного блога — сделал проще картинка собирается в одну строку
"{$aChannel.image} = <image>\r\t<url>{$aChannel.image}</url>\r\t\t<title>{$aChannel.title}</title>\r\t\t<link>{$aChannel.link}</link>\r\t</image>"
(на самом деле не так, но чтоб стало понятнее) и если у пользователя нет аватара то ничего не выводится (в шаблоне фида $aChannel.image пустая сторка)
avatar
Кто нибудь просвятите для чего нужен этот функционал? Это получается что новости (топики из определенных блогов) транслируются куда, в яндекс новости? В чем польза от этого для сайта? Если реально полезно может кто плагин сделает?
avatar
Юрий, это нужно было для новостного сайта на ливстрите. Те новости, которые попадают в топ-5 яндекса по нашему городу (его можно увидеть в шапке над поиском в яндексе), собирают по 400-500 просмотров. В день — шапка меняется обычно раз в пол дня. Так что для новостного городского сайта на ливстрите есть польза от яндекс.новостей. Ощутимая или нет на будущее, когда все знают уже этот сайт — не знаю. Пока нам яндекс.новости привлекли только двух новых пользователей, которые оставили по несколько комментов. То есть просмотров — до 1000 в день, а новых пользователей — мало.
Конечно, для того, чтобы попасть в яндекс.новости, мы сделали не только rss-ленту, а списывались с сотрудниками яндекса, те одобряли сайт, потом я посылала письмо и факс со своими данными в компанию Яндекс на имя его гендира… Но это другая история.
avatar
Спасибо большое, теперь все понятно.
avatar
а проектом похвастайтесь и что за город?
по москве и питеру попасть в топ последние 2-3 года уже не реально.
avatar
У нас Ростов-на-Дону. Сейчас сайт идет как «тематический», на самой последней ступени в рейтинге. Однако, попадали за неделю четыре раза в топ. Конечно, Яндекс любит больше РИА-новости, Интерфакс, Комсомольскую правду. Но и местные сайты иногда ставит. Если есть какие-то дополнительные факты, а не просто рерайт. Сайтом уже хвасталась: livestreet.ru/blog/sites/10263.html
avatar
Подскажите, а в Яндексе строго относятся к наличию зарегистрированного СМИ в подобной ситуации?
avatar
Им все равно. Мы до сих пор не зарегистрированы как СМИ. И ничего:) Главное, чтобы у вас была возможность указать адрес и телефон в анкете на странице партнера яндекса (можно и домашний, в принципе). Без контактов вас не подключат.
avatar
уважаемый автор, а можно ли где-то скачать готовые сорцы? Очень заинтересовало
avatar
Так они ж все в посте есть.
avatar
Вроде сделал все как в посте, но тег все равно отображается на странице и по ссылке /yandex_rss сайт выдает ошибку :( 0.5.1, соответственно. есть у кого идеи?
avatar
А у меня вопрос: вот я хочу сделать так, что бы новость отбиралась не по тегу, а из блога «Новости». Я так понимаю, нужно исправить вот это условие:

if (in_array($sAllowTag, $oTopic->getTagsArray()))


Пожалуйста, подскажите, как оно должно выглядеть?
avatar
Сделал все как написано — исчезли все счетчики и блок от Яндекс.директ.

Подскажите, пож, с чем может быть связано и как поправить? Версия 0.4.2
avatar
Извините, вопрос снят — ошибся немного при копировании.
avatar
Сделал все как написано, но по адресу мой сайт/yandex_rss ничего не открывается. Версия 0.4.2

Подскажите, пож., как поправить!

Заранее благодарю.
avatar
А заодно перестала открываться директория /rss, которая раньше открывалась…
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.