ウェブサイトの更新情報を手軽に知ることのできるRSSというものがあります。
ブログを中心とした様々なサイトでこのRSSを取得できるようになっていて、自分が見たいサイトを集めて一覧表示させると更新の有無を効率よく知ることができて便利です。
このアメブロでも読者登録すれば「マイページ」の「チェックリスト」に更新情報を一覧表示させることができることはご存知のことと思います。
これに似たようなことをPHPでやってみようと思います。
まず押さえておくポイントは、ブログ等で取得できる更新情報が書かれたファイルにはいろいろなタイプがあるということです。
調べたところ「RSS1.0」「RSS2.0」「Atom0.3」「Atom0.9」「Atom1.0」などがあるようですが、これらはそれぞれフォーマットが違います。
とりあえずこの5種に対応したものを作ればほとんどまかなえそうなので早速作ってみます。
RSSはHTMLのようなタグ(XML系のマークアップ言語)で構造化された単なるテキストファイルなので正規表現で抜き出すのは簡単です。
PHPにもXML関係の関数がいろいろ用意されていますがどれを使ったらいいかよくわかりませんでした。試しに使ってみたら日付が取得されなかったのでpregを使って正規表現で切り出すことにします。
■ソースコード(rssreader.php)
// RSSアドレス設定他
$rss_url= array(
"http://www.example.com/rss/rss10.rdf",
"http://www.example.com/rss/rss20.xml",
"http://www.example.com/atom/atom09.xml",
"http://www.example.com/atom/atom10.xml",
);
$interval= 10; // RSSを読みにいく間隔 単位[分]
$jcode= "SJIS"; // 掲載サイトの文字コードに合わせる
$max_text= 20; // 表示させる文字数(2連続の半角文字は1文字扱い)
$tempfile= "rssdata.tmp";
$width= 300; // 単位[px]
$bgcolor1= "white";
$bgcolor2= "#fff0c0";
// RSSキャッシュ読み込み
$cache= array();
if (file_exists($tempfile) and $fp= fopen($tempfile, "rb")){
while (!feof($fp)){
$tmp= rtrim(fgets($fp));
if (empty($tmp)) continue;
list($atime, $rss, $sitename, $title, $link, $date)= explode("<>", $tmp, 6);
if (array_search($rss, $rss_url) === false) continue;
$cache[ 'ATIME'][$rss]= $atime;
$cache[ 'RSS'][$rss]= $rss;
$cache['SITENAME'][$rss]= $sitename;
$cache[ 'TITLE'][$rss]= $title;
$cache[ 'LINK'][$rss]= $link;
$cache[ 'DATE'][$rss]= $date;
}
fclose($fp);
}
// RSSキャッシュ期限切れまたはキャッシュのないRSSを読みに行く
$interval*= 60;
$now= time();
foreach ($rss_url as $rss){
// キャッシュ期限内ならスキップ
if (isset($cache['ATIME'][$rss]) && $cache['ATIME'][$rss] + $interval > $now) continue;
// 読みに行く
$xml= request($rss);
// 切り分け
list($sitename, $title, $link, $date)= _xml_parse($xml);
// セット
$cache[ 'ATIME'][$rss]= $now;
$cache[ 'RSS'][$rss]= $rss;
$cache['SITENAME'][$rss]= entity($sitename);
$cache[ 'TITLE'][$rss]= entity($title);
$cache[ 'LINK'][$rss]= $link;
$cache[ 'DATE'][$rss]= $date;
}
// 表作成
$view= "<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" style=\"border:1px solid black;width:{$width}px\">";
arsort($cache['DATE']);
$i= 0;
foreach ($cache['DATE'] as $rss=>$date){
// データ取得失敗?ならスキップ
if (empty($date)) continue;
// 背景色を変える
$bgcolor= ++$i % 2 ? $bgcolor2 : $bgcolor1;
// 冗長なタイトルなら詰める
$sitename= preg_replace("/((?:[\x81-\x9f\xe0-\xfc][\x40-\x7e\x80-\xfc]|(?:(?:&|<|>| |"|&#x?[a-f\d]+;|[\x09\x0a\x0d\x20-\x7e\xa1-\xdf]){1,2})){1,$max_text}).*$/i", '$1', $cache['SITENAME'][$rss]);
if ($sitename !== $cache['SITENAME'][$rss]) $sitename.= "...";
$view.= "<tr><td nowrap style=\"text-indent:6px;background-color:{$bgcolor};border-bottom:1px dashed black;font-size:14px;text-align:left\"><a href=\"{$cache['LINK'][$rss]}\" title=\"{$cache['TITLE'][$rss]}\" style=\"display:block;text-decoration:none\" target=\"_blank\">{$sitename}</a></td><td nowrap style=\"background-color:{$bgcolor};border-bottom:1px dashed black;font-size:12px\">(" . date("n/j G:i", $date) . ")</td></tr>";
}
$view.= "</table>";
// JavaScriptコードを返す
header("Content-Type: text/javascript");
$view= str_replace('"', '\"', mb_convert_encoding($view, jcode($jcode), "SJIS-win"));
echo <<<EOT
document.open();
document.write("{$view}");
document.close();
EOT;
// RSSキャッシュ保存
if ($fp= fopen($tempfile . ".tmp", "wb")){
foreach ($cache['ATIME'] as $rss=>$atime)
fputs($fp, implode('<>',
array($atime, $rss,
$cache['SITENAME'][$rss],
$cache[ 'TITLE'][$rss],
$cache[ 'LINK'][$rss],
$cache[ 'DATE'][$rss],
)) . "\n");
fclose($fp) and rename($tempfile . ".tmp", $tempfile);
}
function entity($s)
{
return strtr($s, array("<"=>"<", ">"=>">", '"'=>"""));
}
function jcode($s)
{
if (preg_match('/^utf-?8/i', $s)) return "UTF-8";
else if (preg_match('/^(?:x-)?euc[_\-]?(?:jp)?/i', $s)) return "CP51932";
else if (preg_match('/^(?:jis|iso-2022)/i', $s)) return "JIS";
else return "SJIS-win";
}
function request($url)
{
$_url= parse_url($url); empty($_url['port']) and $_url['port']= 80;
$nl= "\x0d\x0a";
$res= '';
empty($_url['query']) or $_url['path'].= '?' . $_url['query'];
if (!$fp=fsockopen($_url['host'], $_url['port'], $errno, $errstr, 10)) return '';
$senddata= "GET {$_url['path']} HTTP/1.1{$nl}";
$senddata.="User-Agent: RSS-READER/" . phpversion() . $nl;
$senddata.="Host: {$_url['host']}{$nl}";
$senddata.="Referer: http://" . $_url['host'] . $nl;
$senddata.="Accept: */*{$nl}";
$senddata.="Accept-Language: ja,en-us{$nl}";
$senddata.="Content-Type: application/x-www-form-urlencoded{$nl}";
$senddata.="Connection: close{$nl}";
$senddata.="{$nl}";
fputs($fp, $senddata);
while (!feof($fp)) $res.= fgets($fp, 4096);
fclose($fp);
$res= mb_convert_encoding($res, "SJIS-win", "SJIS-win,UTF-8,CP51932,JIS");
list(,$res)= explode($nl . $nl, $res, 2);
return $res;
}
function _xml_parse($xml)
{
if (preg_match('/<title>(?:<!\[CDATA\[)?(.*?)(?:\]\]>)?<\/title>.*?(<item\b.*?<\/item>)/is', $xml, $match)){
$sitename= $match[1];
preg_match('/<title>(?:<!\[CDATA\[)?(.*?)(?:\]\]>)?<\/title>/is', $match[2], $submatch);
$title= $submatch[1];
preg_match('/<link>.*?(http[^\]<"\s]+)/is', $match[2], $submatch);
$link= $submatch[1];
preg_match('/(?:<dc:date>|<pubDate>)(.*?)</is', $match[2], $submatch);
$date= strtotime($submatch[1]);
} else if (preg_match('/<title>(.*?)<\/title>.*?(<entry\b.*?<\/entry>)/is', $xml, $match)){
$sitename= $match[1];
preg_match('/<title>(.*?)<\/title>/is', $match[2], $submatch);
$title= $submatch[1];
preg_match('/<link\b[^>]*?href="?(http[^"\s>]+)/is', $match[2], $submatch);
$link= $submatch[1];
preg_match('/(?:<modified>|<updated>)(.*?)</is', $match[2], $submatch);
$date= strtotime($submatch[1]);
} else return array();
return array($sitename, $title, $link, $date);
}
?>
■PHPスクリプトの呼び出し(index.html)
<script type="text/javascript" src="rssreader.php"></script>
</body>
試しに「アメブロ
」「FC2
」「ココログ
」「Yahoo
」「goo
」「ヤプログ
」「Livodoor
」「JUGEM
」「エキサイト
」「SeeSaa
」の目ぼしい有名人ブログのRSS情報を読みに行くテストをおこなってみました。
ヤプログのRSSだけ補正が必要だったので正規表現部分を書き換える必要がありました。
一応これでできあがりです。
デザインは苦手なのでこれが限界です。
見栄えは悪いですが、一応動作したということで・・