カモランドで提供しているRSSを,記事全文を載せるように変更した.Yuki::RSS.pmを改造して,contentモジュールの表記で生成するようにした.
RSSで全文配信するはじめにカモランドでのRSSの提供だが,当初は通信頻度の多さを考えて,サマリだけを配信すべきだと考えていたのだが, 自分のRSS利用生活が進んでいくと,利用者としてはRSSとして全文を配信してもらったほうが, RSSリーダー上で読み取りが完結するので便利だと考えるようになった.俺のRSS利用生活は, という感じなのだが,全文を配信していないフィード (NIKKEI NET - IT PLUS, OTN Japanの掲示板, @IT, JVN など) への対応として, 全文を取得して表示するような実装を自作リーダーにわざわざ作りこんでいる. 理由は,記事本体のページに飛ぶのが面倒で,時間のロスだからだ. RSSリーダー上で,記事本体も読めるようにしてしまっている. しかしサイトによって全文取得の方法が違っているので実装を分けているのが面倒だし,今後のメンテ上もいまいちだ. (実装とは具体的には,取得したページのHTMLからページヘッダ,ナビゲーション部などを除去して記事本文を切り出すところ. これはサイトのページデザインにもろに依存する) 最近のスラドにもこういう話題が出ており,全文配信が便利だと感じている人は俺だけではないみたいだ. ...全文配信で便利になるなら,配信しようではないか.
RSSに全文を載せる方法技術的にはどうなのかだが( RSS -- サイト情報の要約と公開 The Web KANZAKI ),RSSからはcontentモジュールを使った表記で全文配信できるようだ. (これまでサマリを入れていた<description>に全文を入れてしまうのは,さすがに気が引けるのでやらない)また,この利用法が一般的に通用するのか,世の中のRSSリーダーが解釈してくれるのかが気になるが, いくつか調べたところ,FC2ブログ のRSSがこの形式(contentモジュールによる全文配信)をとっていたので, FC2ブログがやってるならまぁ大丈夫なんじゃなかろうかという気になった. 実際Bloglines では,FC2ブログの記事全文を正しく表示できていた. しかし<content:encoded>を使うとしても,単純に記事全文をCDATAで入れれば良いというものではなかった. 実際のカモランドの記事HTMLをチェックしてみると,ここらへんがまずそう.
配信サイズもできれば節約したいので,結局HTMLタグについては,基本的な文書構造に関して出力しうる必要最小限を 残して,それ以外は除去するようにした. つまり,同じ記事に対して, HTML形式(記事本体) → [JavaScript等の除去] → RSSに入れる全文HTML形式(content:encoded) → [サマリ抽出] → RSSに入れるサマリの形式(description)
Yuki::RSS.pmの改造うちではRSSの生成にYuki::RSS.pmを使っているが,これをcontentに対応するために改造した.要は,
という感じ. RSS.pmのas_string関数 sub as_string { my ($self) = @_; my $doc = <<"EOD"; <?xml version="1.0" encoding="$self->{encoding}" ?> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://purl.org/rss/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" > <channel rdf:about="$self->{channel}->{link}"> <title>$self->{channel}->{title}</title> <link>$self->{channel}->{link}</link> <description>$self->{channel}->{description}</description> <items> <rdf:Seq> @{[ map { qq{<rdf:li rdf:resource="$_->{link}" />} } @{$self->{items}} ]} </rdf:Seq> </items> </channel> @{[ map { qq{ <item rdf:about="$_->{link}"> <title>$_->{title}</title> <link>$_->{link}</link> <description>$_->{description}</description> <content:encoded><![CDATA[@{[ filter_content($_->{content}) ]}]]></content:encoded> <dc:date>$_->{dc_date}</dc:date> <dc:subject>$_->{subject}</dc:subject> </item> } } @{$self->{items}} ]} </rdf:RDF> EOD } RSS.pmに追加した関数 # HTMLを<content:encoded>向けの内容に変換する # これはカモランドのWikiが生成するHTMLに合わせた変換なので, # 実際にサイトで使っているHTMLタグに合わせて変換をカスタマイズする必要あり # sub filter_content { my ($c) = @_; $c =~ s/<script.+?<\/script.*?>//gis; $c =~ s/<style.+?<\/style.*?>//gis; $c =~ s/<([^>]+)>/tagfilter($1)/eg; return $c; } sub tagfilter { my ($tag) = @_; $tag = tagfilter2($tag); $tag =~ s/ class="[^"]+"//; $tag =~ s/ class='[^']+'//; return $tag; } sub tagfilter2 { my ($tag) = @_; my $ltag = lc $tag; return "<$tag>" if ($ltag =~ /^\/?p$/); return "<$tag>" if ($ltag =~ /^\/?h[0-9]$/); return "<$tag>" if ($ltag =~ /^a /); return "<$tag>" if ($ltag =~ /^\/a$/); return "<$tag>" if ($ltag =~ /^\/?li( |$)/); return "<$tag>" if ($ltag =~ /^\/?ul( |$)/); return "<$tag>" if ($ltag =~ /^\/?ol( |$)/); return "<$tag>" if ($ltag =~ /^\/?pre( |$)/); return "<$tag>" if ($ltag =~ /^\/?br$/); return "<$tag>" if ($ltag =~ /^\/?span( |$)/); return "<$tag>" if ($ltag =~ /^\/?div( |$)/); return "<$tag>" if ($ltag =~ /^\/?table( |$)/); return "<$tag>" if ($ltag =~ /^\/?tr( |$)/); return "<$tag>" if ($ltag =~ /^\/?td( |$)/); return "<$tag>" if ($ltag =~ /^\/?th( |$)/); return "<$tag>" if ($ltag =~ /^\/?dl$/); return "<$tag>" if ($ltag =~ /^\/?dt$/); return "<$tag>" if ($ltag =~ /^\/?dd$/); } |