xdoc2txtを使用しないテキスト抽出

勤め先の部門内ファイルサーバで、ずっとHyperEstraierによる検索システムを使用していたのですが、ここしばらく止めてしまっていました。

停止した直接のきっかけはサーバのクラッシュだったのですが、検索システムの再稼働を先延ばしにしていたのは、インデックス更新処理が止まらなくなるトラブルが起きていたからです。

インデックス更新処理トラブルの現象

現象としては、

  • ある特定のPDFファイル(とりあえず2つだけ)のxdoc2txtによるテキスト抽出処理が、CPUを使いきったまま停止しなくなる

というものでした。
問題が生じるのは数百GByteは消費しているファイルサーバ中のたった2つのファイルですが、何しろ止まらないので、夜間にインデックス更新させようとしても翌朝更新処理が終わっていないことになります。
気付いた時点でxdoc2txtを強制終了するのですが、今度はそこから残りのインデックス更新がはじまってしまいます。Hudsonによる定期ビルドも同じマシンでやっているので、高負荷になる処理の終了時間が読めなくなるのは大変にまずいです。

xdoc2txtを使用しないテキスト抽出

とりあえず該当のPDFファイルを(使ってなさそうだったので)ファイル名を変えてestcmd gatherが見に行かないようにしていましたが、今後同様のファイルが出ないとも限りません。できれば、よりトラブルが出にくいと思われる方法でテキスト抽出をするように変更したいところです。

そこで、第16回 テキスト情報の抽出[その3]:検索エンジンを作る|gihyo.jp … 技術評論社で触れられているWindows Desktop Searchが使用しているIFilterを使用することにしました。IFilterコンポーネントを使用すれば:

  • Office用のIFilterはMicrosoftから提供されているので、Officeデータファイルのテキスト抽出については安定した動作が期待できる
  • Windows環境での標準のテキスト抽出I/Fといえるので、ほかのフォーマットについても豊富にフィルタが供給されることが期待できる

といったメリットが考えられます。

インデックス更新処理バッチコマンドの変更

IFilterの実体はCOMのインターフェイスなので、本来の手順としては、処理対象のファイルに対応するフィルタをアクティブにし、IFilterインターフェイスのメソッドを適切に呼び出す必要があります。

しかし、単にテキスト抽出をするだけであれば、上記のURLで触れられているIFilterのデバッグ用ツールである、filtdump.exeが使用できます。

filtdump.exeは、MicrosoftWindows Search SDKパッケージ(http://www.microsoft.com/downloads/details.aspx?FamilyID=645300AE-5E7A-4CE7-95F0-49793F8F76E8&displaylang=enからダウンロード可能)に含まれています。
使い方は、

filtdump.exe -b -o <出力ファイル名> <入力ファイル名>

のようにして呼び出せば、<入力ファイル名>のフォーマットに応じたフィルタを呼び出し、<出力ファイル名>に抽出されたプレーンテキストのみを書き出してくれます。

このとき、<出力ファイル>はUTF-16LEで書き出されるので注意が必要です。

また、filtdump.exeはほかにPlatform SDKVisual Studioに含まれるものなど、いくつかのバージョンがあるようですが、比較的新しい、上記のWindows Search SDKに含まれるバージョンでは、<入力ファイル名>のパスが日本語文字列を含んでいると問題が起きる(フォーマットから対応するフィルタの決定に失敗する)ようです。

今回はestcmd gatherから呼び出すだけで、estcmdはフィルタに引き渡すファイルをデータベースフォルダ内に一時的にコピーしてそのパスを渡すので、HyperEstraierのデータベースを日本語を含まないパス内に配置しておけば問題になりません。

実際の使い方として、私はHyperEstraier付属のestxfilt.batから次の内容のestifilt.batを作りました。

@echo off
filtdump.exe -b -o %2 %1

そして、インデックス更新バッチコマンドの方は、estxfilt.batの代わりに上記を使用するように、

estcmd gather -fx .pdf,.rtf,.doc,.xls,.ppt T@estifilt -fz -ic UTF-16LE -pc CP932 -sd -cl -cm [catalog] [directory]

としました。MS-Officeの各ファイルとPDFに対してestifiltを使用し、その出力の文字コードはUTF-16LEとして扱います。

PDFファイルについてはまだ問題が...

以上の様に変更してテストをしてみたのですが、問題のPDFファイルの一つについては、IFilterで処理しても本文部分が文字化けしてしまい、内容の抽出がうまくできませんでした。

元のPDFファイルが部分的に異なるエンコードでも使用しているのか、原因はわかりませんが、少なくとも停止してしまうということはなくなったので、とりあえずよしとすることにします:-P*1

2010-01-06追記

この記事については、コメント欄でいろいろと教えていただきました。通りすがりさん、A-chanさん、ありがとうございました。
年を越してしまいましたが、本文の方にまとめておきます。

・xdoc2txtの更新

まず、IFilterを試すきっかけになった、xdoc2txtのトラブルですが、xdoc2txtは結構頻繁にアップデートされています。更新履歴を見ると、

・破損したPDFで異常終了するケースを修正 
・破損したPDFで無限ループになるケースがあるのを修正 

などとありますから、今回のようなトラブルではまず、xdoc2txtだけでもアップデートしてみるのがいいでしょう。
ただ、今回問題になった2つのPDFファイルでCPU 100%のまま終了しなくなる現象は、Ver.1.35でも変わりませんでした。

・PDF用のフィルタプログラム

IFilterによる処理で文字化けするケースについては、A-chanさんにご指摘いただいたように、フォントのサブセットの埋め込みによって起こる現象のようです。本文の適当な文字列をコピーしても文字化けしてしまいますが、それを検索欄にペーストすると、正しく元の文字列にマッチします。

フォントのサブセットを埋め込む際に、元の文字コードの情報を完全に落とすというのもおかしな実装のような気もしますが、標準のPDF用IFilterをfiltdump.exe経由で使用しているだけではどうしようもないようです。

また、A-chanさんからは、このときのfiltdumpの挙動などから、PDFについてはpdftotextを使った方がよいのではないか?との情報をいただきました。今度試してみようと思います。

・0x5cワークアラウンド入りhyperestraier-1.4.13-win32

こちらの記事でとりあげたestcmd.exeのディレクトリスキャンルーチンが0x5cで終わるフォルダ名をうまく扱えない問題のワークアラウンドパッチ入りバイナリは、こちらからダウンロードできます

記事中のパッチを当てて再ビルドしただけですが、再ビルドの過程でなにか問題が混入してしまっている可能性もありますので、あくまで非公式ビルドということでご利用ください。また、公式パッケージに含まれるruby/Javaバインディングは取り除いてあり、クローラ関係は全くテストしていません。

また、パッチによらずにディレクトリスキャンを別のコマンドで実行する方法ですが、

cmd /c "for /R \\server\share %i in (*.*) do @echo %i" | estcmd gather casket -

とかでいいのでは、と思っていたのですが、、、うまく動きませんでした。今度調べてみます。。。

Namazuとの比較について

本記事の内容とは離れてしまうのですが、コメント欄でA-chanさんにNamazuとHyperEstraierの比較について質問させていただきました(こちらの記事でNamazuに対するメリットを書きたかったためでした→id:aenomoto:20091227:1261932844)。

  • Namazuの辞書ベースのキーワード抽出が、辞書にない専門用語が多い文書では検索精度の低下を起こすこと
  • Namazuはインデクサがperlで実装されているが、これについても日本語を含むパス名に絡む問題があること
  • HyperEstraierの方がインストール〜設定が容易であること

などをコメント頂きました。ありがとうございます。

分かち書きN-gram法の検索エンジンの方式の違いが、普通に使うだけでも実感できる程度のものなのか気なっていたのですが(当然文書の内容にもよるでしょうが)、辞書にない単語が多い文書で効いてくるというのは確かに納得できるところです。

Namazuでも日本語パス名がらみの問題があるというのはちょっと意外でした(NamazuWindows環境も含めて、長い実績があると思っていたので)。ただ、perl文字コード関係は、5.6と5.8以降でも全く違うし、5.8以降でスクリプトUTF-8エンコードを使用してもパス名関係は問題が出るようですので、確かに難しいのかもしれません。

・リンクまとめ

最後に、記事中の外部サイトへのリンクをまとめておきます。

内容 URL
IFilterの簡単な利用方法 http://gihyo.jp/dev/serial/01/make-findspot/0016
Windows Search SDK (filtdump.exeを含む) [http://www.microsoft.com/downloads/details.aspx?FamilyID=645300AE-5E7A-4CE7-95F0-49793F8F76E8&displaylang=en
xdoc2txt本家 http://www31.ocn.ne.jp/~h_ishida/xdoc2txt.html
Xpdf本家 (pdftotext) http://www.foolabs.com/xpdf/download.html
0x5cワークアラウンド入りhyperestraier-1.4.13-win32 http://www.hi-ho.ne.jp/a-enomoto/hatena/hyperestraier-1.4.13-win32.zip

*1:フォントの埋め込みによって、元の文字コードの情報がなくなっている(?)ようです。コメント欄と下の追記を参照。