RSSデータの取得とデータの蓄積サンプル

昔（多分今もあるけど）mixiに「mixiニュース」というコンテンツありました。これは単にニュースを見れるだけではなく、そのニュースについてコメント的に日記を書くことができるコンテンツで、自分は一時期これにめっちゃハマってました。

ですがご存知の通り、足あと機能の廃止により「足あと」と言う名のアクセスカウンタ機能が消え失せ、結果大幅にモチベーションが低下してついに書かなくなりました。

とはいえ人間一度味をしめるとなかなか脱却できないもので、何かどうにかしてやれないかなあと考えたところ

「そうだニュースのRSSを解析して自前のデータベースに蓄積しとけばいいんだ」
「確かアレって元記事のURLもあるし見出し見て気に入ったニュースを見て書けばいいんだ」
「ついでにmixiみたく見出しやURLを『引用』（これ大事）とかにしとけば似た感じになるぞ」

という結論に至りこれを実装してみました。


#実装に必要なもの
・レンタルサーバー
・php
・mysql

#テーブル定義

テーブルその１、rssデータをを配信してるサイトを管理するテーブル<pre>
CREATE TABLE rssmaster (
  id int(10) NOT NULL AUTO_INCREMENT ,
  name varchar(255) DEFAULT NULL,
  url varchar(255) DEFAULT NULL,
  dispname varchar(255) DEFAULT NULL,
  rdf varchar(255) DEFAULT NULL,
  status int(1) DEFAULT 1,
  version varchar(10) DEFAULT NULL,
  created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  updated timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (id)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
</pre>

サンプルデータ。<pre>INSERT INTO `rssmaster` (`id`, `name`, `url`, `dispname`, `rdf`, `status`, `version`, `itemlimit`, `created`, `modified`) VALUES (1,'マイナビニュース - その先を伝える総合情報サイト','http://news.mynavi.jp/','MYCOMジャーナル','http://rss.rssad.jp/rss/mycom/index',1,'1.0',0,'2013-09-04 12:20:17','2013-09-04 12:20:17');
INSERT INTO `rssmaster` (`id`, `name`, `url`, `dispname`, `rdf`, `status`, `version`, `itemlimit`, `created`, `modified`) VALUES (2,'マイナビウーマン | エキサイトニュース','http://www.excite.co.jp/News/source/Escala/','マイナビウーマン最新配信記事 | エキサイトニュース','http://www.excite.co.jp/News/rss/source/?source=Escala',1,'1.0',0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `rssmaster` (`id`, `name`, `url`, `dispname`, `rdf`, `status`, `version`, `itemlimit`, `created`, `modified`) VALUES (3,'メンズサイゾー | エキサイトニュース','http://www.excite.co.jp/News/source/Menscyzo/','メンズサイゾー最新配信記事 | エキサイトニュース','http://www.excite.co.jp/News/rss/source/?source=Menscyzo',1,'1.0',0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `rssmaster` (`id`, `name`, `url`, `dispname`, `rdf`, `status`, `version`, `itemlimit`, `created`, `modified`) VALUES (4,'サイゾーウーマン | エキサイトニュース','http://www.excite.co.jp/News/source/Cyzowoman/','サイゾーウーマン最新配信記事 | エキサイトニュース','http://www.excite.co.jp/News/rss/source/?source=Cyzowoman',1,'1.0',0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `rssmaster` (`id`, `name`, `url`, `dispname`, `rdf`, `status`, `version`, `itemlimit`, `created`, `modified`) VALUES (5,'朝日新聞デジタル：朝日新聞社のニュースサイト','http://www.asahi.com/','朝日新聞デジタル速報ニュース','http://rss.asahi.com/rss/asahi/newsheadlines.rdf',0,'1.0',0,'2013-09-14 22:07:09','2013-09-14 22:07:09');
INSERT INTO `rssmaster` (`id`, `name`, `url`, `dispname`, `rdf`, `status`, `version`, `itemlimit`, `created`, `modified`) VALUES (6,'新着ニュース : YOMIURI ONLINE（読売新聞）','http://www.yomiuri.co.jp/latestnews/','YOMIURI ONLINE 新着ニュース','http://rss.yomiuri.co.jp/rss/yol/latestnews30',1,'2.0',0,'2013-09-14 22:09:01','2013-09-14 22:09:01');
</pre>実際には[ここ](http://feeds.japan.cnet.com/rss/cnet/all.rdf)みて手動で取り込んでた。

テーブルその２、記事データを管理するテーブル<pre>
CREATE TABLE rssdata (
  itemkey varchar(32) NOT NULL,
  id int(10) NOT NULL,
  sitetitle text,
  sitedescription text,
  sitelink text,
  itemtitle text,
  itemdescription_body text,
  itemdescription_plane text,
  itemdescription_image text,
  itemdescription_title text,
  itemdescription_comment text,
  to varchar(255) DEFAULT NULL,
  itemlink text,
  status int(1) DEFAULT '1',
  version varchar(10) DEFAULT NULL,
  created timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ,
  modified timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (itemkey),
  KEY id (id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
</pre>

#RSSデータをとってくるプログラム(parserdf.php)
<pre>
&lt;?php
//include("feedcreator.class.php");
mb_internal_encoding("utf-8");	// 内部エンコード指定@文字化け対策
$newslimit = 1; // サイトごとの件数はrssmasterにもたせた
$newscount = 0; 
$dbHost="127.0.0.1";
$dbUser="mysql";
$dbPass="password";
$db_select = mysql_connect($dbHost, $dbUser, $dbPass);
mysql_set_charset("utf8",$db_select);	// DBエンコード指定@文字化け対策
$db_selected = mysql_select_db('rssbbs', $db_select);

$timestamp = date("YmdHis");
System("mysqldump -u root rssbbs rssdata --password=password  &gt; rssdata.$timestamp.txt");
mysql_query("DELETE FROM rssdata WHERE ( status = 0 AND ( itemdescription_comment IS NULL OR itemdescription_comment = '' ))
OR modified &lt; date_add(current_timestamp, interval -7 day) ");

$ret = mysql_query("SELECT * FROM rssmaster WHERE status = 1 ORDER BY id");
while ($array = mysql_fetch_array($ret)) 
{
	$id = $array['id'];
	$newslimit = $array['itemlimit'];
	$version = $array['version'];
	//$result = simplexml_load_file($array['rdf']);
	// 2013.06.29 description中のCDATAをテキストとして取得
	$result = simplexml_load_file($array['rdf'], 'SimpleXMLElement', LIBXML_NOCDATA);
	// 2013.08.27 配列型に変換
	$result = json_decode(json_encode($result), true);

	$sitetitle = NULL;
	$sitedescription = NULL;
	$sitelink = NULL;

	// sitetitle
	if (array_key_exists('title', $result['channel']))
	{
		$sitetitle = $result['channel']['title'];
	}
	else
	{
		$sitetitle = $array['dispname'];
	}

	// sitedescription
	if (array_key_exists('description', $result['channel']) && is_string($result['channel']['description']))
	{
		$sitedescription = $result['channel']['description'];
	}
	else
	{
		$sitedescription = $array['name'];
	}

	// sitelink
	if (array_key_exists('link', $result['channel'])) 
	{
		$sitelink = urlcheck((string)$result['channel']['link'], $array['url']);
	}
	else
	{
		$sitelink = $array['url'];
	}

	$isitems=false;
	$items=array();

	// RSS Version1.0
	if ($version === '1.0')
	{
		if (array_key_exists('item', $result))
		{
			$isitems=true;
			$items=$result['item'];
		}
	}
	// RSS Version2.0
	elseif($version === '2.0')
	{
		if (array_key_exists('item', $result['channel']))
		{
			$isitems=true;
			$items=$result['channel']['item'];
		}
	}
	if ($isitems){
		$newscount = 0;
		foreach($items as $item)
		{

			// newslimit=0は全件
			if ($newslimit != 0){
				if ($newscount &gt;= $newslimit) break;
				$newscount++;
			}

			$itemtitle = NULL;
			$itemlink = NULL;
			$itemkey = NULL;
			$itemdescription_source = NULL;
			$itemdescription_image = NULL;
			$itemdescription_plane = NULL;
			$itemdescription_body = NULL;

			$itemtitle = (array_key_exists('title', $item)) ? $item['title'] : "";
			if (! is_string($itemtitle))
			{
				continue;
			}
			if (substr($itemtitle,0,2) === 'PR' || substr($itemtitle,0,2) == 'AD' || substr($itemtitle,1,2) == 'PR') 
			{
				continue;
			}

			$itemlink = (array_key_exists('link', $item)) ? $item['link'] : "";
			$itemkey = md5($itemlink);	// 記事単位のkey生成
			$itemdescription_source = (array_key_exists('description', $item)) ? $item['description'] : "";
			if (is_string($itemdescription_source)){
				$itemdescription_image = getimage($itemdescription_source);
				$itemdescription_plane = strip_tags($itemdescription_source);
				$itemdescription_plane = str_replace(array("\r\n","\r","\n"), '', $itemdescription_plane);

				$itemdescription_body = str_replace("'","\"",$itemdescription_source);
				$itemdescription_body = str_replace("&lt;p&gt;", "", $itemdescription_body);
				$itemdescription_body = str_replace("&lt;/p&gt;", "", $itemdescription_body);
				$itemdescription_body = str_replace(array("\r\n","\r","\n"), '', $itemdescription_body);
			}

			$check_sql = "SELECT itemkey FROM rssdata WHERE itemkey = '$itemkey'";
			$check_ret = mysql_query($check_sql);
			$check_array = mysql_fetch_array($check_ret);
			if(empty($check_array) || $check_array[0]['itemkey'] === '')
			{

				$replace_sql = "INSERT INTO rssdata 
					(
						id,
						sitetitle,
						sitedescription,
						sitelink,
						itemkey,
						itemtitle,
						itemdescription_body,
						itemdescription_plane,
						itemdescription_image,
						itemlink,
						status,
						version
					)
					VALUES 
					(
						$id,
						'$sitetitle',
						'$sitedescription',
						'$sitelink',
						'$itemkey',
						'$itemtitle',
						'$itemdescription_body',
						'$itemdescription_plane',
						'$itemdescription_image',
						'$itemlink',
						0,
						'$version'
					)";
				$replace_sql = str_replace(array("\r\n","\r","\n", "\t" ), '', $replace_sql);

				mysql_query($replace_sql);

				logging(array(
					'id' =&gt; $id,
					'sitetitle' =&gt; $sitetitle,
					'sitedescription' =&gt; $sitedescription,
					'sitelink' =&gt; $sitelink,
					'itemkey' =&gt; $itemkey,
					'itemtitle' =&gt; $itemtitle,
					'itemdescription_body' =&gt; $itemdescription_body,
					'itemdescription_plane' =&gt; $itemdescription_plane,
					'itemdescription_image' =&gt; $itemdescription_image,
					'itemlink' =&gt; $itemlink,
					'version' =&gt; $version));
			}	
		}
	}
}
mysql_close($db_select);


// ---------------------------------------------------------------
//	functions
// ---------------------------------------------------------------

// url1の形式チェック・存在チェックをする。両方trueならurl1を、falseがあればurl2を返す
function urlcheck($url1,$url2)
{
	$returl = $url1;

	// url形式チェック
	if (preg_match('/^(https?|ftp)(:\/\/[-_.!~*\'()a-zA-Z0-9;\/?:\@&=+\$,%#]+)$/', $url1))
	{
		// URL存在チェック
		if($fp = @fopen($url1, 'r'))
		{
			fclose($fp);
		}
		else
		{
			$returl = $url2;
		}
	}
	else
	{
		$returl = $url2;
	}
	return $returl;
}

// imgタグ抜き出し
function getimage($str)
{
	if (!is_string($str)) return;
	if (preg_match('/(\&lt;img).*(gif|jpg|jpeg|png)(.*?&gt;)/', $str, $match))
	{
		return str_replace("'","\"",$match[0]);
	}
	else
	{
		return "";
	}
}

function logging($array)
{
    $log = NULL;
    $log.= date("Y/m/d H:i:s")." : ";
	$Ymd = date("Ymd");
    foreach($array as $key =&gt; $value)
    {
        if (is_numeric($key))
        {
            continue;
        }
        $log.= "$key=$value,";
    }
    $log = substr($log,0,-1);
	$log.= "\n";
    $logfile = str_replace("php","log",$_SERVER['PHP_SELF']) ;
    error_log($log,3,'/var/log/'.$logfile);
}
?&gt;
</pre>

#使い方
mysqlにテーブル作ってphpのソースにmysqlのアクセス設定してparserdf.phpをcli起動すれば動くはずです。

これだけだと見る方法がないので実運用にはcakePHPにてWebUIを実装しています。
それはまた次回に。