私のメインサイトでは namazu 2 でサイト内全文検索ができるようになっている。namazu 2 は UNIX 環境日本語全文検索エンジンの定番になって久しいが,基本的に ISO-2022-JP, Shift_JIS, EUC-JP の日本語文字コードを前提としており,多国語文書には適さない。ロシア語に限って言えば,Unicode ロシア語文書も検索できるのだけど(JIS の智慧のおかげ)。
最近ではサイト内検索エンジンをブログ管理システムに任せるか,Google のドメイン指定の検索窓を設けることで,自らインデックス管理自体をしないですむようになっており,独自に検索エンジンを設置する必要性は薄くなって来ている。ちなみに,Google のドメイン指定で自分のサイト内だけを検索するための検索窓は,以下のようなコードを自サイトのページに埋め込めばよい。
<form method="get" action="http://www.google.com/search"> <input type="text" name="q" size="30" maxlength="255" value="" /> <input type="hidden" value="検索対象ドメイン" name="as_sitesearch" /> <input type="hidden" name="hl" value="ja" /> <input type="hidden" name="ie" value="UTF-8" /> <input type="submit" value="Google 検索" /> </form>
私は受信したメールを MHonArc でアーカイブし,パスワードで文書セキュリティを保護することで,Web でどこからでも自分のメールを見られるようにしている。このような用途では,Google を使うわけには行かず(非公開なんだから当然),どうしても独自に検索エンジンを設置しなければならない。
Unicode msearch
Katsushi Matsuda,毛流麦花両氏による msearch は Unicode 文書からインデキシングが可能である。よって多国語文書も本来の文字列で検索が可能である。そこで私も Unicode 版 msearch Ver. 1.52 を導入してみた。これで柔軟な多国語検索とメールアーカイブ検索が両立できるというわけだ。http:
msearch は,茶筌で日本語検索対象語を抽出してインデキシングを行う namazu とは異なり,おそらく n-gram で全文インデックスを抽出している。これは検索モレを防ぎかつ高速化を実現するのに有効な方法である。ただし,msearch は多国語文字を取り扱うことができるとはいえ,いわゆる語形変化に追随できるようなインデキシング解析をしているわけではなさそうである。Google なら,нести (「携える」という意味のロシア語不規則変化動詞不定形) を入力すると,несут (三人称複数現在) などの変化形をも検索できる。さすがである。
msearch は PDF, Microsoft Word, Microsoft Excel, Microsoft PowerPoint をインデキシングするためのフィルタを持たないため,そのままでは原則 HTML 文書だけを扱うことになる。私自身は .doc, .xls, .ppt ファイルをプライベートで扱うことはないので,まったく困らないのだが,PDF だけはなんとしても検索できるようにしたい。以下,PDF も取り扱うことができるように msearch 環境を調整するメモをしるしておく。環境は Mac OS X Snow Leopard (インデックス作成) 及び FreeBSD 8.1-
Unicode 版 msearch インストール
Unicode msearch のダウンロード,インストールは『サイト内全文検索エンジン ― Unicode版msearch』に毛流麦花氏による懇切丁寧な解説があるので,そちらを参照する。アーカイブを解凍し,cgi-bin/
私の場合,PDF 処理その他でサイトのドキュメント・ツリーを書き換えてしまうオペレーションが発生するため,Apache22 の公開エリアとは別に ~/var/webindex ディレクトリ (以下「ワークツリー」) を作成し,そのなかにサイト・ドキュメント・ツリーをコピーするとともに,msearch リソース・ディレクトリをも設置した。ここで検索インデックスをローカル作成(msearch では,Web ブラウザからインデックス作成操作が可能であるが,これを使わず,サーバ・ローカルの端末でコマンド操作によりインデックス作成を行うことを「ローカル作成」と呼んでいる)し,できたものを Apache22 の公開エリア /usr/
msearch のサイト用独自設定は default.cfg で行う。本稿の試行では,とりあえず set $home= の右辺を私のサイト URL に書き換えただけである。
PDF インデキシング
msearch で PDF ファイルのインデックスを作成するには,いくつか注意事項があり,少し工夫が必要である。そのままで PDF をインデックス作成対象に指定すると,インデックス・ローカル作成プログラム genindex.pl (本稿で「インデクサ」とあるのはこれのこと) は異常終了する。
PDF はテキスト変換した上でインデクサに掛けるのが基本である。UNIX X11 PDF ビュア XPDF のユーティリティ pdftotext で PDF — テキスト変換を行った上でインデキシング実行すればよい。このときテキスト化されたファイルを元の PDF ファイルと同じ名称にしておかないと,検索結果のリンクで当該ファイルを参照できないので,テキスト変換結果で元 PDF ファイルを上書きしておく必要がある。PDF 以外でも wvWare (Microsoft Word 用),xlHtml (Microsoft Excel 用),pptHtml (Microsoft PowerPoint 用) の各 UNIX ソフトウェアを利用し HTML ないしテキスト形式に変換することで,msearch インデキシングが可能である。
第二の注意点として,HTML / XML 以外のファイルに対して msearch は BOM で Unicode エンコードを判断していることがある。BOM がないと,Web 検索結果画面上の当該ヒットエントリの文字が化けてしまうのである。普通,UTF-8 でテキストファイルを作成するとき,BOM を付けたりしないので,インデクサに掛ける前に UTF-8 BOM (十六進コード "EFBBBF") をワークツリーのテキスト変換後ファイルの先頭に書き込んでおく。これは echo, cat コマンドなどで簡単にできるのだけれども,私は,PDF に混在した不要な制御コード文字を取り除く目的と合わせて,これを行う簡単なプログラム chkucntlchr を書いた。PDF 以外のテキストファイルも同様の処置が必要である。
以上の処理を自動で行うシェルスクリプトのコード例を以下に示す。これを含んで,msearch インデックス作成の全体シェルスクリプト例を最後に掲げてある。
# PDF format conversion $WRK=ワークツリー $UCK=chkucntlchr # 制御コード削除・BOM 付加ツール $STP=タイムスタンプファイル (前回実行時の日付属性をもつ空ファイル) echo "* Convert PDF to TEXT by pdftotext (XPDF)." cd $WRK for i in `find -L . -newer $STP -name "*.pdf"` do pdftotext -enc UTF-8 -nopgbrk $i $i.txt if [ $? -eq 0 ]; then echo "** pdftotext $i OK." $UCK < $i.txt > $i if [ $? -eq 2 ]; then echo "** $i is empty. Ignore." rm -f $i fi else echo "** pdftotext $i NG. Ignore." rm -f $i fi rm -f $i.txt done
また,chkucntlchr ツールの Perl コードは以下の通りである。
#!/usr/bin/perl -w # -*- coding: utf-8; mode: cperl; -*- # chkucntlchr # 2011(c) isao yasuda. # - delete words including control characters (U+0001--U+0020, U+007F--U+00A0) # - Add UTF-8 BOM (x'efbbbf') for msearch indexer # - Return code 0: normal; 1: suppressed; 2: imput empty; use strict; use utf8; binmode STDOUT, ":utf8"; my $flg = 0; my $lc = 0; my $utf8_bom = "\xEF\xBB\xBF"; # BOM for UTF-8 utf8::decode($utf8_bom); print $utf8_bom; while (<STDIN>) { chomp($_); $lc++; utf8::decode($_); my @line = split(/\s/, $_); foreach my $wd (@line) { if ($wd =~ /[\x{0001}-\x{0020}\x{007F}-\x{00A0}]/) { $flg = 1; } else { print "$wd "; } } print "\n"; } if (! $lc) { print STDERR "*** $0: input empty.\n"; exit 2; } if ($flg) { print STDERR "*** $0: suppressed control characters.\n"; } else { print STDERR "*** $0: no problem.\n"; } exit $flg;
segmentation fault 対策
msearch インデクサに PDF を食わせるに際して,もっとも悩んだのはテキスト化した PDF でも,インデクサが segmentation fault エラーで異常終了する場合があることであった。インデクサからコールされる indexing.pl に罠を仕掛けて調査したところ,問題が二つ判明した。
まず第一には,サイトのインデックス対象ファイル数が多いと,UNIX の ulimit のファイルオープン数の制限に引っ掛かり,異常終了してしまう。ulimit -a で制限値を確認し,インデックス対象ファイル数よりも open files の設定値が小さければ,ulimit -n 数値 で値をファイル数よりも大きな値に設定する。
第二には,msearch インデクサは正規表現 s 演算子によって HTML タグの除去処理を行っているが,これをすべてのファイルに適用しており,テキスト変換された PDF ファイル中の文字列如何によっては誤動作してしまう。私のサイトの PDF は,LaTeX 多言語文書を dvipdfmx で処理した生成物が多い。LaTeX フォント・パッケージの enc ファイル(エンコーディング定義ファイル)によってはヘンな文字の羅列になることがあり,これでタグ判定の正規表現がぶっとんでしまったらしい。indexing.pl 791 行目を,HTML / XML でないときは実行しないように,以下の改変を行うと,アボートしないようになった。
# $contents =~ s/<(?:[^"'>]|"[^"]*"|'[^']*')*>/ /g; # オリジナル $contents =~ s/<(?:[^"'>]|"[^"]*"|'[^']*')*>/ /g if ($html_xml); # 対策
XPDF 多国語化
XPDF pdftotext の多国語対応について簡単にしるしておく。FreeBSD Ports,Mac OS X MacPorts では XPDF Japanese port が用意されており,これをインストールすれば日本語 PDF については扱うことが出来るようになる。しかし,多国語 PDF はダメ。
XPDF のサイトでは中国語,ギリシア語などいくつかの言語設定追加リソース・パッケージが公開されている。これらを用いて多国語対応設定ファイルを作成する。以下のシェルスクリプトを実行すれば,パッケージをダウンロード・展開した上で,カレントディレクトリに xpdfrc を生成する。これを $HOME/.xpdfrc として格納する。次に,パッケージのリソース xpdf-japanese 等を XPDF 管理ディレクトリ (FreeBSD なら /usr/
#!/bin/sh # Download XPDF language packs WGET="wget -nH -nd " XPDFSITE="ftp://ftp.foolabs.com/pub/xpdf" # XPDFETC は xpdfrc があるディレクトリに変更する XPDFETC="/usr/local/etc" $WGET $XPDFSITE/xpdf-arabic.tar.gz $WGET $XPDFSITE/xpdf-chinese-simplified.tar.gz $WGET $XPDFSITE/xpdf-chinese-traditional.tar.gz $WGET $XPDFSITE/xpdf-cyrillic.tar.gz $WGET $XPDFSITE/xpdf-greek.tar.gz $WGET $XPDFSITE/xpdf-hebrew.tar.gz $WGET $XPDFSITE/xpdf-japanese.tar.gz $WGET $XPDFSITE/xpdf-korean.tar.gz $WGET $XPDFSITE/xpdf-latin2.tar.gz $WGET $XPDFSITE/xpdf-thai.tar.gz $WGET $XPDFSITE/xpdf-turkish.tar.gz # Expand archives for i in *.tar.gz; do tar zxvf $i; done # make xpdfrc cp $XPDFETC/xpdfrc . for i in `find . -name "add-to-xpdfrc"` do cat $i >> xpdfrc; done
インデキシング用シェルスクリプト
最後に私が自サイト用に作成したインデキシング用シェルスクリプトを掲載しておく。Mac OS X Snow Leopard 環境である。HTML ソース Subversion 管理エリアからワークツリーに更新ファイルをコピーし,ワークツリーでインデキシングしたのち,Mac 上の Apache22 cgi-bin 試験環境にインデックスファイル default.idx をコピーする。これでできた Mac 上の default.idx をそのまま FreeBSD サーバの msearch 環境に転送して公開するという運用形態である。
私は自サイトの HTML 等公開コンテンツを Subversion でバージョン管理しており,ソースを commit すると Subversion の commit スクリプトが動作して,自動的に更新コンテンツを Apache22 のドキュメント・ツリーにコピーするようにしている。このスクリプトを変更し,commit のタイミングで msearch インデクス作成スクリプトを呼び出して,検索インデクスを自動的に更新することもできる。crontab に登録して定時自動実行するのもよいと思う。
上記の PDF テキスト変換以外にも,HTML の iso-2022-jp to UTF-8 変換なども実行内容に含まれている。シェル内の各パス設定,サイト設定は私の環境そのままなので,もしこれを活用する方がいらっしゃるのなら,自分の環境に応じて書き換えないといけない。もちろん,中味をよく確認し,私のいい加減なコードにヘンなところがあれば手直しいただいたほうがよい。
#!/bin/sh # -*- coding: utf-8; -*- # msearch index generator for ISOLDE # - coded by isao yasuda, 1 Apr. 2011 # # DESCRIPTION # ----------- # 1.前回タイムスタンンプより新しいページをSRCからWRKに格納する。 # 2.JISコードのページをUTF-8に変換して格納する。(nkf, sed) # 3.PDFをテキスト変換してWRKに格納する。 # rc OK: .pdf の内容はテキストファイル # rc NG: .pdf, .pdf.txt を削除する # 5.テキスト変換 OK のものの制御コードを削除する。(chkucntlchr) # 4.WRKでインデックスを生成する。(genindex.pl # 5.インデックスをWRKからPUBに格納する。 # # CAUTION # ------- # 1.初期作成時はファイル数が多いのでWRKにsite treeをコピーしておく。 # # SRC: ページソースエリア # WRK: ワークエリア # PUB: 公開エリア (ここでは触らない) WWW=/usr/local/www/apache22 SRC=/home/isao/src/noxinsomniae PUB=$WWW/data CGI=$WWW/cgi-bin/msearch WRK=/home/isao/var/webindex/website MSE=/home/isao/var/webindex/msearch STP=/home/isao/var/webindex/stamp TMP=/home/isao/var/webindex/tmp UCK=$MSE/chkucntlchr NKF=/usr/local/bin/nkf PDFTOTEXT=/usr/local/bin/pdftotext echo "********************************************************" echo "* msearch Index Generation Start `date '+%Y/%m/%d %H:%M:%S.'` *" echo "********************************************************" # Copy SRC to WRK echo "* New Files Archiving." cd $SRC find -L . -newer $STP -type f | grep -v '.svn' |\ xargs tar cf - | ( cd $WRK; tar xvf - ) cd $WRK # Convert iso-2022-jp to utf-8 echo "* Convert ISO-2022-JP pages to UTF-8 pages." cd $SRC for i in `find -L . -newer $STP -name "*.html"` do $NKF -w $i |\ sed -e 's|charset=[Ii][Ss][Oo]-2022-[Jj][Pp]|charset=UTF-8|g' > $WRK/$i done # PDF format conversion echo "* Convert PDF to TEXT by pdftotext (XPDF)." cd $WRK for i in `find -L . -newer $STP -name "*.pdf"` do $PDFTOTEXT -enc UTF-8 -nopgbrk $SRC/$i $i.txt if [ $? -eq 0 ]; then echo "** $PDFTOTEXT $i OK." $UCK < $i.txt > $i if [ $? -eq 2 ]; then echo "** $i is empty. Ignore." rm -f $i fi else echo "** $PDFTOTEXT $i NG. Ignore." rm -f $i fi rm -f $i.txt done # add BOM to text files echo "* Add BOM to TEXT files." cd $WRK for i in `find -L . -newer $STP -name "*.txt" -or -name "*.tex"` do $UCK < $i > $i.tmp if [ $? -eq 2 ]; then echo "** $i is empty. Ignore." rm -f $i fi mv $i.tmp $i done # rm svn control cd $WRK find . -name ".svn" -or -name ".#*" | xargs rm -fr # Indexing echo "* Execute Indexing by genindex.pl. PARAM:" echo "* 1 インデックス名前: default" echo "* 2 インデックス対象DIR: $WRK" echo "* 3 インデックス対象URL: http://yasuda.homeip.net/" echo "* 4 インデックス対象拡張子: .html,.txt,.pdf,.tex" echo "* 5 非インデックス対象DIR: admin,common,css,archives" echo "* 6 非インデックス対象拡張子: (指定無し)" echo "* 7 非インデックス対象KWD: (指定無し)" echo "* 8 ランキング方法: 最終更新日時降順(1)" echo "* 9 alt属性の文字: 指定しない(0)" cd $MSE ./genindex.pl <<EOM default /home/isao/var/webindex/website http://yasuda.homeip.net/ .html,.txt,.pdf,.tex admin,common,css,archives 1 0 EOM if [ $? -eq 0 ]; then echo "* Index Generation Succeeded." ls -l $MSE/default.idx echo "* Now Copy default.idx to $CGI." cp -p $MSE/default.idx $CGI # STAMP modify SDT=`ls -l $STP` echo "* Previous Update: $SDT" touch $STP SDT=`ls -l $STP` echo "* Now Updated: $SDT" else echo "* Index Generation Something bad." ls -l $MSE/default.idx fi echo "* Procedure Ended. `date '+%Y/%m/%d %H:%M:%S.'`" # end of script