2004/03/20 土

Bulkfeeds を使って特徴的単語を表示

Bulkfeeds: Similarity Search を表示していると、ときどき「これどの単語でひっかけてんだよ」という謎の記事が関連付けられることがあります。記事一覧と一緒に関連付けに用いられた特徴的単語も表示できるといいなぁ、と思っていたら、特徴的単語を取得する API が用意されました。

blog.bulknews.net: Bulkfeeds: Similarity API 更新

Bulkfeeds の Similarity REST API で、どの単語が特徴的単語かを bulkfeeds:similarTerms というエレメントからとれるようにしました。 ... 単語だけほしいんだ!というひとは、similar.xml の方をつかってください。

早速 quick hack。similar.xml のほうを使って、エントリーの特徴的単語の表示する JavaScript を出力する CGI(ややこしい)を作ってみました。

JavaScript Include -> CGI -> Similarity API と少し遠回りしています。最初はプラグインを作って静的に表示させようと思ったんですが、リビルドが重くなるのがイヤなのと、最近の JavaScript Include 実験シリーズということでこんな形に。それに静的表示じゃ最新結果が表示されないし。

作成した CGI は以下のような感じです。指定された URL を引数に Similarity API を叩いて、レスポンスの XML データをパースし、表示させたい形に document.write する JavaScript を出力します。うりゃうりゃと適当に作りました(^^

#!/usr/bin/perl -w
 
use strict;
use FileHandle;
use CGI;
use LWP::Simple;
use XML::Parser::Lite;
use URI::Escape;
use Jcode;
 
# 初期設定
mkdir 'cache', 0755 unless -e 'cache';
my $q = new CGI;
my $url = $q->param('url') || '';
 
# XML読み込み
my $xml_url = 'http://bulkfeeds.net/app/similar.xml?url='.uri_escape($url);
my $cache = 'cache/'.uri_escape($url).'.xml';
my $xml;
# キャッシュがないかキャッシュ生成時から1時間経過していれば GET
if (!(-e $cache) || (-M $cache) > (1 / 24)) {
    $xml = LWP::Simple::get($xml_url)
        || do {print $q->header(-type => 'application/x-javascript'); exit};
    # キャッシュ生成
    my $fh = FileHandle->new($cache, 'w');
    if ($fh) {print $fh $xml; $fh->close;}
} else {
    $xml = do {local $/; my $fh = FileHandle->new($cache); <$fh>};
}
 
# XMLパース -> 配列格納
$xml =‾ s/\n//g;
my $parser = new XML::Parser::Lite;
our @terms;
our $tag;
$parser->setHandlers(
    Start => sub {$tag = $_[1]},
    Char  => sub {push(@terms, $_[1]) if ($tag eq 'term')},
    End   => sub {$tag = ''},
);
$parser->parse($xml);
 
# JavaScript出力
print $q->header(-type => 'application/x-javascript');
if (@terms > 0) {
    print "document.write('<div class=\"bulkfeedsSimilarityTerms\">');";
    print "document.write('特徴的単語: ');";
    for (my $i = 0; $i < @terms && $i < 5; $i++) {
        my $term = Jcode->new($terms[$i], 'utf8')->euc;
        print "document.write('<a class=\"similarTerm\" "
            ."href=\"http://bulkfeeds.net/app/search2?q="
            .uri_escape($term)."\">$term</a> ');";
    }
    print "document.write('</div>');";
}

自分専用に EUC-JP 固定& HTML タグ込みで出力してるので、使いたい方は出力部分を適当に変えて下さい。

これに適当な名前を付けて CGI として設置します。この CGI を JavaScript Include として呼ぶために、エントリーのテンプレートに以下のように記述します。

<script type="text/javaScript">
var url = '<$MTEntryPermalink$>';
var cgi = 'http://your.site/path/to/similar.cgi';
document.write('<scr' + 'ipt  type="text/javaScript" src="' + cgi
    + '?url=' + escape(url) + '"></scr' + 'ipt>');
</script>

結果は以下の通りです(エントリー直後は何も表示されません)。

各特徴的単語には Bulkfeeds: Search へのリンクを貼ってみました。これを Google とかはてなとかに関連付けても面白いかもしれません。terms.xml のほうを使ってエントリーの文章を POST し、文章中の特徴的単語にリンクをはるグローバルフィルタプラグインとかを作っても面白いですね。

こんな感じの適当 hack でしたが、わけわかんない単語が抽出されたりして割と面白いです。表示がちょっと重くなるが玉にキズ(^^;

■ 追記

読み込んだ XML のキャッシュをするようにしました。miyagawa 氏と相談して更新時間を1時間にしてあります。キャッシュの仕組みは mirror ではなく自前でやってます(^^;

■ 追記2

XMLパース -> 配列格納のところ、XML::Simple の XMLin() を使えば以下のように一行で書けますね(^^;) miyagawa氏に教わりました。てへ。

use XML::Simple;
...
# XMLパース -> 配列格納
my @terms = @{XMLin($xml, ForceArray => [ "term" ])->{term}};

■ 追記3

XML::Simple と LWP::UserAgent の mirror を使って、以下のように短く書き直しました。Cache::File も使おうと思ったけどとりあえず保留。勉強になるなぁ。

#!/usr/bin/perl -w
 
use strict;
use CGI;
use FileHandle;
use LWP::UserAgent;
use XML::Simple;
use URI::Escape;
use Jcode;
 
# 初期設定
mkdir 'cache', 0755 unless -e 'cache';
my $q = new CGI;
my $url = uri_escape($q->param('url'));
 
# XML読み込み
my $xml_url = "http://bulkfeeds.net/app/similar.xml?url=$url";
my $cache   = "cache/$url.xml";
# キャッシュがないかキャッシュ生成時から1時間経過していれば mirror
if (!(-e $cache) || (-M $cache) > (1 / 24)) {
    my $ua = LWP::UserAgent->new();
    $ua->agent("bricklife.similar/1.0");
    $ua->mirror($xml_url, $cache)->is_success
        || do {print $q->header(-type => 'application/x-javascript'); exit};
}
my $xml = do {local $/; my $fh = FileHandle->new($cache); <$fh>};
 
# XMLパース -> 配列格納
my @terms = @{XMLin($xml, ForceArray => ["term"])->{term}};
 
# JavaScript出力
print $q->header(-type => 'application/x-javascript');
if (@terms > 0) {
    print "document.write('<div class=\"bulkfeedsSimilarityTerms\">');";
    print "document.write('特徴的単語: ');";
    for (my $i = 0; $i < @terms && $i < 5; $i++) {
        my $term = Jcode->new($terms[$i], 'utf8')->euc;
        print "document.write('<a class=\"similarTerm\" "
            ."href=\"http://bulkfeeds.net/app/search2?q="
            .uri_escape($term)."\">$term</a> ');";
    }
    print "document.write('</div>');";
}

■ 追記4

アクセス過多による CGI 停止処理を受け、サーバへの負荷を考え設置をやめました。見たくなったら以下の「Powered by Bulkfeeds」をクリックすれば見れますしね。Bulkfeeds も重くなってきてるなぁ。

Posted by ooba at 01:35 | Comments (2) | TrackBacks (1) | このエントリーを含むはてなブックマーク
Trackbacks
TrackBack URL: http://www.bricklife.com/mt/mt-tb.cgi/181
エントリーの特徴的単語を抽出する
Excerpt: BulkfeedsのSimilarity Searchは Blog エントリに含まれる文章を形態素解析し、特徴的単語を抜き出して、それらの単語で Bulkfeeds の RSS Item 全文検索します。 というもので(最近とてつもなく重いけど:^^;)かなり楽しいアプリケーションなんですが、まま 「なんでこ†...
Weblog: はかいおうこうむてん
Tracked: 2004年7月 1日 06:13
Comments
1 : name: miyagawa date: 2004/03/20 03:06 [RES]

APIでもっていったレスポンス、一定時間キャッシュしてもらえませんかね。

2 : name: dzwonki polifoniczne nokia date: 2004/04/06 11:06 [RES]

Cool article!!!

Post a comment




Powered by Movable Type 4.0