parser

Написать ответ на текущее сообщение

 

 
   команды управления поиском

RSS - ридер а-ля Sanja v.2

Эдуард Габдуллин 05.05.2006 15:31

Полсле трех дней ожесточенных боев получилось следующее. За основу взят класс rss.p - автор Sanja v.2:
http://www.parser.ru/forum/?id=26071
кое-что подправил, в основном работу с датами и SQL - запросы, вот то, что у меня получилось:
##############################
# В этот класс вынесен весь код, который
# отвечает за чтение RSS потоков с других сайтов.
@CLASS
rss

##############################
# Получение списка RSS ньюсфидов, которые не обновлялись больше часов, чем
# указано в столбце min_refresh_period и их обновление
@update_list[][limit;rss_feeds]

^if(def $form:limit){
	$limit[$form:limit]
	$limit(^limit.int(15))
}{$limit(15)}

$rss_feeds[
	^MAIN:pSQL.table{
		SELECT
			uri,
			hours_to_keep
		FROM
			rss_list
		WHERE
			is_enabled="yes"
		AND
			last_update < ^date_sub_hour[;min_refresh_period]
		ORDER BY
			last_update
	}[$.limit($limit)]
]

^if(def $rss_feeds){
	^rss_feeds.menu{
		^update_feed[$rss_feeds.uri]
		^MAIN:pSQL.void{
			DELETE FROM
				rss_items
			WHERE
				pubdate < ^date_sub_hour[;$rss_feeds.hours_to_keep]
			AND
				feed_id = "$rss_feeds.uri"
		}
	}
}


##############################
# Повторное чтение RSS потока, разбор и запись в базу обновлений:
@update_feed[feeduri][rss;cleanup;replacements;rss_text;xml;items;updatedcnt;counts;rss_version;date;title;link;description]

# пытаемся загрузить RSS файл с помощью ^file::load
# При этом может произойти целая туча неприятностей - другой сервер может не отвечать,
# файл окажется испорчен или удалён - на этот случай мы пользуемся оператором ^try

^try{
	$rss[^file::load[text;^untaint{$feeduri};
		$.timeout(5)
		$.any-status(1)
		$.headers[
			$.User-Agent[RSS aggregator from HimOil.ru]
		]
	]]

#	Поскольку парсер неспособен считать XML-элементы наподобие <item rdf:about="http://
#	выскребаем их из документа:
	
	$replacements[^table::create{from	to
encoding="UTF-8"	encoding="windows-1251"
encoding="utf-8"	encoding="windows-1251"
encoding='utf-8'	encoding='windows-1251'
encoding="iso-8859-1"	encoding="windows-1251"
encoding='iso-8859-1'	encoding='windows-1251'
 rdf:	 rdf_
&mdash^;	-
&hellip^;	...
&nbsp^;
&quot^;	"
"	"
"	"
&copy^;	(c)
&laquo^;	"
&raquo^;	"
&trade^;	 <sup>(TM)</sup>
</rdf:RDF>	</rss>
xmlns=	xmln_dis=
xmlns:	xmln_dis_
<rdf:li	<rdf_li
<rdf:Seq	<rdf_Seq
</rdf:Seq	</rdf_Seq
rdf:resource=	rdf_resource=
<admin:generatorAgent	<admin_generatorAgent
<rdf:RDF	<rss version="2.0"
dc:subject>	subject>
dc:creator>	creator>
dc:publisher>	publisher>
syn:updateFrequency>	syn_updateFrequency>
syn:updatePeriod>	syn_updatePeriod>
syn:updateBase>	syn_updateBase>
sy:updateFrequency>	sy_updateFrequency>
sy:updatePeriod>	sy_updatePeriod>
sy:updateBase>	sy_updateBase>
dc:language>	language>
dc:rights>	rights>
dcterms:isReferencedBy	dcterms_isReferencedBy
admin:errorReportsTo	admin_errorReportsTo
<content:encoded>	<description>
</content:encoded>	</description>
trackback:ping	trackback_ping
trackback:about	trackback_about
dc:date>	pubDate>}]

	$rss_text[^untaint{$rss.text}]

#	Дочищаем текст:
	$rss_text[^rss_text.replace[$replacements]]

#	Это - на случай особо извращённых интерпретаций RSS (с <rdf:Seq>):
	
	^if(^rss_text.match[rdf_Seq][]){

		$replacements[^table::create{from	to
</channel>^taint[^#0A]<items>^taint[^#0A]</items>^taint[^#0A]</rss>	</channel></rss>}]

		$rss_text[^rss_text.replace[$replacements]]
	}

#	Проверяем канал (RCCnews.ru) и испраляем кое-что
	^if(^rss_text.match[RCCNews.ru][]){
		$replacements[^table::create{from	to
</channel>^taint[^#0A]<image>	<image>
</items>^taint[^#0A]</rss>	</channel></rss>}]
		$rss_text[^rss_text.replace[$replacements]]
	}

	$xml[^xdoc::create{$rss_text}]
#	Избавляемся от корневого тега RDF
	$items[^xml.select[/rss/channel/item]]

$updatedcnt(0)

^if(def $items){
#	Создаем табличку замен для каналов, которые любят ненужное форматирование
	$cleanup[^table::create{from	to
<img src="http://top.list.ru/counter?js=na^;id=427384^;t=57" border="0" height="1" width="1" alt="Рейтинг@Mail.ru">	
<span style='font-family^: Tahoma, Verdana^;font-weight^: bold^;font-size^: 18px^;color^: #666666^;'></a>	
<span style='font-family^: Tahoma, Verdana^;font-weight^: bold^;font-size^: 18px^;color^: #666666^;'>	
<span style='font-family^: Tahoma^;font-size^: 11px^;color^: #666666^;text-decoration^: none^;'><br>	
<span style='font-family^: Tahoma^;font-size^: 11px^;color^: #666666^;text-decoration^: none^;'>	
</span>	
<br>^taint[^#0A]<br>	<br />
^taint[^#0A]<br> </p>	
 <p> 	
 </p> 	
<br>	<br />
<br /><br />	<br />}]

#	Нам нужно узнать версию RSS-потока, который мы получили.
# 	Тонкость в том, что у RSS 2.0 есть тэг PubDate, а у RSS 0.91 нету.
#	Поэтому для RSS 2 мы пользуемся тем, что есть, а для старых версий RSS
#	пользуемся политикой "дата, когда выкачали = дата создания"
	$rss_version[^xml.selectString[string(rss/@version)]]
	^for[i](1;$items){
		^if($rss_version eq "2.0"){
			$date{^xml.selectString[string(/rss/channel/item[position() = $i]/pubDate)]}
			^if(def $date){
				$date[^dtf:from822[$date]]
			}{
				$date[^date::now[]]
			}
		}{
#			Если мы имеем дело с RSS старой версии, берём текущую дату:
			$date[^date::now[]]
		}
		$title{^xml.selectString[string(/rss/channel/item[position() = $i]/title)]}
		$title[^title.replace[$cleanup]]
		$link{^xml.selectString[string(/rss/channel/item[position() = $i]/link)]}
		$description{^xml.selectString[string(/rss/channel/item[position() = $i]/description)]}
		$description[^description.replace[$cleanup]]
#		Проверка для канала Mabico.ru (эти идиоты шлют кучу пробелов переносов и точку)
		^if(^description.length[] <= 14){
			$description[$title]
		}

		$counts[^MAIN:pSQL.table{SELECT COUNT(*) AS cnt FROM rss_items WHERE link = "$link"}]

		^if(^counts.cnt.int(1) == 0){
			^MAIN:pSQL.void{INSERT INTO rss_items (feed_id, title, link, description, pubdate)
				VALUES ('$feeduri', '$title', '$link', '$description', '^dtf:format[%Y-%m-%d %H:%i:%S;$date]')
			}
			^updatedcnt.inc[]
		}

	}
# 	Обновляем запись об этом RSS-фиде в базе:
	^if($updatedcnt >= 0){^MAIN:pSQL.void{UPDATE rss_list SET last_update = ^MAIN:pSQL.now[] WHERE uri = "$feeduri"}}
}
}{
	$exception.handled(1)
}


##############################
# Вывод кешированных RSS-элементов из базы
@display[][rss_items]
$rss_items[
	^MAIN:pSQL.table{
		SELECT
			title,
			link,
			description,
			pubdate,
			rss_list.uri, rss_list.name, rss_list.allow_untaint
		FROM
			rss_items
			^MAIN:pSQL.left_join[from;rss_list;rss_items.feed_id=rss_list.uri]
		ORDER BY
			pubdate DESC
	}[$.limit(20) $.offset(^if(def $form:skip){$form:skip}{0})]
]
^if(def $rss_items){
	^rss_items.menu{
		<p>
		<b>^dtf:format[%d.%m.%Y %H:%i;$rss_items.pubdate] 
		^if(def $rss_items.title){
			<a href="$rss_items.link">^untaint{$rss_items.title}</a>
		}{
			Без заголовка
		}</b>
		<br />
		^if($rss_items.allow_untaint eq "yes"){
			$desc[^untaint[as-is]{$rss_items.description}]
		}{
			$desc[^untaint[html]{$rss_items.description}]
		}
		^if(def $desc){
			^if(^desc.length[] >= 255){
				$desc[^desc.left(255)&#133^; <a href="$rss_items.link">далее&#133^;</a>]
			}
			$desc
		}{
			<i>Эта запись - без текста</i>
		}
		<br />
		<span class="textSmall" style="font-style:italic">
		^if(def $rss_items.uri){
			<a href="$rss_items.uri">$rss_items.name</a>
		}{
			$rss_items.name
		}
		</span>
		</p>
	}
}
структура таблиц здесь:
http://www.parser.ru/forum/?id=26072
внимание поля hours_to_keep и min_refresh_period - в часах

проверено на полутора десятках входящих каналов RSS 0.91 и 2.0, вроде бы работает

чуть не забыл...
############################################################
@date_sub_hour[sDate;iHour]
$result[DATE_SUB(^if(def $sDate){$sDate}{NOW()},INTERVAL $iHour HOUR)]
#end @date_sub_hour[]