古い記事
ランダムジャンプ
新しい記事
今回の YahooHacks は、
長い文を検索キーとしてWeb検索をするというハックです。
一年前の検索会議[2005-09-30-3]で紹介しましたが、
今回ゼロから書き直しました。
サンプルコードが長くなってしまってすいません…。

(一時的にデモを置いておきます。そのうち消えます。ご了承ください。
http://chalow.net/misc/yahoohacks-samp/hack_sentence.cgi
検索例:SEOの10ステップと...←うろ覚えタイトル
)

使用している Web API の提供が終了となったため、現在動作しません。ご了承ください。



■■■長い文をそのまま検索キーとして Web 検索する

どこかからコピペしてきた長い文をそのまま Yahoo! で検索しても
ヒットしないことが多いです。

そういう場合でも何かしら検索結果を出すことを目指し、
文の中から名詞だけ取り出してAND 検索 (スペースで区切って検索) する、
というハックを紹介します。

■基本ロジック

まず入力された文をそのまま検索キーとして Yahoo! API で検索します(1)。
検索結果がゼロだったら、文を形態素解析し、名詞だけ取り出し(2)、
それらを並べて再度 Yahoo! API で検索します(3)。

例:
(1) 「著者名の一部から本を検索できる」で検索→見つからない。
(2) 形態素解析&名詞抽出→「著者」「名」「一部」「本」「検索」
(3) 「著者 名 一部 本 検索」で再度検索。

■コード

形態素解析には工藤拓さんの MeCab を使っています。
MeCab はオープンソースの形態素解析器で、Perl モジュールもあります。
MeCab のページ(http://mecab.sourceforge.jp/)から、
mecab, mecab-ipadic, mecab-perl の
3つの最新のパッケージを取得し、インストールします。

以下、前述のロジックで長文検索を行う CGI のコードです。

#!/usr/bin/perl -T
use strict;
use warnings;
use Encode;
use CGI;
use LWP::Simple;
use XML::Simple;
use HTML::Template;
use MeCab;

my $q = new CGI;
my $key = $q->param('key') || "";
my $new_key;

my $r = yapi_search({key => $key});
if (@$r == 0) {
    my $word = get_term_frequency($key);
    my @keys = sort {$word->{$b} <=> $word->{$a}} keys %$word;
    if (@keys > 0) {
        $new_key = join(" ", @keys);
        $r = yapi_search({key => $new_key});
    }
}

my $template = join("", <DATA>);
my $t = HTML::Template->new(scalarref => \$template,
                            associate => $q,
                            die_on_bad_params => 0);
$t->param(results => $r);
$t->param(new_key => $new_key);

print $q->header(-charset => 'UTF-8'), $t->output();

sub yapi_search {
    my ($args_ref) = @_;
    my $key = $args_ref->{key};
    my $num = $args_ref->{num} || 10;
    return [] unless $key;
    $key =~ s/([^0-9A-Za-z_])/'%'.unpack('H2',$1)/ge;
    $key =~ s/ /+/g;
    my $url = "http://search.yahooapis.jp/WebSearchService/V1/"
        ."webSearch?appid=YahooDemo&query=$key&results=$num";
    my $yahoo_response = get($url);
    my $xmlsimple = XML::Simple->new();
    my $yahoo_xml = $xmlsimple->XMLin($yahoo_response);
    if (ref($yahoo_xml->{Result}) eq "ARRAY") { # found: many
        return $yahoo_xml->{Result};
    } elsif (ref($yahoo_xml->{Result}) eq "HASH") { # found: 1
        return [$yahoo_xml->{Result}];
    }
    return []; # not found
}

sub get_term_frequency {
    my ($str) = @_;
    Encode::from_to($str, 'utf-8', 'euc-jp');
    my $m = new MeCab::Tagger("");
    my $n = $m->parseToNode($str);
    my %word;
    while ($n = $n->{next}) {
        if ($n->{feature} =~ /^\xcc\xbe\xbb\xec/) { # 名詞
            my $w = $n->{surface};
            Encode::from_to($w, 'euc-jp', 'utf-8');
            $word{$w}++;
        }
    }
    return \%word;
}

__DATA__
<html lang="ja">
<head>
<title>Sentence Search</title>
</head>
<body>
<h1>Sentence Search</h1>
<form method="get">
<input type="text" name="key" value="<TMPL_VAR name=key>" size="80">
<input type="submit" value="search">
</form>
<TMPL_IF name=new_key>
<p>Not Found: <TMPL_VAR name=key></p>
<p>New Search Key: <TMPL_VAR name=new_key></p>
</TMPL_IF>
<h2>Search Results</h2>
<TMPL_LOOP name=results>
<h3><a href="<TMPL_VAR name=Url>"><TMPL_VAR name=Title></a></h3>
<p><TMPL_VAR name=Summary></p>
</TMPL_LOOP>
</body>
</html>

yapi_search() は与えられたキーで Y!API で Web 検索する関数です。
検索結果をハッシュの配列のリファレンスとして返します。
ハッシュのキーは、Title, Url, Summary など XML のエントリと同じです。

get_term_frequency() は Mecab で形態素解析し、名詞だけを取り出し、
ハッシュに格納する関数です。ハッシュのリファレンスを返します。
Mecab の Perl モジュールで扱う日本語文字コードは EUC-JP なので、
UTF-8 との間の変換を行っています。

■Hack の実行

そのままWebサーバに置けば動作するはずです。
検索実行例を下図に示します。

画像



参考ページ:
- Yahoo!デベロッパーネットワーク
  http://developer.yahoo.co.jp/
- MeCab
  http://mecab.sourceforge.jp/
- Taku Kudo (工藤拓、MeCabの作者)
  http://chasen.org/~taku/

関連書籍:
Yahoo! Hacks