クラシック音楽へのおさそい〜Blue Sky Label〜


全文検索システムを作る(2)〜最初の一歩「検索フォーム」

 主要な検索システムの概要については以下のサイトで紹介されています。だいたいの雰囲気はつかめますから参考になると思います。
 ユング君も検索システムを構築するときは参考にさせてもらいました。


「全文検索ソフト(perl版)徹底比較」・・・http://homepage3.nifty.com/cinema1987/data/howto.htm

 「設置が比較的簡単なPerl版のスクリプトを取り上げます。個人の小規模サイトならこれで十分でしょう。ホームページに全文検索エンジンを設置したいと考えている人の参考になれば幸いです。」という紹介文の通りのページです。
 「いちいち読んでいる暇がない方は、結論をどうぞ。」という、とっても親切な作りにもなっています。(^^)

日本語全文検索エンジンソフトウェアのリスト ・・・http://www.kusastro.kyoto-u.ac.jp/~baba/wais/other-system.html
京都大学の馬場さんのページで、それぞれのシステムをもう少し専門的に検討したいときは参考になります。


ユング君が最初に選んだシステム・・・「検索フォーム」

「全文検索ソフト徹底比較」でも高く評価されているシステムです。CGIだけを使って簡単に設置できますし、機能的にも大変優れていますから最初はこのシステムにチャレンジしました。


 このCGIは、ご存知「とほほのWWW入門」に収録されています。

「とほほのWWW入門」・・・wsrch315.zip


 まずは圧縮ファイルを解凍します。
 「wwwsrch.cgi」・「wwwsrch.htm」・「jcode.pl」の3つのファイルと、設定のためのマニュアルとして「readme.htm」が展開されるはずです。このマニュアル用のファイルは実によくできていますから、ユング君の以下の駄文などを読まずにそちらを参照にされた方がいいかもしれません。(^^;

 さて、無事に解凍ができたら、愛用のテキストエディタで「wwwsrch.htm」を開いてみましょう。300行の足らずのスクリプトですが、はじめのところに以下のような記述があります。・・・(1)等というのはユング君が付け加えたものです。


$target_dir = '..'; # 検索対象ディレクトリ・・・(1)
$return_url = '../index.html'; # [戻る]ボタン
$sufix{".htm"} = 1; # 拡張子 .htm を検索する・・・(2)
$sufix{".html"} = 1; # 拡張子 .html を検索する
$how_many_lines = 2; # マッチした行の前後何行を表示するか・・・(3)
$do_logging = 1; # 検索キーワードのロギング


 言うまでもありませんが、最初のperlのパスは自分のプロバイダにあわせて変更しておきましょう。その次に設定を変更するのがこの部分です。
 (1)の検索対象のディレクトリの設定は少し複雑なので後回しにします。

 (2)の部分は、どのファイルを検索対象にするのかの指定で、デフォルトでは「html」「htm」両方ともに対象となっています。基本的にはこれで問題はないと思いますが、「.txt」タイプのファイルも検索対象にしたいときは以下の一文をここに追加します。
 $sufix{".txt"} = 1;
 これでテキストファイルも検索対象に追加されます。

 (3)は、このシステムのもっとも優れている点で、対象の文書を検索してくるだけでなく、適合したキイワードの前後数行を書き出してくれる部分です。その時に何行書き出すのかをここで指定します。
 デフォルトは2行ですが、もし5行程度書き出したいのなら、
 $how_many_lines = 5; と変更します。

 さあ、問題は(1)の検索対象のディレクトリの指定です。
 ここは、CGIを設置したディレクトリからの相対パスで指定します。
 ところが、初心者にはこの「相対パス」とか「絶対パス」というのが理解しづらいんですよね。でも、これはCGI世界の必要技術ですから、少し横道にそれますが、これを機会に少し詳しく解説しておきます。

相対パスと絶対パス

ある特定のファイルを指定するときに、ファイル名だけではパソコンは判断することができません。例えば、「a.gif」という画像ファイルを指定したいときに、「a.gif」と書いただけではどこの「a.gif」なのか途方に暮れてしまいます。
 そのため、パソコンの世界では二通りのやり方で場所を指定します。それが、「相対パス」と「絶対パス」です。

絶対パス
 分かりやすいのは「絶対パス」です。
 窓の世界なら、「C:¥kazou¥gif¥a.gif」というように指定するやり方です。
 住所にたとえれば「JAPAN 大阪府河内長野市***町☆丁☆番☆号」と書くやり方で、世界中のどこで投函しても目的の場所にたどり着きますし、表現方法はこれ一つです。

 ただ、ホームページにアップしたファイルの場合は、UNIXのディレクトリ構造にあわせて記述する必要があるので、窓の世界しか知らない人にはとまどいが大きいかもしれません。(例えば、UNIXには窓の世界のような「ドライブ」という概念がない、などなど)
 UNIXの時は最上位のルートディレクトリからの構造を書いていきます。例えば、「/home/usr/public_html/yun/」みたいな感じです。
 さらに、専用サーバーでも使っていない限り、ユーザーは指定された「public_html」ディレクトリより上の構造がどうなっているのかは、管理者に問い合わせない限り分からないという問題もあります。世間には上位のディレクトリ構造をさぐり出すツールもあるようですが、こういうことは管理者に聞いた方が早いでしょう。
 親切なプロバイダでは、public_htmlディレクトリの絶対パスを記述しているところもあります。


 注意
 絶対パスのことを「http://〜」というURLの事だと解説してあるサイトがありますが、これは厳密に言うと間違いです。リンクをはるだけならこれでも問題がないのですが、CGIのように、サーバー内のディレクトリ構造が求められるときは、「http://〜」で指定しても、パソコンは何のことなのか判断できません。ルートからのディレクトリ構造を書いてあげないとサーバーは認識しません。
 ただし、スクリプトの内容によっては「http://〜」でもかまわないときもありますが、そう言うときは「http://〜で指定しても可」などと記述してありますからコメントアウトの部分で確認してください。

相対パス

 しかし、通常ファイルの位置を指定するというのは、たとえてみればご近所に回覧板を届けるようなものですから、フルパス(絶対パスのことをこういうように表現することもあり)で指定するというのはちょっと大げさにすぎます。お隣に届けるのに「JAPAN 河内長野市・・・」と記載されているとかえって分かりにくくなります。
 そう言うときには、お届けをしようとしている人の家を基準にして場所を指定した方が分かりやすくて手間もかかりません。
 例えば、「自分の家から二軒となりの***さんへ!」というような具合です。
 こういう指定の仕方を「相対パス」とよんでいます

 そして、もう一つ知っておいて便利なのが、お届けをする人が住んでいる家のことを「カレントディレクトリ」とよぶことです。相対パスで指定するときは、このカレントディレクトリからの相対的な場所を指定することになります。
 これだと、public_htmlより上がどんな構造になっていようと関係がありません。たとえてみれば、「自分の家からn軒となりのAさん」と表現すれば、自分が日本に住んでいようがイギリスに住んでいようが、Aさんの場所は確実に特定できるのと同じです。

 ただ、問題なのは、この相対パスによる指定の仕方は、ちょっとしたルールを覚えておく必要があることです。
 絶対パスの時は、窓の世界ならドライブ名から順番に、UNIXならルートから順番に書いていけばすむわけですが、相対パスの時はそのように単純にはいきません。

 しかし、ルールは単純です。
 自分の家を基準にご近所の家を指定するには言葉はいくつ必要でしょうか、考えてください。
 答えは一つ、「おとなり」という表現だけで可能ですね。「3軒隣のBさん」なら「となりのとなりのとなりのBさん」で表現可能ですよね。
 ただ、パソコンの場合は階層構造がありますから、隣といっても「上」か「下」を指定する必要があります。そこで、パソコンの世界における表現法は下のようになります。言葉の意味は「おとなり」です。

 まず、カレントディレクトリを表示するときは「./」となります。・・・いってみれば自分の家です。
 上に行くときは「../」と表現します。・・・これは上の階にすむおとなり、ですね。
 下へ行くときは「ディレクトリ名/」です。・・・こちらは、下の階のおとなりです。
 これだけで、すべての相対パスを表示します。

 少し練習してみます。

 「./a.gif」は「カレントディレクトリにあるa.gif」と読みます。
 「../gif/a.gif」は「一つ上の階層のgifディレクトリの中にあるa.gif」と読みます。
 「gif/a.gif」は「カレントディレクトリ配下のgifディレクトリにあるa.gif」と読みます。
 「../../gif/a.gif」だとどう読みますか?分かりますね?
 
 では、今回の検索フォームのディレクトリ構造を参考にしながら具体的に練習してみましょう。
 ディレクトリ構造はだいたいこんなものでしょうか?


├[home]
 ├ [public_html]┐
  ├[wwwsrch]┐
  │ ├ wwwsrch.cgi
  │ ├ wwwsrch.htm
  │ ├ jcode.pl
  │ ├[gif]- a.gif、b1.gif..g.gif、h.gif (画像ファイル)
  │
  ├[yung]・・・(A)
  │├[meien]
  │├[szell]・・・(B)
  │├[syoukai]
  │ ├[bach]・・・(C)
  └ index.html


 まず、カレントディレクトリはどこでしょうか?・・・・ そう、CGIが設置されている「wwwsrch」ディレクトリですね。

 まずは小手調べに、wwwsrcf.htmを相対パスで指定するとどうなるでしょう?・・・「./wwwsrch.htm」ですね。意味は、「カレントディレクトリにあるwwwsrch.htm」と言うことです。
 カレントディレクトリの配下にある「gifディレクトリ」はどうなるかというと、「gif/」となりますね。
 このディレクトリにおさめられているa.gifは「gif/a.gif」となるわけです。
 下へ行くのは分かりやすいですね。

 次に上に行く場合を見てみましょう。
 カレントディレクトリの一つ上にある「public_htmlディレクトリ」はどうなるでしょうか?・・・一つ上のお隣ですから「../public_html」ですね。
 それでは、(A)の「yungディレクトリ」を相対パスで指定するにはどうしたらいいでしょうか?
 これは一つ上の階層にあがって、それから一つ下に下がるわけですから「../yung/」となります。
 yungディレクトリ配下にあるszellディレクトリならいかがですか?・・・そう、「../yung/szell/」となりますね。
 (C)の「bachディレクトリ」なら「../yung/syoukai/bach/」となるわけです。

 さらに、一番上の「homeディレクトリ」(こんなところを指定することはまずないでしょうが)だとどうなりますか?
 カレントディレクトリから見ると、お隣のお隣の「homeさん」ですから、・・・・「../../home」となりますね。
 分かりましたか?(^^;もしこれで理解できたら、あなたは天才です。分からない人はそれが普通ですから、じっくりと考えみてください。

 実は、ユング君が一番悩んだのがここの部分です。そして、以上のような理解の仕方で今まで何とか乗り切ってきましたが、もしかしたらこの説明は間違っているかもしれませんので(^^;、その時は指摘していただけると幸いです。

検索対象のディレクトリを指定する

 そこで、話を元に戻して、(1)の検索対象のディレクトリの指定の仕方を考えてみます。
 ここは道は二つに分かれます。検索対象のディレクトリが一つしかないときと、複数ある時です。

検索対象のディレクトリが一つの時

これは簡単です。
 $target_dir = '..'; # 検索対象ディレクトリ・・・(1)
 ここの’〜’のところに、検索対象にしたいディレクトリを「相対パス」で指定すれば終わりです。相対パスを理解していれば簡単ですが、そうでないとこれは大変です。上記の解説をじっくりと読み返してください。

検索対象が複数ある時

 こちらの方が一般的でしょう。検索対象のディレクトリが一つというのは少ないと思います。
 この時は、さきほどの、  $target_dir = '..'; # 検索対象ディレクトリ・・・(1)  という記述は無視して放置しておきます。
 変更するのは、ずーっと下の方、、「&search1($target_dir);」という記述の部分です。120行目あたりにありますから、やはり行数表示ができるエディタでないと不便ですね。

 ここを、次のように書き換えます。ユング君のサイトだと例えばこうなります。

&search1("../syoukai/");
&search1("../szell/");
&search1("../saisei/");
&search1("../rennkabann/");
&search1("../link/");
&search1("../meien/");

つまり、($target_dir)という部分に、検索したいディレクトリの相対パスを記入するわけです。ここでも、やはり相対パスが必要です。

 これで、CGIの設定は終わりです。
 後は、サーバーにアップしてパーミッションの変更をしておきます。(お約束ですね)

検索をしてみよう!


相対パスという概念を理解しているなら設置は実に簡単です。それでは、早速CGIを動作させて検索をしてみましょう。

 基本は、検索窓をつけたいページに以下のようなタグを貼り付けます。




AND
OR




 これは、検索窓をつけたいページがCGIと同じディレクトリにあるときです。もし違うディレクトリにあるページに窓をつけたいときは、そのページからのCGIの位置を相対パスで指定してください。(また、相対パスですよ・・・)
 自信がない人は、同梱されているwwwsrch.htmをアップしてそのまま使う方がいいでしょう。これでは愛想がないと言うときは、このHTMLファイルをカスタマイズする方が間違いが少ないと思います。

 または、CGIに直接リンクをはって、HTMLファイルを表示する部分を自分なりにカスタマイズするといいかもしれません。
 例えば、こんな感じです。→こちら・・・<検索はできませんのでご注意を!>
 だいたい以下のように書き直していますから、参考にしてください。


# HTML文書を書き出す
print "Content-type: text/html\n";
print "\n";
print "<HTML>\n";
print "<HEAD>\n";
print "<TITLE>ユング君のホームページ内を検索します</TITLE>\n";
print "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">\n";
print "</HEAD>\n";
print "<BODY TEXT=black BGCOLOR='#ffffd9' link=green vlink=red alink=navy>\n";
print "<center><H3>ユング君のホームページ内を検索します</H3></center>\n";
print "<center>ナマズさんのようにはCOOLではありません。参考程度にお使いください。</center>\n";
print "<center><FORM METHOD=POST ACTION=\"wwwsrch.cgi\">\n";
print "<NOBR>\n";
&jcode'convert(*word, $kcode_cgi, "euc");
print "<INPUT TYPE=text NAME=WORD SIZE=25 VALUE=\"$word\">\n";
print "<INPUT TYPE=submit VALUE=\"検索\">\n";
if ($FORM{'ANDOR'} eq "and") {
print "<INPUT TYPE=radio NAME=ANDOR VALUE=and CHECKED>AND\n";
print "<INPUT TYPE=radio NAME=ANDOR VALUE=or>OR\n";
} else {
print "<INPUT TYPE=radio NAME=ANDOR VALUE=and>AND\n";
print "<INPUT TYPE=radio NAME=ANDOR VALUE=or CHECKED>OR\n";
}
print "</NOBR>\n";
print "</FORM></center>\n";
print "<UL>\n";
print "<LI>このホームページの内容を検索します。\n";
print "<LI>複数の単語を入力する時はスペースで区切ってください。\n";
print "<LI>なるべくサーバーに負荷をかけないよう、あまり精密な検索は行っていません。(^^;\n";
print "<LI>たまに、誤った語句が検索されたり、語句が検索されなかったりすることがあります。(^^;\n";
print "<LI>サーバーエラーが頻繁におこるようでしたら、掲示板にカキコしてくれるとうれしいです。\n";
print "</UL>\n";
if ($return_url ne "") {
print "<A HREF=\"$return_url\">[戻る]</A>\n";
}
print "<HR>\n";


 みたいな感じでカスタマイズしてあります。
 ポイントは、「print」というコマンド以下の記述は行末までがそのままHTMLファイルに表示されると言うことです。それから、行末の「¥n」はHTMLのタグでは<BR>にあたります。その後ろについている「;」は、perlでは行末につける「お約束」で記号ですから消しては駄目です。
 ですから、HTMLのタグが分かる人なら何を意味しているのかはすぐに分かると思いますから書き換えても大丈夫だと思います。ですから、じっくりと観察して自分の気に入るようにカスタマイズしてください。


 そして、これで実際に検索をしてみるとこういう感じで検索結果が表示されます。
 「セル」という言葉で検索するとこういう感じです。→こちら・・・ただし、これはローカル環境での結果表示ですからリンクはしません。

 なかなか大したものだと思いませんか。これほど簡単に設置ができて、これだけの結果がでるのですからCGIというのは大したものだと思います。
 ただし、結果をじっくり見てみれば分かると思うのですが、「アムステルダム・コンセルトヘボウo. 」、「イッセルシュテット」、「セルジュ・チェリビダッケ 」なんてのもヒットしてしまうのがこの手のシステムの泣き所です。こういうのを「適合率が低い!」と言います。

 それからたまに、リンクの部分が上手く表示されないときがあるそうです。その時は、検索してきたアドレスをサーバー環境に合わせて読み替える必要があります。どうするのかというと、「正規表現」というものを使います。
 230行あたりに(行数表示ができるエディタが必要ですね)、「# 表示するという記述があります。この下に次の一行を書き加えます。

$target =~ s|../cc/dd|http://xx.yy.zz/aa/bb|;

 基本的にはこれだけでOKだと思います。上手くリンクしないときはこの「おまじない」をしてください。

まとめ

始めて設置したときに、この検索結果が表示されたときは感動しました。正直言って、その時はこれで十分だなと思いました。
 ところが、一つだけ怖れていたことがありました。それは、注意事項の中でトホホ氏も指摘していたことです。

 「あらかじめ検索用のインデックスを作成しておくタイプではなく、検索の都度、全文を検索するタイプですので、設置は簡単ですが、Webサーバーには多大な負荷をかけてしまいます。多量のファイルを対象に検索を行う場合は、Namazu などのインデックス作成型検索エンジンのご利用をオススメします。」

 その時点でのユング君のサイトはファイル数が200を超えていました。最初に紹介した「全文検索ソフト(perl版)徹底比較」サイトでも、検索対象のファイル数は数十程度を想定していました。200を超えるボリュームではサーバーへの負荷が無視できないだろうと思って、管理者に問い合わせてみました。
 結果は、想像したようにかなり負荷がかかるようで、「できれば使用を遠慮していただきたい」との返信がかえってきました。予想はしていたとは言え、正直がっくりきました。

 結論から言うと、ファイル数が数百を超えるサイトでは、CGIを使った簡単なシステムは使えないと言うことです。
 ただ、この「検索フォーム」は、CGIを使うもののなかではもっとも優れたものだと言えます。特に検索結果の表示画面はほれぼれするほど立派です。
 ファイル数が数十程度のサイトなら十二分に実用になると思います。

 しかし、ユング君程度のサイトだとインデックスファイルを作成するタイプを使う必要があることがわかりました。いつまでもガックリときているわけにはいきませんから、次に取り組んだのが「Splendid Search」です。
 これはインデックスファイルを作成するタイプではあるのですが、namazuほど複雑ではないのでユング君には手頃かなと思った次第です。 このシステムの概要について次は解説していきます。namazuへの道は遠い!