RSSリーダーの本文表示の違いを探る(その3:NoCDATAの利用)
Movable Typeには NoCDATA というオプションが用意されています。これを用いれば RSSフィードのデータの特殊文字をエンコードする設定も可能です。ユーザーズマニュアルに下記の説明があります。
デフォルトでMovable Typeは、データをXMLにエンコードしているとき、あなたのデータにHTMLタグや、XMLに対し安全でないデータが含まれていないかどうかをチェックし、検出した場合は、データをCDATAタグで囲みます。 ただし、ニュース・アグリゲータの中には、CDATAを他のデータと一緒にすると、問題が生じることがあります。こうした問題がある場合は、NoCDATAを使って特殊文字をエンティティにエンコードすることができます。
具体的には設定ファイル mt.cfg の250行目にある
- # NoCDATA 1
という行の先頭の#とブランクを削除することで有効になります。
実験として、
本文に<a href="hogehoge">HTMLタグ</a>を含んでいます。
という本文を例に変換してみました。
NoCDATA 0 の場合
<description><![CDATA[<p>本文に<a href="hogehoge">HTMLタグ</a>を含んでいます。</p>]]></description>
NoCDATA 1 の場合
<description><p>本文に<a href="hogehoge">HTMLタグ</a>を含んでいます。</p></description>
これまでのまとめです。
- HTMLタグや特殊文字を存在する場合、CDATAセクションとなる
- NoCDATAオプションを利用することでHTMLタグや特殊文字のエンコード(つまりCDATAセクションとならない)が可能
- MTEntryBody を使用し Convert Line Breaks が有効の場合、改行用のHTMLタグが付与されるため常にCDATAセクションとなる。MTEntryExcerpt を使用するとHTMLタグは全て除去されるため、CDATAセクションとなるのはHTMLエンティティが存在する場合のみ
- Movable Type3.0 と 3.1x では description のテキスト部分に用いられているMTタグが異なる
ということで、一番最初の index.rdf のサンプルはHTMLタグを用いてCDATAセクションの有無を説明したかったのですが、CDATAセクション有無がMTタグに依存してしまうため、description には MTEntryExcerpt を適用した状態でHTMLエンティティを用いました。
しかしよく考えてみると、その状態で本文とは別に「概要(excerpt)」を記述すればHTMLタグを利用できた訳です。つまりCDATAセクション化やエンコードはそもそも「概要」を対象とした機能であり、そうすることで機能は本来の役割を自然に振る舞うのではないでしょうか。
RSSリーダーの本文表示の違いを探る(その2)
RSSフィード本文のエンコードの仕組みについては前回の通りですが、ここでひとつ疑問がわきました。それはサイトによってHTMLタグがあるものとないものが混在するのはなぜか?という点についてです。これはMyBlogListリーダーをご覧になったことのある方ならご存知と思います。
本テンプレートユーザの方は Movable Type をお使いなので一律同じ結果、つまりHTMLタグが常に表示されるか、あるいは常に表示されないか、のどちらか一方になるだろうと思って見比べてみたところ、それでもタグありとタグなしの表示が混在していました。
今回はこの原因について探ってみました。たいした結果ではありません。
実は最初、私が試したサンプルは本文に
本文にHTMLタグを含んでいません。本文にHTMLタグを含んでいません。
本文にHTMLタグを含んでいません。
と書いて、これをRSSフィードにすると
<description>本文にHTMLタグを含んでいません。本文にHTMLタグを含んでいません。本文にHTMLタグを...</description>
という結果になることを期待しました。ところが実際に生成してみると、
<description><![CDATA[<p>本文にHTMLタグを含んでいません。本文にHTMLタグを含んでいません。<br />
本文にHTMLタグを含んでいません。</p>]]></description>
となりました。これは明らかに Mobable Type の Convert Line Breaks 機能により <p> や <br> タグが付与されたもので、たしかにMyBlogListリーダーではここから "<![CDATA]" と "]]>" を除いた部分が表示されていた記憶があります。
ではタグが付与されないパターンはどうやって生成されているのでしょうか?
ここで小粋空間の index.rdf(抜粋) を見てみると
:
<item rdf:about="http://www.koikikukan.com/archives/2005/01/25-233913.php">
<title>Movable Typeの脆弱性と対策</title>
<link>http://www.koikikukan.com/archives/2005/01/25-233913.php</link>
<description>ほぼ引用ですいません(+出遅れてます)。 【重要】 Movable Typeの脆...</description>
<dc:subject>3.121-ja</dc:subject>
<dc:creator>yujiro</dc:creator>
<dc:date>2005-01-25T23:39:13+09:00</dc:date>
</item>
:
と、HTMLタグは付与されていませんでした(他のエントリーについても同様です)。私は index.rdf のテンプレートをカスタマイズしていませんし、そもそも積極的にカスタマイズするページでもありません。
両者の違いについて内部処理で表示が切り替わる仕組みがあるのか?とも考えましたが、答えは簡単でした。
小粋空間で用いているindex.rdf テンプレート(抜粋)では該当部分に
<description><$MTEntryExcerpt encode_xml="1"$></description>
と MTEntryExcerpt が設定されており、実験で用いた Movable Type 3.121(新規インストールで利用)の index.rdf テンプレートには
<description><$MTEntryBody encode_xml="1"$></description>
と、MTEntryBodyが設定されていました。
つまり、Movable Type3.1x で index.rdf テンプレートの description タグのテキスト部分に MTEntryBody が用いられるようになったことが、表示に違いが生じていた原因と考えられます。小粋空間は3.01からのアップデートだったため、旧テンプレートの設定である MTEntryExcerpt が引き継がれていた、という訳です。
RSSリーダーの本文表示の違いを探る(その1)
昨年末、「MyBlogListリーダー等のRSSリーダーの本文表示でHTMLタグが表示される場合があります(注:現在は改善されているようです)がどうしてですか?」という質問の回答です。色々調べてみたとろ結構面白い結果となりましたので連載ものでいってみます。
MyBlogListリーダーで本文にHTMLタグが含まれるのはその仕様に依存すると思われますが、HTMLタグが本文に付与されることについては、少なくとも Movable Typeについてはその機能によるものです。
具体的には、本文中にHTMLタグまたはHTMLエンティティ(らしきもの)が存在する場合、文章全体がCDATAセクションという
- <![CDATA[ ? ]]>
というもので括られます("?"に本文が入ります)。これで括ることによって文章にどのような文字が含まれていても適正なXMLデータとして扱われますので、一切加工されずそのまま保存されるという仕組みになっています。
RSSリーダーはRSSフィード(index.rdf/index.xml等)を参照します。下記に Movable Type でひとつだけエントリーしたRSS1.0フィードの index.rdf を示します。上は本文にHTMLエンティティを含まないもの、下は含んだ(ここでは"<"と">")ものです。青字部分と赤字部分がそれぞれ対象となる部分です。
index.rdf(本文にHTMLエンティティを含まない)
<?xml version="1.0" encoding="utf-8"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:admin="http://webns.net/mvcb/"
xmlns:cc="http://web.resource.org/cc/"
xmlns="http://purl.org/rss/1.0/">
<channel rdf:about="http://?/">
<title>First Weblog</title>
<link>http://?/</link>
<description></description>
<dc:language>ja</dc:language>
<dc:creator></dc:creator>
<dc:date>2005-01-27T17:39:25+09:00</dc:date>
<admin:generatorAgent rdf:resource="http://www.movabletype.org/?v=3.121-ja" />
<items>
<rdf:Seq><rdf:li rdf:resource="http://?/archives/2005/01/post.html" />
</rdf:Seq>
</items>
</channel>
<item rdf:about="http://?/archives/2005/01/post.html">
<title>てすと</title>
<link>http://?/archives/2005/01/post.html</link>
<description>本文にHTMLエンティティを含んでいません。本文にHTMLエンティティを含んでい...</description>
<dc:subject></dc:subject>
<dc:creator>Melody</dc:creator>
<dc:date>2005-01-27T17:39:25+09:00</dc:date>
</item>
</rdf:RDF>
index.rdf(本文にHTMLエンティティを含む)
<?xml version="1.0" encoding="utf-8"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:admin="http://webns.net/mvcb/"
xmlns:cc="http://web.resource.org/cc/"
xmlns="http://purl.org/rss/1.0/">
<channel rdf:about="http://?/">
<title>First Weblog</title>
<link>http://?/</link>
<description></description>
<dc:language>ja</dc:language>
<dc:creator></dc:creator>
<dc:date>2005-01-27T17:39:25+09:00</dc:date>
<admin:generatorAgent rdf:resource="http://www.movabletype.org/?v=3.121-ja" />
<items>
<rdf:Seq><rdf:li rdf:resource="http://?/archives/2005/01/post.html" />
</rdf:Seq>
</items>
</channel>
<item rdf:about="http://?/archives/2005/01/post.html">
<title>てすと</title>
<link>http://?/archives/2005/01/post.html</link>
<description><![CDATA[本文に<b>HTMLエンティティ</b>を含んでいます。...]]></description>
<dc:subject></dc:subject>
<dc:creator>Melody</dc:creator>
<dc:date>2005-01-27T17:39:25+09:00</dc:date>
</item>
</rdf:RDF>
仮にHTMLタグが含まれていれば、
<description><![CDATA[本文に<b>HTMLエンティティ</b>を含んでいます。...]]></description>
となります。最初からHTMLタグを例に扱った方がわかりやすいのですが、扱えない理由があります。これについては後ほど明らかにしたいと思います。
参考までに、変換処理を実際に行っている
- lib/MT/Util.pm
のソース(抜粋)を掲載しておきます。
my %Map = ('&' => '&', '"' => '"', '<' => '<', '>' => '>', '\'' => ''');
my $RE = join '|', keys %Map;
sub encode_xml {
my($str, $nocdata) = @_;
$nocdata ||= MT::ConfigMgr->instance->NoCDATA;
if (!$nocdata && $str =~ m/
<[^>]+> ## HTML markup
| ## or
&(?:(?!(\#([0-9]+)|\#x([0-9a-fA-F]+))).*?);
## something that looks like an HTML entity.
/x) {
## If ]]> exists in the string, encode the > to >.
$str =~ s/]]>/]]>/g;
$str = '<![CDATA[' . $str . ']]>';
} else {
$str =~ s!($RE)!$Map{$1}!g;
}
$str;
}

