September 28, 2003

Google2GD - Googleの検索結果数を追ってグラフ化

[ Perl , インターネット ]

積読状態だった『Google Hacks―プロが使うテクニック&ツール100選』を読んでみたところ、P.206 "[Hack #63] 検索結果数の長期的な変化を追う" というページが目に止まりました。

ウェブサービスを使って、特定の検索語にヒットするページが Google データベースに登録された日で絞り込んで、その件数を求めるという内容。Perl で書いたスクリプトから CSV を吐き出して、後は手作業で Excel でグラフ化する例が載ってたんですが、折角だからグラフ化もスクリプト任せにしてしまおうということで、ちょっと書いてみました。

試しに「"powered by movabletype" で引っかかる日本語のページ」という条件でスクリプトを動かしたら、こんなグラフになりました。

google2gd.png

「"powered by movabletype"」でひっかかるページが 9/22 日には 252 件登録された、という見方になります。(これにはちょっとした罠があります。)

何の役に立つかよくわからない微妙なグラフだったりしますけど。なんとなくグラフの色を Google 色にしてみました。深い意味はないです。;)

Google Hacks においては、例えば検索語「OS X Jaguar」に対して、 MacOS Jaguar の出荷日 (2003年8月24日) とかでグラフ化してやると、検索結果にピークが出て面白いみたいな例が載っていました。

ということは最近の大きな出来事で検索かけたらピークが出るかなあと思って「Googleツールバー 2.0」とか「FOMA ドラクエ」とか色々試してみたんですが、なぜかどれも 9/26 日のカウントがやたら跳ね上がって、9/27 が 0 件になってしまいました。ちょっと悩んで原因の予想はだいたいつきました。(後述)

Google検索には daterange: という構文があって、検索語と一緒に "daterange:2452905-2452910" とか追記して検索すると、Googleデータベースに登録された期間で検索結果を絞り込むことができます。daterange: 構文の引数には開始日と終了日をユリウス暦で指定します(2452905はユリウス暦で2003年9月22日、2452910は2003年9月27日)。この開始日と終了日を同じにすることで、ある一日にGoogleデータベースに登録されたページのみに検索結果を絞り込むことができます。

で、ウェブサービス(Google Web API)ではヒット件数も取得できるので、その二つを使えば日毎の登録ページ数が分かるという仕組みです。

例によって Perl で、SOAP::Lite で Google Web API にアクセス、GD::Graph でグラフ画像(PNG)を出力させています。

Google Web API のインタフェースは WSDL で取得します。WSDL は Google Web APIs Developer's Kit にファイルとして同梱されています。また、Google Web API は一日 1,000 回までという制限があり、ライセンスキー(無料)が必要です。Google Web API のサイトでアカウントを作ると、メールで送られて来ます。

#!/usr/local/bin/perl
# Google の検索結果数の変化を追ってグラフ化するスクリプト
#
# usage: google2gd.pl '本 "Google Hacks"' > /path/to/image.png
# options: -ja ... 日本語のページを検索
#          -filter ... 同一のホストからの結果は最初の2つだけに限る
#          -debug ... デバッグモード
#
# 参考: Google Hacks [Hack #11], [Hack #63]
use strict;
use warnings;
 
use Getopt::Long;
use SOAP::Lite;
use Time::JulianDay;
use GD::Graph::bars3d;
use Jcode;
 
use constant GOOGLE_KEY => 'your_google_key';
use constant GOOGLE_WSDL => './GoogleSearch.wsdl';
use constant TRUE => 1;
 
# オプション処理
my ($ja_flag, $filter_flag, $DEBUG);
GetOptions( ja => \$ja_flag, filter => \$filter_flag, debug => \$DEBUG);
my $lr = $ja_flag ? 'lang_ja' : '';
my $filter = $filter_flag ? 'true' : 'false';
 
# 引数処理
die "usage: $0  \n" if (not @ARGV == 2);
my ($query, $image_file) = @ARGV;
$query = Jcode->new($query)->utf8;
 
# ユリウス暦を求める
my $yesterday_julian = int local_julian_day(time) - 1;
my $start_julian = $yesterday_julian - 5;
my $end_julian = $yesterday_julian;
 
my $google_search = SOAP::Lite->service("file:" . GOOGLE_WSDL );
my (@date, @count);
 
# 日毎の検索結果数を求める
foreach my $julian ( $start_julian..$end_julian ) {
    my $full_query = "$query daterange:$julian-$julian";
 
    # Google 検索
    my $result = $google_search->doGoogleSearch(
                                                GOOGLE_KEY,  # key
                                                $full_query, # query
                                                0,           # start
                                                1,           # maxResults
                                                $filter,     # filter
                                                "",          # restrict
                                                "false",     # safeSearch
                                                $lr,         # lr
                                                "utf8",      # ie
                                                "utf8"       # oe
                                                );
    
    my $date = sprintf("%d/%d", (inverse_julian_day($julian))[1, 2]);
    my $count = $result->{estimatedTotalResultsCount};
 
    print Jcode->new($full_query, 'utf8')->euc, "\t$date ($count)\n" if ($DEBUG);
 
    push @date, $date;
    push @count, $count;
}
 
my @data = (\@date, \@count);
 
# グラフ
my $graph = GD::Graph::bars3d->new(200, 200);
$graph->set(
    title => 'Google2GD',
    x_label => 'Date',
    y_label => 'Results Count',
    shadow_depth => 5,
    bar_spacing => 3,
    values_space => 10,
    x_labels_vertical => TRUE,
    show_values => TRUE,
    cycle_clrs => TRUE,
    dclrs => [ qw( lblue red yellow lblue green red  ) ],
    shadowclr => 'lgray',
    accentclr => 'black' ,
    );
 
my $gd = $graph->plot(\@data);
 
local *IMG;
open IMG, "> $image_file" or die "cannot open $image_file. $!";
binmode IMG;
print IMG $gd->png;
 
exit (0);

ウェブサービスでアクセスして、日毎のヒット数を算出するところまでは、Google Hacks に載ってたスクリプトをちょっとだけ改造したものになってます。

このスクリプトを cron とかで回せば一応、日ごとにちゃんとグラフが最新になって、それをウェブで公開しとくみたいなことができるかなと思いますが、先に書いた問題があるので、直近のデータに関してはあまり役に立たないかも。

9/26 日の検索ヒット数がやたらに多くなってしまう原因はおそらくこうです。daterange: 構文では Google データベースに登録された日で検索結果を絞ることができますが、過去に一度登録されたことのあるページでも、再度登録された場合には、その時点での日付がデータベースに登録された日付になります。つまり、最終データベース登録日なんですね。(Google Hacks [#Hack 11] に書いてました。) また、別の言い方をすれば最後にロボットがデータを持ち帰った日ということになります。27 日は 0 件、すなわちまだデータベースが更新されていないことになるので、26 日がデータベースの最終更新日。26 日が大きな数字になるのはあたりまえ、ということなんじゃないかなと。

ここで言ってる 27 日ってのは日本時間だというのも、0件になるのにちょっと関係してると思います。

ちなみに daterange: による絞込みは Google が正式にサポートしているものではないそうで、妙な結果が出ても Google は苦情を受け付けない、だそうです。

Google Web API と言えば、Goodpic の数日前のエントリ 'Google web service APIの検索結果が少ない問題' にある話なんかも注意しないといけないですね。

Posted by naoya at September 28, 2003 03:37 AM | トラックバック (2)  b_entry.gif
トラックバック [2件]
TrackBack URL: http://mt.bloghackers.net/mt/suck-tbspams.cgi/474
daterange: の引数は「ユリウス日」であって「ユリウス暦」ではない
Excerpt: こういう間違いを見ると、訂正したくなる。
Weblog: Activity Memo
Tracked: December 8, 2003 03:35 AM
reality Blum asian idetic
Excerpt: Hi
Weblog: scythe 05/01
Tracked: January 5, 2006 07:01 PM
コメント [0件]