parser

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

 

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

RSS френдлента а ля LiveJournal

Sanja v.2 01.10.2003 11:27

Значит, родил я subj. Код ниже, пользуйтесь. Это кусок более масштабного проекта (знающие люди могут убедиться, что он продвигается).

Обновление RSS'ов вызывается так:
^rss:update_list[]
вывод сообщений со всех подписанных сайтов в одну френдленту - так:
^rss:display[]
Создайте две таблицы в MySQL, метод ^MAIN:dbconnect со строкой подключения в корневом auto.p и запишите туда же следующие переменные:
# URL сервера (без слэша на конце!) Обычно угадывается верно, но, например, 
# на хостинге Zenon это не всегда так, и приходится вписывать вручную):
$global_server[$env:SERVER_NAME]

# Сдвиг вашего локального времени (не летнего, обычного) от Гринвича
# (в Лондоне - 12:00, в Москве - 15:00, получается, что $global_timeoffset = +3 )
# (Для жителей центральной Австралии - ваш offset - дробный: "9.5")

$global_timeoffset(3)

# Ваше локальное время сдвигается летом?
$global_daylight[yes]

# Это происходит с [день, месяц] по [день, месяц]:
# (обычно с 29-Mar по 25-Oct, см. http://www.worldtimezone.com/daylight.htm)

$global_daylight_m_start(3)
$global_daylight_d_start(29)

$global_daylight_m_end(10)
$global_daylight_d_end(25)
SQL Dump
#
# Table structure for table 'cityblog_rssitems'
#

DROP TABLE IF EXISTS cityblog_rssitems;
CREATE TABLE cityblog_rssitems (
  id int(16) unsigned NOT NULL auto_increment,
  title text,
  link varchar(255) NOT NULL default '/',
  description text NOT NULL,
  pubdate datetime NOT NULL default '0000-00-00 00:00:00',
  PRIMARY KEY  (id),
  UNIQUE KEY id (id),
  KEY id_2 (id)
) TYPE=MyISAM COMMENT='Items from RSS feeds';

#
# Table structure for table 'cityblog_rsslist'
#

DROP TABLE IF EXISTS cityblog_rsslist;
CREATE TABLE cityblog_rsslist (
  uri varchar(255) NOT NULL default '',
  name varchar(255) default NULL,
  last_update datetime NOT NULL default '0000-00-00 00:00:00',
  is_enabled enum('yes','no') NOT NULL default 'no',
  min_refresh_period tinyint(3) unsigned NOT NULL default '1',
  hours_to_keep tinyint(3) unsigned NOT NULL default '24',
  PRIMARY KEY  (uri),
  UNIQUE KEY uri (uri),
  KEY uri_2 (uri)
) TYPE=MyISAM COMMENT='RSS feeds to fetch';



#
# Dumping data for table 'cityblog_rsslist'
#

INSERT INTO cityblog_rsslist (uri, name, last_update, is_enabled, min_refresh_period, hours_to_keep) VALUES("http://forum.gfk.ru/forum/rss.html", "Forum.GfK.ru", "2003-10-01 11:15:41", "yes", "1", "24");
INSERT INTO cityblog_rsslist (uri, name, last_update, is_enabled, min_refresh_period, hours_to_keep) VALUES("http://www.parser.ru/_rss.html", "Parser.ru Forum", "0000-00-00 00:00:00", "no", "1", "24");
Файлы классов: rss.p
##############################
# В этот класс вынесен весь код, который
# отвечает за чтение RSS потоков с других сайтов.
@CLASS
rss

@USE
log.p

##############################
# Загрузчик
@load[]
$result[]

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

<p>Идёт поиск RSS-лент, которые должны быть обновлены&#133^; 

^MAIN:dbconnect{
	$rss_feeds[
		^table::sql{SELECT
			uri,
			name,
			last_update,
			is_enabled,
			min_refresh_period,
			hours_to_keep
		FROM
			cityblog_rsslist
		WHERE
			is_enabled="yes"
		AND
			(UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(last_update)) >= (min_refresh_period * 60 * 60) # В часе 60 минут по 60 секунд, если вы не в курсе :-)
		ORDER BY
			last_update
		}
	]

	^if(def $rss_feeds){
		<ul>
			^rss_feeds.menu{
				<li><a href="$rss_feeds.uri" target="_new">$rss_feeds.name</a>
				последний раз был открыт $rss_feeds.last_update,
				выполняется обновление&#133^;</li>
				^update_feed[$rss_feeds.uri]
				
				^void:sql{DELETE FROM cityblog_rssitems
					WHERE
						(UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(pubdate)) >= ($rss_feeds.hours_to_keep * 60 * 60)
					AND
						link = "$rss_feeds.uri"
					}
			}
		</ul>
	}{
		<p>В настоящий момент таковых нет.</p>
	}

}


##############################
# Повторное чтение RSS потока, разбор и запись в базу обновлений:
@update_feed[feeduri]

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

^try{
# Считаем, что RSS-поток пришёл в кодировке
	$rss[^file::load[text;^untaint{$feeduri};
		$.timeout(5)
		$.headers[
			$.User-Agent[CityBlog RSS aggregator from $MAIN:$global_server]
		]
	]]
	$xml[^xdoc::create{^untaint{$rss.text}}]
	$items[^xml.select[/rss/channel/item]]
}{
	$exception.handled(1)
	$result[   Не получилось загрузить RSS-поток!]
	^log:write[Не получилось загрузить RSS-поток $feeduri]
}

$updatedcnt(0)

^if(def $items){

<h1>$Encoding</h1>

#	Нам нужно узнать версию 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)]}
#			Вы думали, чехарда с датами закончилась? А нифига ;-)
#			Дата может быть в таком формате - Tue, 30 Sep 2003 14:32:46 MSD
#			или в таком - Mon, 29 Sep 2003 11:23:12 GMT. A есть ещё обозначение "UTC"...
#			see also:	http://208.30.42.17/logistics/tzhelp.asp
#						http://www.worldtimezone.com/wtz-names/wtz-msks.html
#			Нам надо привести это время к нашему локальному
			$date[^date::create[^dates:string2GMT[$date]]]
		}{
#			Если мы имеем дело с RSS старой версии, берём текущую дату, приведённую к Гринвичу:
			$date[^date::create[^dates:GMT_datetime[]]]
		}
		$title{^xml.selectString[string(/rss/channel/item[position() = $i]/title)]}
		$link{^xml.selectString[string(/rss/channel/item[position() = $i]/link)]}
		$description{^xml.selectString[string(/rss/channel/item[position() = $i]/description)]}


		$countitems[^table::sql{SELECT 
			COUNT(*) 
		AS 
			cnt 
		FROM 
			cityblog_rssitems
		WHERE
			link = "$link"
		}]

		$counter[$countitems.cnt]
		$counter(^counter.int(1))
		
		^if($counter == 0){
			^void:sql{INSERT INTO cityblog_rssitems (title,link,description,pubdate)
				VALUES ("$title", "$link", "$description", "^date.sql-string[]")
			}
			
		}{
#			Эта запись уже выкачивалась, апдейтить не надо.		
		}

#		<p><a href="$link">$title</a> (^date.sql-string[])</p>
		^updatedcnt.inc[]
	}

# 	Обновляем запись об этом RSS-фиде в базе:
	^void:sql{UPDATE cityblog_rsslist 
	SET 
		last_update = NOW()
	WHERE
		uri = "$feeduri"
	}
}

Найдено новых записей: $updatedcnt </p>

@display[]

^MAIN:dbconnect{
	$rss_items[
		^table::sql{SELECT
			id,
			title,
			link,
			description,
			DATE_FORMAT(pubdate, "%d/%m/%Y, %H:%i") as date
		FROM
			cityblog_rssitems
		ORDER BY
			pubdate 
		DESC}
	]
}

^if(def $rss_items){
	<ul>
		^rss_items.menu{
			<li><a href="$rss_items.link">$rss_items.title</a> ($rss_items.date GMT)</li>
		}
	</ul>	
}
Файлы классов: dates.p
##############################
# Класс для работы с датами
@CLASS
dates

##############################
# Получаем текущую дату по Гринвичу из нашей локальной:
@load[]

# Нам нужно узнать, на сколько сдвинуть текущую дату и время.
# Сдвиг складывается из разницы по отношению к Гринвичу + поправки на летнее время

$local[^date::now[]]

# Летнее время:

^if($MAIN:global_daylight eq "yes"){

# Попадает ли текущая дата в промежуток, когда вводится летнее время?
	$daylight_lbound[^date::create($local.year;$MAIN:global_daylight_m_start;$MAIN:global_daylight_d_start;0;0;0)]
	$daylight_ubound[^date::create($local.year;$MAIN:global_daylight_m_end;$MAIN:global_daylight_d_end;0;0;0)]

	^if(
		($local.day >= $daylight_lbound.day) 
	&&
		($local.month >= $daylight_lbound.month) 
	&& 
		($local.day <= $daylight_ubound.day) 
	&&
		($local.month <= $daylight_ubound.month) 
	){
#		Текущая дата - в том, диапазоне, в котором летнее время в силе:
		$daylight_offset(1)
	}{
#		...или нет:
		$daylight_offset(0)
	}
}

# Из текущей даты и времени мы должны вычесть час летнего времени и разницу с Гринвичем:
$GMTdate[^date::create($local - (($daylight_offset + $MAIN:global_timeoffset)/24))]

##############################
# Получаем текущую дату (системную):
@local_datetime[]
$result[^local.sql-string[]]


##############################
# Получаем текущую дату (приведённую к Гринвичу):
@GMT_datetime[]
$result[^GMTdate.sql-string[]]

##############################
# Приводим дату в виде строки ("Mon, 29 Sep 2003 11:23:12 GMT") к Гринвичу:
@string2GMT[string]

^string.match[([A-Za-z]{3}), ([0-9]+) ([A-Za-z]{3}) ([0-9]+) ([0-9]+):([0-9]+):([0-9]+) ([A-Za-z]{3,4})][g]{
	$year($match.4)
	$month[$match.3]

	^if($month eq Jan){$month(1)}
	^if($month eq Feb){$month(2)}
	^if($month eq Mar){$month(3)}
	^if($month eq Apr){$month(4)}
	^if($month eq May){$month(5)}
	^if($month eq Jun){$month(6)}
	^if($month eq Jul){$month(7)}
	^if($month eq Aug){$month(8)}
	^if($month eq Sep){$month(9)}
	^if($month eq Oct){$month(10)}
	^if($month eq Nov){$month(11)}
	^if($month eq Dec){$month(12)}

	$day($match.2)
	$hour($match.5)
	$minute($match.6)
	$second($match.7)
	$timezonename[$match.8]
}

#	Табличка с названиями временных зон и поправками:
#	Название 	Аббр.	Поправка
#	Samoa Standard Time	SST	-11
#	Hawaii-Aleutian Standard Time  	HST	-10
#	Alaska Standard Time	AKST	-9
#	Hawaii-Aleutian Daylight Time  	HDT	-9
#	Alaska Daylight Time	AKDT	-8
#	Pacific Standard Time	PST	-8
#	Mountain Standard Time	MST	-7
#	Pacific Daylight Time	PDT	-7
#	Central Standard Time	CST	-6
#	Mountain Daylight Time	MDT	-6
#	Central Daylight Time	CDT	-5
#	Eastern Standard Time	EST	-5
#	Atlantic Standard Time	AST	-4
#	Eastern Daylight Time	EDT	-4
#	Atlantic Daylight Time	ADT	-3
#	Greenwich Mean Time	GMT	0
#	Western Europe Time	WET	0
#	British Summer Time	BST	1
#	Central Europe Time	CET	1
#	Irish Summer Time	IST	1
#	Western Europe Summer Time	WEST	1
#	Central Europe Summer Time	CEST	2
#	Eastern Europe Time	EET	2
#	Eastern Europe Summer Time	EEST	3
#	Moscow Time	MSK	3
#	Moscow Summer Time	MSD	4
#	Western Standard Time	WST	8
#	Central Standard Time	CST	9.5
#	Eastern Standard Time	EST	10

^if(def $year && def $month && def $day){
	^switch[$timezonename]{
		^case[SST]{$tz_offset(-11)}
		^case[HST]{$tz_offset(-10)}
		^case[AKST]{$tz_offset(-9)}
		^case[HDT]{$tz_offset(-9)}
		^case[AKDT]{$tz_offset(-8)}
		^case[PST]{$tz_offset(-8)}
		^case[MST]{$tz_offset(-7)}
		^case[PDT]{$tz_offset(-7)}
		^case[CST]{$tz_offset(-6)}
		^case[MDT]{$tz_offset(-6)}
		^case[CDT]{$tz_offset(-5)}
		^case[EST]{$tz_offset(-5)}
		^case[AST]{$tz_offset(-4)}
		^case[EDT]{$tz_offset(-4)}
		^case[ADT]{$tz_offset(-3)}
		^case[GMT]{$tz_offset(0)}
		^case[WET]{$tz_offset(0)}
		^case[BST]{$tz_offset(1)}
		^case[CET]{$tz_offset(1)}
		^case[IST]{$tz_offset(1)}
		^case[WEST]{$tz_offset(1)}
		^case[CEST]{$tz_offset(2)}
		^case[EET]{$tz_offset(2)}
		^case[EEST]{$tz_offset(3)}
		^case[MSK]{$tz_offset(3)}
		^case[MSD]{$tz_offset(4)}
		^case[WST]{$tz_offset(8)}
		^case[CST]{$tz_offset(9.5)}
		^case[EST]{$tz_offset(10)}
		^case[DEFAULT]{$tz_offset(0)}
	}
	$UnadjustedString[^date::create($year;$month;$day;$hour;$minute;$second)]
	$GMTString[^date::create($UnadjustedString - ($tz_offset/24))]
}{
	$GMTString[^date::now[]]
}

$result[^GMTString.sql-string[]]