コンテンツフィルタというのは、コンテンツをフィルタリングするソフトウェアです。バナー広告、JavaScript、ポップアップウインドウといったものを除去するものです。
代表的なものに、Proxomitron-J や Privoxy があります。
Proxomitron や Privoxy は、大変有用なソフトウェアですが、フィルタのルールを独自の言語で書かなければなりません。
「そんなのヤだ! ヤだ! Ruby じゃなきゃヤだ!」と言う声が聞こえてきますね。という か、そうでしょ。そうなんでしょ。そうだと言ってくれ。そうじゃないとこの記事は成り たたないのだっ!!
……というわけで、Ruby でコンテンツフィルタを書いてみましょう。
しかし、プロキシサーバを一から書くとなると、大変です。面倒です。やってられません。
そこで WEBrick ですよ!
WEBrick っていうのは、知ってる人は知ってるし、知らない人は知らない、そんなライブ ラリです。
……と、こんなことで済ますと編集長に怒られるので、真面目に説明します。
RAA:webrick によると、
だそうです。日本語にすると、「WEBrick は HTTP サーバを作るためのライブラリだよっ ♪」ということですね。このライブラリを使えば HTTP サーバを数行で書けます。す げぇー。1
しかも、Ruby 1.8 系標準添付なので、気軽に使えるのも良いのですよ。
WEBrick ってのは、HTTP サーバだけじゃなくて、HTTP Proxy サーバを作るための機能 もついてるんですよ。WEBrick::HTTPProxyServer というクラスがそれ。 これを使えば、HTTP プロキシサーバも数行で書けます。
で、書いてみました。
うわぁ。本当に数行だよ。これでプロキシサーバとして動作するってんだから凄いよねぇ。
「書いた気がしない」とか「手抜きだ」とか言わないように。そのへんはまぁ、あれだよ。 うん。
君が自前主義症候群なのだよ
そうだ。そうに違いない。こんなに便利なものがあるんだから、それを使えば良いのだ。 自分はもっと高いレベルの部分を実装することに力を入れるのだ。車輪の再発明は避ける のだ!
ということで。
えぇーっと、一行ずつに解説とかは面倒なんでやめます。コメントを懇切丁寧に書いてあ るんで、それを読んでください。
さて、これを simpleproxy.rb とかいう名前で保存して、普通に ruby simpleproxy.rb とかやって起動すれば、ちゃんとプロキシサーバとして動きます。
前の章で作ったのは、
WEBrick だとこんなに短かくプロキシサーバを書けるんだぞ
というのを見せるためのプログラムなので、駄目駄目です。ポート番号が80番だったりし ます。
というわけで、ちゃんとオプションを設定しましょう。
さて、:Logger は、WEBrick::Log::new(“log.txt”, WEBrick::Log::DEBUG) を設定していますね。ここは重要なので、解説しておきます。
WEBrick::Log.new() の第一引数は、ログの出力先です。ファイル名を与えると、そのファ イルにログを吐きます。nil を与えると、$stderr にログを吐きます。それ以外なら、与 えられた引数に << します。
第二引数は、ログの出力レベルです。FATAL、ERROR、WARN、INFO、DEBUG の中から、用途 に応じて選んでください。今回はすべてのログを見たいので、WEBrick::Log::DEBUG を選 びました。
他にも色々ありますが、まぁこのぐらいを設定すれば十分でしょう。さらなるオプション は lib/webrick/config.rb を読んで調べてください。なぜソースを読めというのかって?
ドキュメントが無い
からだよっ!
さて、これだけだと、ただ丸投げするだけの HTTP プロキシサーバです。今回作 りたいのはコンテンツフィルタですから、こっからが本番です。
で、コンテンツフィルタなんてものを作るための機能が WEBrick にあるのかなー?と 言うと……あるんですねー。その名も__ハンドラ__です。パンドラでもゴンドラでもあ りません。ハンドラです。こいつを使います。
ハンドラはこんな感じで書きます。
Proc を使うんですね。で、この手続きを
こんな感じでプロキシサーバオブジェクトに設定してやれば、通信が行われた時に call されます。
さて、ハンドラの中身です。引数として、req と res というのが渡されてますね。req と res ってのは WEBrick::HTTPRequest と WEBrick::HTTPResponse のインスタンスです。
WEBrick::HTTResponse の方をイジれば、ダウンロードしてきたコンテンツを改変できま す。
ハンドラはデータのダウンロードが終了した後に呼ばれるので、リクエストをイジること は出来ません。WEBrick::HTTPRequest の方をイジっても無意味です。無視されます。そ して悲しい気分になれます。イジっても実害は無いので、悲しい気分になりたい方はイジっ て頂いても構いません。2
この二つにはいっぱいインスタンス変数がついてます。それらをイジって、色々遊べるわ けです。
でまぁ、それっぽいインスタンス変数の一覧をあげてみました。大体名前でわかるでしょ。 うん。そゆことでよろしく。
詳しい中身については、次の章で説明します。
では、いよいよ実際にフィルタを書いてみましょう。
さて、どんなフィルタをかけるのか、というところが問題になってくるわけですが、 Proxomitron のフィルタでも、一番需要が多いのが、広告カッターです。ウザい広告を 取り除きたい、のですよ。そういうことで、今回は広告カッターを例題として取り上げま す。
さて、どこの広告をカットするか、ということが問題になってくるわけですが、今回は皆 大好き、スラシュドットジャパンの広告をカットしてみましょう。
まずは、http://slashdot.jp/ を開いて、ソースを表示します。すると、いかにも広告っ ぽい部分があります。
という部分です。広告は英語で ad ですから、きっとここが広告です。
では、この部分をカットするハンドラを定義しましょう。
簡単そうなコードですね。順番に見ていきましょう。
さて、もう一度確認すると、req は WEBrick::HTTPRequest のインスタンス、res は WEBrick::HTTPResponse のインスタンスです。
req.host には、リクエストされたホストの名前が入っています。今回はslashdot.jp の 広告を抜きたいので、それにマッチするかを調べています。
res#[] には相手サーバの返してきた HTTP ヘッダが入っています。ここでは、’ content-type’ をキーとしたときに帰ってくるものが text/html であるかどうか、を調 べています。
まとめると、「slashdot.jp に対するリクエストで、コンテンツのタイプが html のもの」 の時、この条件は真になります。
さて、if の中身です。res.body には、向こうのサーバから帰ってきた本文が含まれてい ます。これは書換え可能データなので、容赦なく書換えてしまって構いません。今回は、 広告部分にマッチする部分をカットしています。
これまでの成果をまとめると、次のようになります。
これを、slashdotfilterproxy.rb とでも名前をつけて保存してください。そして、コマ ンドラインから、おもむろに
と打つと、
と表示されて、サーバが起動します。
あとはブラウザのプロキシ設定を、自作のプロキシに向けるだけです。
http://slashdot.jp/ にアクセスすると、上部の広告がカットされている筈です。
解けた方は私にメールで送ってください。宛先は tokuhirom at yahoo dot co dot jp で す。
この記事では、WEBrick を使って簡単なプロキシサーバの作り方を紹介しました。
フィルタを Ruby で自由に書けるので、可能性は無限大です。皆さんも自分でフィルタ を書いてみてください。
日常的に使うものなので、使っているうちに改良したい点が出てくると思います。Ruby が上達するコツは頻繁にプログラムを書くことですから、よい練習になると思います。
宿題にも挑戦してください。ポイントは文字コードの扱いです。UTF-8 なページを 「にょ。」な語尾に変換できない、なんてのは、ありえません。
自称、日本で一番モンキーレンチが似合う男。
日々TokuLog! で毒を吐くも、本当は「いい日記」を目指したいと思っている。
ご意見・要望・バグレポートは tokuhirom at yahoo dot co dot jp まで。