PHPでRSSアグリゲート
RSSを読み込んで表示する
ココログというweblogサービスが@niftyで始まって以来、RSSという単語も今まで以上に多くの人の目に触れるようになってきたと思う。RSSそのものに関する解説はまた次の機会(いつ?)に譲るとして、今回は当フォーラムサイトにて利用している掲示板新着発言のリスト(RSSフィードサービス)を表示する一種のRSSアグリゲータをソースをまじえてご紹介してみよう。
XML パーサ関数を使ってみたい
RSSはXMLであるのでXMLを読み込んで解析するというXMLパーサ機能がないといけない。幸いPHPではXMLを処理するために専用の関数群が用意されているため、これを利用するのが一番の早道であろう。
今回のプログラムを実装するまで私はXMLパーサ関数を触ったことがなかったもので、どうにか使ってはいるものの、まだ改善の余地があるものと思っているので、よいアイディアがあればぜひご指摘願いたい。
そんな前置きはさておき、どんな関数があるかということになるわけであるが、マニュアルがPHPユーザ会にて翻訳されているのでそこをご覧いただきたい。
XMLパーサ関数等は最近のバージョンであればデフォルトでインストールされるはずであるが、自身の環境で利用可能かどうかはphpinfo()関数などで確認いただきたい。
XMLパーサ関数を使用する際には一度XMLファイルをすべてメモリ上にロードするので巨大なXML等を解析する場合、実際に処理が始まるまでに時間がかかるなどの問題がある。ただRSSはサイトサマリのメタデータであるためそれほど巨大なファイルにはならないと想定されるので、積極的にXMLパーサ関数を利用したい。
で、実際のソースを見てみよう
まずはソースをご覧いただこう。このソースはPHP4.2.0以降を意識して書かれているのでそれ以前のバージョンでの運用ではやや注意が必要。
このプログラムはロードされるとRSSを解析し、指定された文字エンコーディングでHTMLページに対してJavaScriptによってリソースへのリンクを設定する。
1: | //PHPの開始 <?php |
---|---|
2: | //テキストノードの値 function characters($parser, $text){ global $g_buffers; //バッファにテキストを保存 $g_buffers = $text; } |
3: | //要素の開始 function startElement($parser, $name, $attrib){ global $g_intCount; if($name == "item"){ $g_intCount = $g_intCount+1; } } |
4: | //要素の終了 function endElement($parser, $name){ global $g_buffers,$g_tit,$g_uri,$g_board,$g_date,$g_intCount; $g_buffers = mb_convert_encoding($g_buffers,"EUC-JP","UTF-8"); switch($name){ case "title": $g_tit[$g_intCount] = $g_buffers; break; case "link": $g_uri[$g_intCount] = $g_buffers; break; case "description": $g_board[$g_intCount] = substr($g_buffers,8); break; case "dc:date": $g_date[$g_intCount] = $g_buffers; break; default : break; } } |
5: | //初期化 $g_intCount = 0; $fileRss = "../../rss/mescomfhpg.rdf"; $enc_cd = $_GET['enc']; |
6: | //XMLパーサを作成 $xml_parser = xml_parser_create(); |
7: | //大文字変換を行わない xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false); |
8: | //Start およびEndのエレメントを指定する xml_set_element_handler($xml_parser, "startElement", "endElement"); |
9: | //文字データハンドラを設定する xml_set_character_data_h]andler($xml_parser, "characters"); |
10: | //ファイルのオープン処理 if(!($fp = fopen($fileRss,'r'))){ die("RSSが開けません!"); } |
11: | //オープンしたファイルを 読み込み解析 while($fileRssdat=fread($fp,8192)){ if(!xml_parse($xml_parser,$fileRssdat,feof($fp))){ return "XML PARSE ERROR"; } } |
12: | //ファイルのクローズ処理 fclose($fp); |
13: | //作成したXMLパーサを解放する xml_parser_free($xml_parser); |
14: | //配列にバッファしたデータを読み出して出力 for($j=1;$j<=$g_intCount;$j++){ $aryTmp = explode("/",$g_uri[$j]); $boarduri = "http://bbs.com.nifty.com/{$aryTmp[3]}/cf_wrent/{$aryTmp[5]}"; $pubdate = substr($g_date[$j],5,2)."/".substr($g_date[$j],8,2)." ".substr($g_date[$j],11,5); $strOutput = "document.write('◆"; $strOutput .= $strOutputd."<a href=\"{$g_uri[$j]}\">"; $strOutput .= "{$g_tit[$j]}</a>"; $strOutput .= "<a href=\"{$boarduri}\">[{$g_board[$j]}]</a>({$pubdate})<br>')\n"; //出力時のエンコーディング処理 switch($enc_cd){ case "utf8": echo mb_convert_encoding($strOutput,"UTF-8","EUC-JP"); break; case "sjis": echo mb_convert_encoding($strOutput,"SJIS","EUC-JP"); break; case "euc": echo $strOutput; break; default: echo mb_convert_encoding($strOutput,"SJIS","EUC-JP"); } } |
15: | //phpの終了 ?> |
簡単な解説というかメモ
- PHP開始タグ記述
お約束。 - テキストノード作成
- 要素の開始関数
ここで今回処理したいのはRSS中の各記事であるので、記事の各種情報を持つitem要素をひとつの抽出単位とする。この処理により入れ子構造上<item>~</item>の間をひとつの塊として扱うことができる。 - 要素の終了関数
<item>~</item>の中に含まれる各要素を抽出する。それぞれを項目別に配列に格納し、同一配列番号どうしを利用することでひとつのitemが完成される形となる。
対象ファイルはRSS1.0 + DoublinCoreを使用しているため、それに応じて<title>、<link>、<description>、<dc:date>の各要素を抽出して変数に格納。 - 初期化
メインのプロシージャはここから開始。
読み込み対象のファイル名、カウンタの初期値、エンコーディング用の引数情報などを初期化。 - XMLパーサを作成
引数として使用するエンコードィングを指定できるが、デフォルトはUTF-8。RSSはUTF-8で記述されているので今回は省略。 - 大文字変換未指定
XMLパーサのオプション設定。デフォルトでは大文字変換するので明示的に変換を行わないことを宣言。なんで使ってたんだっけ(^^;?<おいおい - 開始終了エレメントを指定
xml_set_element_handler関数を使用して今回のRSS解析に使用する関数を指定。3;4;で指定した各関数に基づいて、今回はXMLの解析処理を行う。 - 文字データハンドラ設定
- RSSファイルオープン
初期化にて設定した該当ファイルをオープン。 - オープンしたファイルを変数に格納
短い処理だが、ここでRSSファイルを読み込んで、事前に3:,4:で定義された関数によって配列にデータが展開されている。 - RSSファイルのクローズ
いつまでもファイルをオープンしているわけにはいかないのでここで解放。 - 作成したXMLパーサを解放
いつまでもXMLパーサを保持しているわけにはいかないのでここで解放。すでに必要な情報はすべて変数に展開完了しているのでOK。 - 変数に格納したデータを順番に読み出して出力
あらかじめひとつのitem要素の中身は同一の配列引数で格納されているので、出力したい順番を指定して順次出力用変数に格納。今回のPHPはJavaScriptのソースファイルとしてHTMLから呼び出される前提なので、出力内容はJavaScriptソース。
最終的に格納された出力用変数を、指定された文字コーディングでエンコードして出力。 - PHPの終了
お約束。
既知のバグとか
残念ながら現在いくつかのバグが存在している(^^;
解決策があればどなたかぜひご教授願いたい。
- 出力される文字が一部不完全
おそらくRSSを一括読み込みをしているのだが、指定しているメモリ量が不足してデータを接ぎ木することによって起こっていると思われる。8192バイトから増やすことで対応可能なはず。 - タイトルに<<~>>を含む場合
上記のようなデータがまぎれこむとどうも<~>という要素だと勘違いするらしく、うまく処理できない。RSS自体は<や>となっているので、この辺りは検証必要。