データベースから投稿データを拾ってXML::RSSでRSS 1.0を生成
ユーザが投稿した内容を保存しているデータベースからデータを拾い上げて、RSSを作ることにするか…… (´∀`)
定番のPerlのXML::RSSを使って、MySQLのデータベースに入っているとする。こんな流れになるかな。サンプルソースコードをまとめたものは、この記事の最後にあります。
#!/usr/bin/perl use strict; use warnings; use CGI; use DBI; use YAML; use XML::RSS; my $CONF = YAML::LoadFile("/your/apps/path/conf.yaml"); my $rss = new XML::RSS (version => '1.0'); my $form = new CGI; $form->charset('utf-8');
まずコンフィグファイルはYAMLが便利なのでこれを使う。私の趣味。
RSSは、new XML::RSSする時にバージョンを選べる。ここを2.0にするだけでRSS 2.0を吐いてくれるから楽チン。
CGIでも無いのにCGI.pmを使っているが、これはユーザがポストした中身を出力するときにescapeHTML関数を使いたいから。それだけのために使っている。(´∀`) charsetをutf-8しておかないと文字化けすることがある。
my $DB_OPT = { mysql_enable_utf8 => 1, on_connect_do => [ "SET NAMES 'utf8'", "SET CHARACTER SET 'utf8'" ], }; my $dbh = DBI->connect( "dbi:mysql:dbname=$CONF->{db_name};host=$CONF->{db_host};mysql_server_prepare=1", $CONF->{db_user}, $CONF->{db_passwd}, $DB_OPT ) or die "$!\n Error: failed to connect to DB.\n"; my $ary_ref = $dbh->selectall_arrayref( "SELECT DATE_FORMAT(settime,'%Y-%m-%dT%H:%I:%S'), title, name, posttext, filepath " . "FROM userpost ORDER BY settime DESC LIMIT 10");
connectするところまでは、MySQLにPerlからDBIで接続するときの定番なので省略……。
今回は面倒なので、SQL文はselectall_arrayrefを使ってしまった。本来ならfetchrow_arrayrefとか使うべきで、あまり推奨されないメソッドなのだが、LIMIT 10と行数制限のあるSQLなので、ま、いいか。(LIMIT付けないSQL文でselectall_arrayrefを無造作に使って、対象が巨大なテーブルで1億行とか返された日には、大変なことになる)
SELECTする時に、RSSで使う'%Y-%m-%dT%H:%I:%S'というISO形式の時刻フォーマットで取ってきている。
$rss->channel( title => "<![CDATA[RSS title]]>", link => "http://www.example.org/hoge/", description => "<![CDATA[Recent posts of Hoge]]>", dc => { date => $ary_ref->[0][0], subject => '<![CDATA[Recent posts]]>', creator => 'ozuma', publisher => 'ozuma', rights => 'Copyright 2012-, ozuma', } );
RSSのチャンネル要素をもりもり設定する。
foreach my $record (@$ary_ref) { my $settime = $record->[0]; my $title = $record->[1]; my $name = $record->[2]; my $posttext = $form->escapeHTML($record->[3]); my $filepath = $record->[4]; $rss->add_item( title => $title, link => "$CONF->{top_url}$filepath", description => '<![CDATA[' . $posttext . ']]>', dc => { subject => $title, creator => $name, date => $settime, }, ); }
次に各レコードごとに値を取り出して、RSSのitemをひとつひとつaddしていく。$posttextはユーザが入力した値で、XSSの危険があるためここでCGI.pmのescapeHTML関数を借りた。
print $rss->as_string; $dbh->disconnect;
最後にRSSをas_string関数で出力し、忘れちゃいけないデータベースハンドルをdisconnetしてオシマイ。お疲れ様(´∀`)
サンプルコード全文
#!/usr/bin/perl use strict; use warnings; use CGI; use DBI; use YAML; use XML::RSS; my $CONF = YAML::LoadFile("/your/apps/path/conf.yaml"); my $rss = new XML::RSS (version => '1.0'); my $form = new CGI; $form->charset('utf-8'); my $DB_OPT = { mysql_enable_utf8 => 1, on_connect_do => [ "SET NAMES 'utf8'", "SET CHARACTER SET 'utf8'" ], }; my $dbh = DBI->connect( "dbi:mysql:dbname=$CONF->{db_name};host=$CONF->{db_host};mysql_server_prepare=1", $CONF->{db_user}, $CONF->{db_passwd}, $DB_OPT ) or die "$!\n Error: failed to connect to DB.\n"; my $ary_ref = $dbh->selectall_arrayref( "SELECT DATE_FORMAT(settime,'%Y-%m-%dT%H:%I:%S'), title, name, posttext, filepath " . "FROM userpost ORDER BY settime DESC LIMIT 10"); $rss->channel( title => "<![CDATA[RSS title]]>", link => "http://www.example.org/hoge/", description => "<![CDATA[Recent posts of Hoge]]>", dc => { date => $ary_ref->[0][0], subject => '<![CDATA[Recent posts]]>', creator => 'ozuma', publisher => 'ozuma', rights => 'Copyright 2012-, ozuma', } ); foreach my $record (@$ary_ref) { my $settime = $record->[0]; my $title = $record->[1]; my $name = $record->[2]; my $posttext = $form->escapeHTML($record->[3]); my $filepath = $record->[4]; $rss->add_item( title => $title, link => "$CONF->{top_url}$filepath", description => '<![CDATA[' . $posttext . ']]>', dc => { subject => $title, creator => $name, date => $settime, }, ); } print $rss->as_string; $dbh->disconnect;