M.C.P.C.

―むり・くり―プラスコミュニケーション(更新終了)


| トップページ |

2011年1月15日 17:08

XML::SAXでRSS 2.0の不正なpubDate要素だけ修正をして書き出すPerlスクリプト

このエントリーをはてなブックマークに追加 mixiチェック

RSS 2.0を、XML::Feedにぶっこむと、たまに日付がうまく処理できないRSSがあって、表示の順序が制御できなくなったりします。

原因を調べたら、日付の表記が、RSS 2.0ではRFC 822に従う必要があるのですが、RSS生成プログラムがRFC 822に従っていない書式で書き出してしまうことが原因でした。

過去にも、

M.C.P.C.: 「RSS 簡単一発作成 『RSS 生成 CGI』」から書き出されるRSS 2.0の日付情報が不正なので直すパッチ

で取り上げたりもしましたが、生成元のプログラムを直していただくことって実際には難しいですよね……

というわけで、RSSを受信した場所で、直す方法をとりたいと思います。

さて、どんな書式になっているか、ということなのですが、

正 > "Fri, 01 Mar 2009 03:00:00 +0900"
誤 > "Fri, 01 Mar 2009 03:00:00 +09:00"
誤 > "Fri, 01 March 2009 03:00:00 +0900"

というわけで、タイムゾーン部分がおかしかったり、月名部分がロングフォーマットになっていたりするパターンが見られました。

つぎに、これをどう直していくか、ということなのですが、正規表現でやることもいいですが、せっかくなので、XMLパーサを使って、pubDate要素のみを操作してみることにしました。

それを行う道具としては、Perlクックブック第2版VOLUME2 pp.1092-1094 の、XML::SAX::Machinesを使ってみました。

XML::SAXは、イベント駆動型のパーサとなります。それは何のことを言っているのか、ということですが、プログラマは猟師さんで、XMLの要素が、山の動物たちです。猟師さんは、XML::SAXという罠を仕掛ける道具を使い、山の動物たちの通り道に、XML::SAXを置きます。今回の猟師さんの獲物がウサギさんだとしたら、XML::SAXに、ウサギさんだけ捕まえるように調整したわなを仕掛けます。そうすると、ウサギさんだけが捕獲できる、というわけです。ウサギさん用の罠なので、うさこが捕まるかもしれないし、キャシーが捕まるかもしれません。捕獲したウサギさんを、ウサギ汁にして食べるか、耳に発信器付けて野に放つかは、猟師さんが決めます。そんなタイプのXML処理系ってことなんです。

Filename: FixPubDate.pl

#!/usr/bin/perl
 
use strict;
use warnings;
use XML::SAX::Machines qw(:all);
use XML::SAX::Writer;
 
# XML::SAX::Writerの中でflagged utf8の値を書きだすので、
# PerlIOレイヤで:utf8を指定したファイルハンドル
# を渡します。
open my $fh, '>:utf8', 'new-feeds.rss' or die $!;
my $writer  = XML::SAX::Writer->new( Output => $fh );
my $machine = Pipeline( FixPubDate => $writer);
$machine->parse_uri('feed.rss');
close $fh;
exit;
 
package FixPubDate; 
use base qw(XML::SAX::Base);
 
my $is_in_pubDate = 0;
 
sub start_element {
  my ($self, $data) = @_; 
  if ($data->{Name} eq 'pubDate') {
    $is_in_pubDate = 1;
  }   
  $self->SUPER::start_element($data);
}
 
sub characters {
  my ($self, $data) = @_; 
  if ( $is_in_pubDate ) { 
    $data->{Data} =~ s{\+(\d\d):(\d\d)}{+$1$2};
    $data->{Data} =~ s{January  }{Jan}gx;
    $data->{Data} =~ s{February }{Feb}gx;
    $data->{Data} =~ s{March    }{Mar}gx;
    $data->{Data} =~ s{April    }{Apr}gx;
    $data->{Data} =~ s{May      }{May}gx;
    $data->{Data} =~ s{June     }{Jun}gx;
    $data->{Data} =~ s{July     }{Jul}gx;
    $data->{Data} =~ s{August   }{Aug}gx;
    $data->{Data} =~ s{September}{Sep}gx;
    $data->{Data} =~ s{October  }{Oct}gx;
    $data->{Data} =~ s{November }{Nov}gx;
    $data->{Data} =~ s{December }{Dec}gx;
  }
  $self->SUPER::characters($data);
}
  
sub end_element {
  my ($self, $data) = @_;
  if ($data->{Name} eq 'pubDate') {
    $is_in_pubDate = 0;
  }
  $self->SUPER::end_element($data);
}
 
1;

スクリプトとしては、カレントディレクトリの feed.rss を読み込み、pubDate要素を修正して、new-feed.rss に書き出します。

もうちょっとエレガントに書けそうな気がしますよね……

投稿 大野 義貴 [RSS] | |

トラックバック(0)

トラックバックURL: http://blog.dtpwiki.jp/MTOS/mt-tb.cgi/3467

コメントする