2008年09月18日(木) 14:49

フィードの広告を柔軟に削除する方法

はてなのホッテントリーRSSにも Goole のフィード広告が表示されるようになりまして、いい加減 目に余るのでフィルタしようと挑戦してみました。
自分だけが使うんじゃなくて、レン鯖でCGIが使える環境を持ってたりすると簡単に実行できる仕組みになるよう頑張ってみましたので、広告がうざくてたまらんって人はお試しを。

ねらい

フィードの広告をフィルタ。
後から変更しやすく。


以上2点。


フィルタ対象フィード

まずはじめに、以下のフィードの広告を削除してみます。


アサヒ・コム速報ニュース
http://www3.asahi.com/rss/index.rdf


フィルタには Yahoo!Pipes を利用

FeedADFilterを開きます。


20080917-01.gif


テキストフォームにフィードのURL(http://www3.asahi.com/rss/index.rdf)を入力して、すぐ横にある「Run Pipe」をクリックすると、フィードの内容がずらっと表示されます(この時点で広告は削除されています。「Edit Source」でフィルタのソースが確認できます)。


20080917-02.gif
<RSSを取得する>


続いて、上図のように「More options」をクリックしてスライドメニューを表示させ、一番上の「Get as RSS」をクリックします。


すると、ブラウザでRSSフィードのページにリンクするので、ブラウザのURLをコピーします。


今回の場合は、以下のようなURLになりました。


http://pipes.yahoo.com/pipes/pipe.run?FeedURL=http%3A%2F%2Fwww3.asahi.com%2Frss%2Findex.rdf&_id=5eefbdcda9c6a4f282fb920d7700db58&_render=rss


これを RSS Reader に登録すれば、広告を削除したフィードを“一応”読むことができます。


文字化けとタイトルの問題

ここまでの手順で、簡単にフィードから広告を削除できましたが、2つ問題があります(解決策は後述)。

  • 日本語が文字化けするフィードがある
  • フィードのタイトルがすべて「FeedADFilter」になる


日本語の文字化けは、フィードを配信しているサーバが HTTP ヘッダに正しく文字コードをセットしないために起こります(Yahoo!Pipesは HTTPヘッダに文字コードが見つからなければ ISO8859-1 決め打ちのようで。。。)。もしくは、フィードの文字コードがそもそも UTF-8 以外の場合も同じく化けることがあるようです。


また、フィードのタイトルがすべて「FeedADFilter」になるのは、広告削除に使用している Yahoo!Pipes がそうしているからです。


今のところ、どちらも Yahoo!Pipes 上で解決する手段がないようなので、Perlスクリプトで加工します。


文字化けの解決

例として、以下のフィードを使用します。


ITmedia TOP STORIES 最新記事一覧
http://rss.rssad.jp/rss/itmtop/2.0/topstory.xml


上記「ITmedia TOP STORIES 最新記事一覧」のフィードは、ブラウザで見る限り問題なく表示されますが、Yahoo!Pipesを通すと文字化けしてしまいます。


20080917-03.gif
<文字化けが発生>


そこで、以下のスクリプトを利用します。
指定したURLのフィードを取得し、HTTP ヘッダに文字コードを正しくセットして、UTF-8 以外のフィードなら UTF-8 に文字コード変換したのちに吐き出すだけの Perlスクリプトです。


[add_header.cgi]

#!/usr/bin/perl

use strict;
use warnings;

use LWP::UserAgent;
use Encode qw(encode decode);

my %Q = get_query();
my $feed = get_web_page($Q{'url'});
if(defined $Q{'char'}){
    $feed = decode($Q{'char'}, $feed);
    $feed =~ s/encoding=(?:"|')([a-zA-Z0-9\-]+)(?:"|')/encoding="UTF-8"/i;
    $feed = encode('utf8', $feed);
}
print "Content-type: application/xml;charset=UTF-8\n\n$feed";

sub get_query {
    my %COM;
    my $buffer = $ENV{'QUERY_STRING'};
    my @pairs = split(/&/,$buffer);
    foreach my $pair (@pairs){
        my($name, $value) = split(/=/,$pair);
        $COM{url_decode($name)} = url_decode($value);
    }
    return %COM;
}
sub url_decode {
    my $str = shift;
    $str =~ tr/+/ /;
    $str =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack('H2', $1)/eg;
    return $str;
}
sub get_web_page {
    my $url   = shift;
    my $agent = shift || 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1;)';
    my $to    = shift || 15;
    my $ua = LWP::UserAgent->new();
    $ua->timeout($to);
    $ua->agent($agent);
    my $req = new HTTP::Request GET => "$url";
    my $res = $ua->request($req);
    if($res->is_success){
        return $res->content;
    }else{
        return '';
    }
}


上記スクリプトを以下のようなURLで実行できるようにします。
http://www.example.com/path/to/add_header.cgi


そして、目的のフィードURL(ITmedia TOP STORIES 最新記事一覧)を引数URLに続けて、クエリーで引き渡します。
http://www.example.com/path/to/add_header.cgi?url=http://rss.rssad.jp/rss/itmtop/2.0/topstory.xml


もし、フィードの文字コードが UTF-8 以外の場合は、以下のように &char=[文字コード] というクエリーも追加します。
(UTF-8以外で配信しているフィードがすぐ見つからなかったのでURLはダミーです)。
http://www.example.com/path/to/add_header.cgi?url=http://www.example.com/non_utf8.xml&char=euc-jp


こうしてできた URL をYahoo!Pipes「FeedADFilter」のテキストフォームに入力すれば、文字化けが解消します。


20080917-04.gif
<文字化けが解消した図(フィルタも有効)>


文字化けの解消した結果のURLも「Get as RSS」から取得します(RSS[A])。


フィードタイトルの解決

文字化けが解消したものの、Yahoo!Pipesを通過したフィードは、タイトルが「FeedADFilter」になってしまう問題があります。
個別の記事タイトルではなく、フィードのタイトルなので、RSSの読み方(リーダー)によっては気にならないかもしれませんが、せっかくなので、この問題も解消します。
手段はやっぱり Perlスクリプトです(Perlがあればなんでも出来る)。


以下のスクリプトを設置、実行できるようにします。


[title_rename.cgi]

#!/usr/bin/perl

use strict;
use warnings;
use LWP::UserAgent;

# PipesURL
my $pipes_url = $ENV{'QUERY_STRING'};
my $pipes_feed = add_utf8(get_web_page($pipes_url));

# SrcURL
my ($src_url) = ($pipes_url =~ /FeedURL=(http[^&]+)/);
$src_url = url_decode($src_url);
my $src_feed = add_utf8(get_web_page($src_url));
my ($src_title) = ($src_feed =~ /<title>([^<]+)<\/title>/i);

# 置換
$pipes_feed =~ s/<title>[^<]+<\/title>/<title>$src_title by Pipes<\/title>/i;

# 出力
print "Content-type: application/xml;charset=UTF-8\n\n" . rm_utf8($pipes_feed);


sub url_decode {
    my $str = shift;
    $str =~ tr/+/ /;
    $str =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack('H2', $1)/eg;
    return $str;
}
sub get_web_page {
    # get_web_page($url, [$agent], [$to]);
    my $url   = shift;
    my $agent = shift || 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1;)';
    my $to    = shift || 15;
    my $ua = LWP::UserAgent->new();
    $ua->timeout($to);
    $ua->agent($agent);
    my $req = new HTTP::Request GET => "$url";
    my $res = $ua->request($req);
    if($res->is_success){
        return $res->content;
    }else{
        return '';
    }
}
sub rm_utf8 {
    my $str = shift || return;
    utf8::encode($str) if utf8::is_utf8($str);
    return $str;
}
sub add_utf8 {
    my $str = shift || return;
    utf8::decode($str) unless utf8::is_utf8($str);
    return $str;
}


以下のようなURLで実行できるようにします。
http://www.example.com/path/to/title_rename.cgi


そして、先ほどの、RSS[A](文字化けが解消した Pipes の RSS は、例えば以下のような URL になっているかと思います。
http://pipes.yahoo.com/pipes/pipe.run?FeedURL=http%3A%2F%2Fwww.example.com%2Fpath%2Fto%2Fadd_header.cgi%3Furl%3Dhttp%3A%2F%2Frss.rssad.jp%2Frss%2Fitmtop%2F2.0%2Ftopstory.xml&_id=hogehoge&_render=rss


その RSS[A] を title_rename.cgi にクエリーとして渡してしまいます。


以下のような URL が完成するかと思います。
http://www.example.com/path/to/title_rename.cgi?http://pipes.yahoo.com/pipes/pipe.run?FeedURL=http%3A%2F%2Fwww.example.com%2Fpath%2Fto%2Fadd_header.cgi%3Furl%3Dhttp%3A%2F%2Frss.rssad.jp%2Frss%2Fitmtop%2F2.0%2Ftopstory.xml&_id=hogehoge&_render=rss


これで、フィードのタイトル問題も解決です。


出来上がった長いフィード URL をRSSリーダーに登録してみましょう。
バッチリ広告が削除され、普通に読めれば成功です。
一応、フィードタイトルは、元のフィードと区別が付くように、タイトルの後ろに「by Pipes」と付きます。


また、文字コードの問題がないフィードも、Yahoo!Pipesを通過させるとタイトル問題が発生するので、このタイトルを元に戻す方法を適用した方が良いです。


まとめ

正直、全部まとめて Perl でやってしまうことももちろん出来ますが、標準モジュールだけでちゃっちゃとやっつけるのはなかなか難しそうだったので、フィルター部分は Yahoo!Pipes に投げました。キャッシュもいい感じにやってくれるし。


Pipes は基本的な広告しかフィルタしていないので、どんどんコピーして(Clone化して)強力なフィルタを構築して教えて欲しいです。
唯一の課題はフィードの巡回頻度ですかねえ。。。(CGIが共有できればみんなで同じフィードを共有できて巡回頻度を上げられる気もするけど)


というわけで、わりと多くの人が利用できて、柔軟に広告をフィルタできる手法の紹介でした。

このエントリーに最初のコメントを書いてみませんか?

名前  クッキーに保存 
エントリーナビ
フィードの広告を柔軟に削除する方法」は、
Webの技 カテゴリーの記事です。


関連エントリー
全文Atomフィードの配信を再開しました
GoogleとYahoo!とMSNのインデックス雑感
pixivのランキングをRSS化するスクリプト作ってみた
ボケて、人気のボケ Feed も欲しいよー
Wikipediaの人気ワードを表示して概要も読めるウィジット作った

トラックバック
このエントリーへトラックバックするURL (言及リンクのないトラックバックは無効です)