RSSをパースして記事を収集する

前回はRSSを使ったアプリケーションの概要とサーバ構築をしました。
今回は実際にRSSフィードを収集し、記事の情報をデータベースに格納するところまでやります。
■管理するデータ
システム内で管理するデータを挙げます。
- 収集対象となるRSSフィードの情報
- RSSフィードから収集した記事やWEBページの情報
- SNSから取得した人気の指標(いいね!の数など)
これらのデータを正規化し、テーブルとして定義すると以下のようになります。
●収集対象のフィードを管理するテーブル(feedテーブル)
項目名 | 物理名 | データ型(サイズ) |
---|---|---|
フィードNo. | feedno | char(4) |
フィード名 | feednm | varchar(200) |
フィードURL | feedurl | varchar(2083) |
フォーマット | format | char(1) |
- フィードNo.という内部的な項目を作成し、プライマリキーにします。フィードNo.は0000〜9999の値を取るようにします。
- フォーマットはRSS1.0、RSS2.0、Atomのいずれかを示すコードです。ここでは、RSS1.0=1, RSS2.0=2, Atom=3とします。
- URLの長さはHTTP1.1の仕様を記述しているRFC2616では定義されておらず、各々のWEBサーバとブラウザの制限によります。IEの2,083文字に合わせて、2,083バイトで定義しています。
●収集した記事の情報を管理するテーブル(articleテーブル)
項目名 | 物理名 | データ型(サイズ) |
---|---|---|
フィードNo. | feedno | char(4) |
URL | url | varchar(512) |
タイトル | title | varchar(1024) |
内容 | contents | varchar(1024) |
記事更新日時 | updatedate | datetime |
int | ||
int | ||
はてな | hatena | int |
- プライマリキーはフィードNo.とURLにします。
- URLはフィードに合わせて2,083バイトとしたいところですが、InnoDBのインデックスキーの制約(767バイト)により512バイトに設定しています。桁数を超えることは少ないと思いますが、もし超えた場合はその記事は捨てます。
- タイトル、内容の桁数は適当なので、桁数を超えたフィードを受け取った場合は、超えた分を切り捨てて登録するようにします。
■データベース準備
MySQLにログインし、必要なテーブルを作成します。
# データベース作成 create database rss; use rss; # feedテーブル作成 create table feed ( feedno char(4), feednm varchar(200), feedurl varchar(2083), format char(1), primary key(feedno) ); # articleテーブル作成 create table article ( feedno char(4), url varchar(512), title varchar(1024), contents varchar(1024), updatedate datetime, facebook int, twitter int, hatena int, primary key(feedno, url) );
これでテーブルが準備できました。早速、当社ブログのRSSフィードを登録してみます。
フォーマットはRSS2.0です。
insert into feed values ('0000','株式会社オリエンタルインフォーメイションサービス','http://www.ois-yokohama.co.jp/blog/wordpress/?feed=rss2','2');
■収集機能
データベースの準備ができたので、収集機能を作ります。
フォーマットごとに収集する要素、属性は以下のようにします。
項目 | RSS1.0 | RSS2.0 | Atom |
---|---|---|---|
タイトル | /rdf//item/title/ | rss/channel//item/title | /feed//entry/title |
URL | /rdf//item/link | /rss/channel//item/link | /feed//entry/link/@href |
内容 | /rdf//item/description | /rss/channel//item/description | /feed//entry/summary |
記事更新日時 | /rdf//item/dc:date | /rss/channel//item/pubDate | /feed//entry/updated |
まずは、収集対象のフィードをfeedテーブルから取得する部分です。
<?php $dsn = 'mysql:host=localhost;dbname=rss;port=3306;'; $pdo = new PDO($dsn, 'user', 'password'); $sql = 'SELECT feedno, feedurl, format FROM feed'; $stmt = $pdo->prepare($sql); $flag = $stmt->execute(); if (!$flag) { $info = $stmt->errorInfo(); exit($info[2]); } $feed_array = array(); while($rec = $stmt->fetch(PDO::FETCH_ASSOC)){ $feed_array[] = $rec; } $stmt = null; ?>
続いて、RSSフィードの内容をパース&articleテーブルへの格納部分を作ります。
XMLのパースにはsimplexml_load_fileを使用します。
<?php date_default_timezone_set('Asia/Tokyo'); define('RSS1_FORMAT','1'); define('RSS2_FORMAT','2'); define('ATOM_FORMAT','3'); $stmt = $pdo->prepare('insert into article (feedno, url, title, contents, updatedate) values (:feedno , :url , :title , :contents, :updatedate) on duplicate key update title = :title, contents = :contents, updatedate = :updatedate'); $stmt->bindParam(':feedno', $feedno, PDO::PARAM_STR); $stmt->bindParam(':url', $url, PDO::PARAM_STR); $stmt->bindParam(':title', $title, PDO::PARAM_STR); $stmt->bindParam(':contents', $contents, PDO::PARAM_STR); $stmt->bindParam(':updatedate', $updatedate, PDO::PARAM_STR); foreach($feed_array as $feed){ $feedurl = $feed['feedurl']; $feedno = $feed['feedno']; $format = $feed['format']; $xml = simplexml_load_file($feedurl); if($format == RSS1_FORMAT){ foreach($xml->item as $item) { $url = $item->link; if(strlen($url) > 512) continue; $title = $item->title; $contents = $item->description; $dc = $item->children('http://purl.org/dc/elements/1.1/'); $updatedate = date('Y-m-d H:i:s', strtotime($dc->date)); $stmt->execute(); } }else if($format == RSS2_FORMAT){ foreach ( $xml->channel->item as $item ) { $url = $item->link; if(strlen($url) > 512) continue; $title = $item->title; $contents = $item->description; $updatedate = date('Y-m-d H:i:s', strtotime($item->pubDate)); $stmt->execute(); } }else if($format == ATOM_FORMAT){ $children = $xml->children('http://www.w3.org/2005/Atom'); $entries = $children->entry; foreach($entries as $entry) { $url = $entry->link->attributes()->href; if(strlen($url) > 512) continue; $title = $entry->title; $contents = $entry->summary; $updatedate = date('Y-m-d H:i:s', strtotime($entry->updated)); $stmt->execute(); } }else{ exit(1); } } ?>
2つを合体させてファイル名をrss_correct.phpで保存し、動作確認をします。
php rss_correct.php
フィードが収集されてarticleテーブルに登録されていることが確認できたら、定常的に収集できるようにcronを設定します。下記の例では毎時0分に収集処理が動くように設定しています。作成したプログラムはホームディレクトリのbinに置いています。
crontab -e 0 * * * * /usr/bin/php /home/{user]/bin/rss_correct.php
これで定常的にフィードから記事の情報が収集できるようになりました。
次回はSNSからの指標の取得と、Webアプリやスマホ等のフロント側への配信ができるような機能を作ります。