カモランドで提供しているRSSを,記事全文を載せるように変更した.Yuki::RSS.pmを改造して,contentモジュールの表記で生成するようにした.

RSSで全文配信する

はじめに

カモランドでのRSSの提供だが,当初は通信頻度の多さを考えて,サマリだけを配信すべきだと考えていたのだが, 自分のRSS利用生活が進んでいくと,利用者としてはRSSとして全文を配信してもらったほうが, RSSリーダー上で読み取りが完結するので便利だと考えるようになった.

俺のRSS利用生活は,

  1. Bloglines にRSSフィードを登録し,BloglinesのクローラにRSSの中身を収集してもらう
  2. 収集された結果をBloglinesからAPIで取得して,自作リーダーで閲覧する

という感じなのだが,全文を配信していないフィード (NIKKEI NET - IT PLUSOTN Japanの掲示板@ITJVN など) への対応として, 全文を取得して表示するような実装を自作リーダーにわざわざ作りこんでいる. 理由は,記事本体のページに飛ぶのが面倒で,時間のロスだからだ. RSSリーダー上で,記事本体も読めるようにしてしまっている.

しかしサイトによって全文取得の方法が違っているので実装を分けているのが面倒だし,今後のメンテ上もいまいちだ. (実装とは具体的には,取得したページのHTMLからページヘッダ,ナビゲーション部などを除去して記事本文を切り出すところ. これはサイトのページデザインにもろに依存する)

最近のスラドにもこういう話題が出ており,全文配信が便利だと感じている人は俺だけではないみたいだ.

...全文配信で便利になるなら,配信しようではないか.

RSSに全文を載せる方法

技術的にはどうなのかだが( RSS -- サイト情報の要約と公開 The Web KANZAKI ),RSSからはcontentモジュールを使った表記で全文配信できるようだ. (これまでサマリを入れていた<description>に全文を入れてしまうのは,さすがに気が引けるのでやらない)

また,この利用法が一般的に通用するのか,世の中のRSSリーダーが解釈してくれるのかが気になるが, いくつか調べたところ,FC2ブログ のRSSがこの形式(contentモジュールによる全文配信)をとっていたので, FC2ブログがやってるならまぁ大丈夫なんじゃなかろうかという気になった. 実際Bloglines では,FC2ブログの記事全文を正しく表示できていた.

しかし<content:encoded>を使うとしても,単純に記事全文をCDATAで入れれば良いというものではなかった. 実際のカモランドの記事HTMLをチェックしてみると,ここらへんがまずそう.

JavaScript
WebインタフェイスのRSSリーダーだと誤って動作してしまう可能性があるので,除去する必要がある
スタイルシート
リーダー側でどうなるか不明なので,一応除去する
相対パス形式のリンク
絶対パスにする必要がある

配信サイズもできれば節約したいので,結局HTMLタグについては,基本的な文書構造に関して出力しうる必要最小限を 残して,それ以外は除去するようにした.

つまり,同じ記事に対して,

HTML形式(記事本体)
 → [JavaScript等の除去] → RSSに入れる全文HTML形式(content:encoded)
 → [サマリ抽出] → RSSに入れるサマリの形式(description)
の3種類を生成することになる.

Yuki::RSS.pmの改造

うちではRSSの生成にYuki::RSS.pmを使っているが,これをcontentに対応するために改造した.

要は,

  • contentモジュール用のネームスペースxmlns:contentを定義する
  • 各itemに<content:encoded>要素を出力する
  • <content:encoded>要素向けのHTML変換を行う

という感じ.

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$/);
}
kamolandをフォローしましょう


© 2017 KMIソフトウェア