忍者ブログ
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。



2024/11/22 20:51 |
PHP1-17:RSSリーダを作ってみる

今回使用したバージョン
magpierss-0.72

昔からホームページの更新のチェックに対する需要は高かったのですが、昔の巡回ソフトは更新のあったページを丸ごとダウンロードするものが主流でした。
ファイルの更新日時の取得は比較的容易なのに対し、更新内容の取得は非常に難しかったからです。
2ちゃんねるブラウザを使用している人はわかると思いますが、2ちゃんねるブラウザは2ちゃんねる専用で、他の掲示板を読むことは出来ません。
ほとんど共通のシステムを使用しているしたらばBBS等は読むことが出来る可能性もありますが、基本的には非対応です。
プログラムはアバウトな解析が心底苦手で、人間の目には殆ど同じにしか映らないソースもプログラムには全然別物に見えるので、個別に対応させないと理解してくれないのです。
掲示板だけでもそのような状態ですから、まるで違う構造の各ホームページやブログの内容を取得するなどほとんど夢物語でした。

そのような状況に対処するため、多くのサイトで共通して使えるフォーマットとしてRSSが発展してきました。
このブログもそうですが、多くの企業サイトやブログではRSSが標準で用意されています。
これを読み取るだけで簡単に更新状況がわかるので便利です。
XML形式で書かれているのでパースもさほど難しいものではありません。
というわけでとりあえずRSSを取得してみることにしましょう。

ただ、やはりというかなんというかRSSも拡張の度に互換性が失われており、現在ではRSS0.91、RSS1.0、RSS2.0という三権分立状態になっています。
一々バージョンをチェックしてそれに対応するパーサを用意して、なんてことは面倒なので既存ライブラリで解決してしまいましょう。

PearのRSSリーダにはXML_RSSXML_Feed_Parserがありますが、PearではないMagpieRSSというライブラリがキャッシュ機能もついていて高性能だったりするのでそちらを使用してみます。

Pearではありませんが、とりあえずPearのフォルダに丸ごと突っ込んでおきます。
色々ファイルがありますが、必要なのはrss_*.incの4ファイルとextlibフォルダだけなのであとは削除しても構いません。
あとキャッシュを使用するためにcacheフォルダを作成しておきます。

とりあえずはMagpieRSSを読み込むクラスを作成。

rssreader.class.php 

<?php

    //初期設定
    define('PEAR_MAGPIERSS_DIR',$_SERVER['DOCUMENT_ROOT'].'/src/php/pear/MagpieRSS/');
    define('MAGPIE_OUTPUT_ENCODING', 'UTF-8');
    define('MAGPIE_CACHE_DIR',PEAR_MAGPIERSS_DIR.'cache/');
    define('MAGPIE_CACHE_AGE',3600);

    require_once(PEAR_MAGPIERSS_DIR.'rss_fetch.inc');

class pearMagpieRSSModel {

    //メンバ変数
    private $rss_array=array();
    
    //コンストラクタ
    public function __construct(){}
   
    //RSS取得
    public function setUrl($rss){
        $this->rss_array[$rss['name']] = fetch_rss($rss['url']);
            if(isset($this->rss_array[$rss['name']]->channel['dc']['date'])){
                $this->rss_array[$rss['name']]->channel['dc']['timestamp']=parse_w3cdtf($this->rss_array[$rss['name']]->channel['dc']['date']);
            }else{
                $this->rss_array[$rss['name']]->channel['dc']['timestamp']='';
            }
        return true;
    }

    //RSS情報
    public function getRssChannel($rss){
        if($this->rss_array[$rss['name']]){
            return $this->rss_array[$rss['name']]->channel;
        }else{
            return false;
        }
    }
    
    //各記事の内容
    public function getAllItem($rss){
        if($this->rss_array[$rss['name']]){
            foreach($this->rss_array[$rss['name']]->items as $key=>$val){
                $ret[$key]['title']            =$val['title'];
                $ret[$key]['link']            =$val['link'];
                $ret[$key]['summary']        =$val['summary'];
                $ret[$key]['desc']            =$val['description'];
                if(isset($val['date_timestamp'])){$ret[$key]['date_timestamp']=$val['date_timestamp'];}
            }
            return $ret;            
        }else{
            return false;
        }
    }

#↓クラスのおわり
}


引数として$rss=array('name'=>'弱小PHPerの憂鬱',url='http://yuubiseiharukana.blog.shinobi.jp/RSS/200/')
というような配列を渡すことになっています。
何も考えずに作っていたらメンバ変数のキーとして日本語を入れてしまうという相当気持ち悪い実装になってしまったのですが、まあ気にしない。

最初のdefineはMagpieRSSの動作を決定しています。
最初のPEAR_MAGPIERSS_DIRは自作の定数で、MagpieRSSの位置を指定しています。
残りはMagpieRSS内で使用されるもので、MAGPIE_OUTPUT_ENCODINGで文字コードを、MAGPIE_CACHE_DIRでキャッシュファイルの保存先を、MAGPIE_CACHE_AGEでキャッシュの保存期間を秒で指定します。
キャッシュを設定しないとリロードのたびにRSSを呼びにいって負担になるので必ず設定するようにしましょう。
そんな頻繁に更新しても仕方がないのでMAGPIE_CACHE_DIR=3600秒、一時間に設定しました。
定数になってるせいで、サイトAではキャッシュ1時間、サイトBではキャッシュ30分、みたいなことが出来ないのですが、そんな細かいことはどうでもいいや。

日付に関してはRSSのもっとも厄介なところのひとつで、バージョンによってフィールドがあったりなかったり、実装によってフィールドがあったりなかったりでもう大変です。
また、日付形式はW3CDTFと呼ばれる形式で、「2003-12-13T18:30:02Z」というような形になっていてPHPでは理解しにくいです。
parse_w3cdtfはMagpieRSSに用意されている関数で、タイムスタンプ形式にパースしてくれます。

さてとりあえず動作確認してみます。
引数が妙なことになっているのは動的取得を考えてのことですが、とりあえずは固定で。

rssreader.php

<?php

    #テスト用に固定値を与える
    $_REQUEST['key']=1;
    $rsslist[1]=array('name'=>'日経トレンディ','url'=>'http://trendy.nikkeibp.co.jp/rss/trendy.rdf');
    
    #初期設定
    //require_once('./rsslist.php');
    require_once('./rssreader.class.php');
    $rss = new pearMagpieRSSModel();

    #引数チェック
    if(empty($_REQUEST['key']))                {die();}
    if(empty($rsslist[$_REQUEST['key']]))    {die();}
    $rsslist=$rsslist[$_REQUEST['key']];

    #RSSをセット
    $ret=$rss->setUrl($rsslist);
    if(!$ret){die('rss->setUrl');}

    #チャンネル情報
    $channel=$rss->getRssChannel($rsslist);

    #内容
    $data=$rss->getAllItem($rsslist);

    #表示
    foreach($data as $key=>$val){
        print('<div class="title"><a href="'.$val['link'].'" target="_blank">'.$val['title'].'</a></div>');
        print('<div class="summary">'.$val['summary'].'</div>');
    }


さて実行。

Warning: gmmktime() expects parameter 3 to be long, string given in C:\xampp\htdocs\src\php\pear\MagpieRSS\rss_utils.inc on line 35

rss_utils.incの35行目は
$epoch = gmmktime( $hours, $minutes, $seconds, $month, $day, $year);
日付の生成に失敗しています。
上を見てみると、preg_matchでマッチしているのですが、
$pat = "/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(:(\d{2}))?(?:([-+])(\d{2}):?(\d{2})|(Z))?/";

これ間違ってない?

デバッグ用にprint("<pre>");var_dump( $hours, $minutes, $seconds, $month, $day, $year);を入れてみると、

string(2) "13"
string(2) "02"
string(3) ":45"
string(2) "08"
string(2) "19"
string(4) "2008"

$secondsがおかしい。

正しくは
$pat = "/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})?(?:([-+])(\d{2}):?(\d{2})|(Z))?/";
だと思うのだがこの余計な()は何なのだろう?


とりあえず
array( $match[1], $match[2], $match[3], $match[4], $match[5], $match[6]);
となっているところを
array( $match[1], $match[2], $match[3], $match[4], $match[5], $match[7]);
として応急処置したところ事なきを得た。

とりあえず完成はしたものの、直に呼び出すだけというのも味気ないので次回はAjaxで呼び出してみようかと。

PR


2008/08/20 19:02 | Comments(0) | TrackBack() | PHP

トラックバック

トラックバックURL:

コメント

コメントを投稿する






Vodafone絵文字 i-mode絵文字 Ezweb絵文字 (絵文字)



<<PHP1-18:AjaxでRSSリーダ | HOME | PHP1-16:Pear::MDB2/プリペアードステートメント>>
忍者ブログ[PR]