話題にしたの結構前ですが、公開するのすっかり忘れていました;;
というわけでソースを公開いたします。
基本的な部分は気ままアンテナ☆ミヾ(*´∇`)σと同じですが、
HTML部分は必要最低限のみを記載してシンプルにしています。
配布・解説等は試験的にConcrete5で構築したサイトで行っていますのでこちらをご覧ください。
ご質問等ありましたらコメント等でお願いします。
あんてなサイトサンプルダウンロード先:徒然なるままに
話題にしたの結構前ですが、公開するのすっかり忘れていました;;
というわけでソースを公開いたします。
基本的な部分は気ままアンテナ☆ミヾ(*´∇`)σと同じですが、
HTML部分は必要最低限のみを記載してシンプルにしています。
配布・解説等は試験的にConcrete5で構築したサイトで行っていますのでこちらをご覧ください。
ご質問等ありましたらコメント等でお願いします。
あんてなサイトサンプルダウンロード先:徒然なるままに
前回の公開したrssLoader
実はRSS2.0だとitemタグを解析できないというバグがある
どこが悪くて読み込めないかというと以下の部分
// 各記事を取得する
foreach($rssDom->item as $item) {
$itemBean = new itemBean();
「RSSのフォーマット・仕様・構造 – RSS1.0、RSS2.0、Content-Type」を見ていただくと分かるが
RSS1.0ではitemタグはchannelタグと同列のDom階層に存在するが
RSS2.0ではitemタグはchannelタグ配下のDom階層にある
RSS2.0を読み込むには「$rssDom->channel->item」としなければならないのだ
修正したソースは以下のようになる
<?php
require_once __DIR__."/channelBean.php";
require_once __DIR__."/itemBean.php";
require_once __DIR__."/rssException.php";
require_once __DIR__."/lib/httpConnecter.php";
require_once __DIR__."/lib/xmlLoader.php";
class rssLoader {
const VERSION_1 = 1;
const VERSION_2 = 2;
const VERSION_ALL = 0;
public function __construct() {
}
public function __destruct() {
}
/**
* RSSを読み込みchannelBeanで取得
*
* @param int $version 対象RSSバージョン指定
* 全バージョンのRSS : rssLoader::VERSION_ALL
* バージョン1.0のRSS : rssLoader::VERSION_1
* バージョン2.0のRSS : rssLoader::VERSION_2
*
*/
public function loadRss($path, $version = self::VERSION_ALL) {
/**
* RSSを取得
*/
$http = new httpConnecter();
$http->connect($path);
$rssStr = $http->loadText();
if($rssStr === "") {
// 取得できなければエラー
throw rssException::notRss("RSSファイルの取得ができませんでした。");
}
/**
* RSSを解析
*/
$xml = new xmlLoader();
$rssDom = $xml->parsor($rssStr);
// すべてのバージョン
if($version == self::VERSION_ALL) {
try {
return $this->loadRss2($rssDom);
} catch (Exception $e) {
return $this->loadRss1($rssDom);
}
}
// rss1.0
else if(isset($rssDom->attributes()->version) == false && $version == self::VERSION_1) {
return $this->loadRss1($rssDom);
}
// rss2.0
else if($rssDom->attributes()->version == 2 && $version == self::VERSION_2) {
return $this->loadRss2($rssDom);
}
else {
throw rssException::notMatchVersion("このRSSは正しい型式ではありません。");
}
return $channelBean;
}
/**
* RSS1.0を解析
* @param unknown $rssDom
*/
private function loadRss1($rssDom) {
//if($rssDom->attributes()->version != self::VERSION_1) {
// throw rssException::notMatchVersion("このRSSはVertion1.0ではありません。");
//}
$channelBean = new channelBean();
$channelBean->encoding = "UTF-8";
$channelBean->rssVersion = self::VERSION_1;
$channelBean->rssUrl = $rssDom->channel->link;
// RSSの本体情報を取得
if(isset($rssDom->channel->title)) {
$channelBean->title = $rssDom->channel->title;
}
if(isset($rssDom->channel->link)) {
$channelBean->link = $rssDom->channel->link;
}
if(isset($rssDom->channel->description)) {
$channelBean->description = $rssDom->channel->description;
}
// dcの要素を取得
$dc = $rssDom->channel->children('http://purl.org/dc/elements/1.1/');
if(isset($dc->date)) {
$channelBean->date = $dc->date;
}
if(isset($dc->language)) {
$channelBean->language = $dc->language;
}
if(isset($dc->creator)) {
$channelBean->creator = $dc->creator;
}
// ver1.0各記事を取得する
foreach($rssDom->item as $item) {
$itemBean = new itemBean();
if(isset($item->title)) {
$itemBean->title = $item->title;
}
if(isset($item->link)) {
$itemBean->link = $item->link;
}
if(isset($item->description)) {
$itemBean->description = $item->description;
}
if(isset($item->category)) {
$itemBean->category = $item->category;
}
if(isset($item->pubDate)) {
$itemBean->date = $item->date;
}
// dcの要素を取得
$dc = $item->children('http://purl.org/dc/elements/1.1/');
if(isset($dc->date)) {
$itemBean->date = $dc->date;
}
if(isset($dc->subject)) {
$itemBean->category = $dc->subject;
}
if(isset($dc->creator)) {
$itemBean->creator = $dc->creator;
}
array_push($channelBean->itemList, $itemBean);
}
return $channelBean;
}
/**
* RSS2.0を解析
* @param unknown $rssDom
*/
private function loadRss2($rssDom) {
if($rssDom->attributes()->version != self::VERSION_2) {
throw rssException::notMatchVersion("このRSSはVertion2.0ではありません。");
}
$channelBean = new channelBean();
$channelBean->encoding = "UTF-8";
$channelBean->rssVersion = self::VERSION_2;
$channelBean->rssUrl = $rssDom->channel->link;
// RSSの本体情報を取得
if(isset($rssDom->channel->title)) {
$channelBean->title = $rssDom->channel->title;
}
if(isset($rssDom->channel->link)) {
$channelBean->link = $rssDom->channel->link;
}
if(isset($rssDom->channel->description)) {
$channelBean->description = $rssDom->channel->description;
}
// dcの要素を取得
$dc = $rssDom->channel->children('http://purl.org/dc/elements/1.1/');
if(isset($dc->date)) {
$channelBean->date = $dc->date;
}
if(isset($dc->language)) {
$channelBean->language = $dc->language;
}
if(isset($dc->creator)) {
$channelBean->creator = $dc->creator;
}
// ver2.0各記事を取得する
foreach($rssDom->channel->item as $item) {
$itemBean = new itemBean();
if(isset($item->title)) {
$itemBean->title = $item->title;
}
if(isset($item->link)) {
$itemBean->link = $item->link;
}
if(isset($item->description)) {
$itemBean->description = $item->description;
}
if(isset($item->category)) {
$itemBean->category = $item->category;
}
if(isset($item->pubDate)) {
$itemBean->date = $item->date;
}
// dcの要素を取得
$dc = $item->children('http://purl.org/dc/elements/1.1/');
if(isset($dc->date)) {
$itemBean->date = $dc->date;
}
if(isset($dc->subject)) {
$itemBean->category = $dc->subject;
}
if(isset($dc->creator)) {
$itemBean->creator = $dc->creator;
}
array_push($channelBean->itemList, $itemBean);
}
return $channelBean;
}
}
上記ソースはほかにもネームスペース「dc」の要素が読み込めていない問題などを修正している
ダウンロードはこちらから→[download id=”1″]
アンテナサイト作成の模様はまだ途中ですが
アンテナサイト自体は完成しているため公開します。
気ままアンテナ☆ミヾ(*´∇`)σ←今回作成したアンテナサイト

前回の構成のためにRSSをダウンロードしてくる処理を作成してみます。
rssDownload.phpが本体でsite.csvから対象URLを取得してrssDataフォルダーにファイルを保存します。
以下がソースです。
<?php
define("RSS_DIR", "../rssData/"); // RSSデータのダウンロード先
define("CSV_FILE", "site.csv"); // RSS配信サイトのURLリスト
define("CRLF", "\r\n");
require_once __DIR__.'/rssLoader/rssLoader.php';
/**
* CSVファイルから対象のRSSURL一覧を取得
*/
echo "load site csv start\n";
// CSVファイルをロード
$siteList = file(CSV_FILE, FILE_IGNORE_NEW_LINES);
if($siteList === false) {
echo "not found ".CSV_FILE."\n";
return;
}
echo "load site csv end\n";
/**
* 各RSSダウンロード
*/
echo "download rss start\n";
$http = new httpConnecter();
try {
$count = 0;
foreach($siteList as $url) {
echo "download url:".$url."\n";
// 接続
$http->connect($url);
// 保存先ファイルオープン
$filePath = sprintf("%s/%03d.rss", RSS_DIR, $count);
$file = fopen($filePath, "wb");
// ストリームでデータをロードし、ストリームで書き込む
$buffer = null;
do {
// webからデータを取得する
$buffer = $http->loadBinnaryStream(4086);
// ファイルにデータを書き込む
fwrite($file, $buffer);
echo ".";
} while(!is_null($buffer));
echo "\n";
// ファイルをクローズ
fclose($file);
$count++;
}
} catch(Exception $e) {
echo $e->getMessage()."\n";
}
echo "download rss end\n";
httpConnecterはrssLoaderが利用しているものを流用しています。
ぶっちゃけこれ使わなくてもいいんですけどね・・
これをcronで定期的回せば定期的にRSSを取得できます。
ソースのダウンロードはこちらから:[download id=”2″]
前回、RSSをロードして情報を取得するクラスを作成した。
後はこのクラスを使って複数のRSS配信サイトからRSSを読み込んで表示するだけなのだが 、
ここで1つ注意すべき点がある。
それはアンテナサイトにアクセスがあるたび毎回RSS配信サイトにアクセスするような構成にしないことだ。
以下の図を見てほしい

この用にアクセス数が増えればその分RSS配信サイトにも同じだけのトラフィック増加が起きることになる。
そしてサイトの表示時間も遅くなることが容易に想像だろう。
まあ、こんな注意いまさらって人が多いだろうが・・
だいたいの人は以下のように、RSSを取得するバッチと表示用の2つのプログラムを作成することになるだろう
もしくはRSSを取得後静的なHTMLを吐き出すようにするって人もいるでしょう
どちらも取得は一定時間ごとに行うのでRSS配信サイト側に係る負荷はアンテナサイトへのアクセス数が増えても増加しません。
また表示時間も 静的HTML > 表示用プログラム の順で早くなります。
今回は取得用と表示用に2つのプログラムを作成する方法を使って作成しようと思います。
では、次回に
ブログやニュースなどの更新情報を配信するために用いられるRSS
これを利用して複数のサイトの情報を取得表示しているのがアンテナサイトです。
今回ふと思い立ってこのアンテナサイトを作ることにしたので数回に分けて作成までの道のりをつらつらとつづって行こうと思います。
まずはRSSを解析する仕組みが必要なのですがググって見つかるソースは拡張モジュールのcurlを利用しているものばかり
基本的に拡張モジュールは使いたくないので自力で作ってみることにした。
・rssLoader.php
<?php
require_once __DIR__."/channelBean.php";
require_once __DIR__."/itemBean.php";
require_once __DIR__."/rssException.php";
require_once __DIR__."/lib/httpConnecter.php";
require_once __DIR__."/lib/xmlLoader.php";
class rssLoader {
const VERSION_1 = 1;
const VERSION_2 = 2;
const VERSION_ALL = 0;
public function __construct() {
}
public function __destruct() {
}
/**
* RSSを読み込みchannelBeanで取得
*
* @param int $version 対象RSSバージョン指定
* 全バージョンのRSS : rssLoader::VERSION_ALL
* バージョン1.0のRSS : rssLoader::VERSION_1
* バージョン2.0のRSS : rssLoader::VERSION_2
*
*/
public function loadRss($path, $version = self::VERSION_ALL) {
/**
* RSSを取得
*/
$http = new httpConnecter();
$http->connect($path);
$rssStr = $http->loadText();
if($rssStr === "") {
// 取得できなければエラー
throw rssException::notRss("RSSファイルの取得ができませんでした。");
}
/**
* RSSを解析
*/
$xml = new xmlLoader();
$rssDom = $xml->parsor($rssStr);
$channelBean = new channelBean();
$channelBean->encoding = "UTF-8";
// rss1.0
if(isset($rssDom->attributes()->version) == false) {
$channelBean->rssVersion = self::VERSION_1;
$channelBean->rssUrl = $rssDom->channel->link;
if($version != self::VERSION_1 && $version != self::VERSION_ALL) {
throw rssException::notMatchVersion("このRSSはVertion1.0ではありません。");
}
}
// rss2.0
else if($rssDom->attributes()->version == 2) {
$channelBean->rssVersion = self::VERSION_2;
$channelBean->rssUrl = $rssDom->channel->link;
if($version != self::VERSION_2 && $version != self::VERSION_ALL) {
throw rssException::notMatchVersion("このRSSはVertion2.0ではありません。");
}
}
// RSSの本体情報を取得
if(isset($rssDom->channel->title)) {
$channelBean->title = $rssDom->channel->title;
}
if(isset($rssDom->channel->link)) {
$channelBean->link = $rssDom->channel->link;
}
if(isset($rssDom->channel->description)) {
$channelBean->description = $rssDom->channel->description;
}
// dcの要素を取得
$dc = $rssDom->channel->children('http://purl.org/dc/elements/1.1/');
if(isset($dc->date)) {
$channelBean->date = $dc->date;
}
if(isset($dc->language)) {
$channelBean->language = $dc->language;
}
if(isset($dc->creator)) {
$channelBean->creator = $dc->creator;
}
// 各記事を取得する
foreach($rssDom->item as $item) {
$itemBean = new itemBean();
if(isset($item->title)) {
$itemBean->title = $item->title;
}
if(isset($item->link)) {
$itemBean->link = $item->link;
}
if(isset($item->description)) {
$itemBean->description = $item->description;
}
if(isset($item->category)) {
$itemBean->category = $item->category;
}
if(isset($item->pubDate)) {
$itemBean->date = $item->date;
}
// dcの要素を取得
$dc = $item->children('http://purl.org/dc/elements/1.1/');
if(isset($dc->date)) {
$itemBean->date = $dc->date;
}
if(isset($dc->subject)) {
$itemBean->category = $dc->subject;
}
if(isset($dc->creator)) {
$itemBean->creator = $dc->creator;
}
array_push($channelBean->itemList, $itemBean);
}
return $channelBean;
}
}
rssLoaderで解析するとchannelBeanクラスが返却される。
このchannelBeanクラス内部にitemBeanのリストがありそこに個々の記事のデータがあるといった感じ
itemBeanがchannelBeanの内部にあるのはRSS2.0ではchannelタグの内部にitemタグがあるからといった理由から
使い方は以下
・rssTest.php
<?php
require_once "rssLoader.php";
$rssLoader = new rssLoader();
try {
$channel = $rssLoader->loadRss("http://watch2ch.2chblog.jp/index.rdf");// ネットワーク上のファイルの場合
//$channel = $rssLoader->loadRss("/var/www/html/rss.rdf");// ローカルファイルの場合
} catch (Exception $e) {
echo $e->getMessage();
}
?>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>RSS</title>
</head>
<body>
<h2><?=$channel->title ?></h2>
<table border="1">
<tr>
<th>タイトル一覧</th>
<th>記事</th>
<th>作成日</th>
</tr>
<?php foreach($channel->itemList as $item): ?>
<tr>
<td><?=$item->title ?></td>
<td><?=$item->date ?></td>
</tr>
<?php endforeach; ?>
</table>
</body>
</html>
ソースコードのダウンロードはこちら:[download id=”1″]
これでRSSを読み込んで記事情報を取り出すことはできました。
次回に続きます。
追記
このrssLoaderにはRSS2.0が読み込めないバグがあります。
修正版は後日別の記事にて掲載いたします。