検索エンジンを作る

第14回テキスト情報の抽出その1]

全文検索エンジンは、文書ファイルからテキスト情報を抽出して、インデックスを作成することで、高速に全文検索を行えるようにするソフトウェアです。当然ながら文書ファイルからなんらかの手段でテキスト情報を抽出しない限り、次のインデックス作成の処理に移れません。今回から、一般の文書ファイルからテキスト情報を抽出するテーマを扱っていきます。

文書フィルタ

連載の第10回目にFINDSPOTではカスタマイズ性を向上させるために、文書フィルタという独立したプログラムを経由して文書ファイルからテキスト情報を抽出する構造になっていることを説明しました。独自のファイル型式を検索対象にしたいのならば、独自ファイル形式用の文書フィルタを用意すれば良いしくみです。

文書フィルタは引数で入力ファイル名と出力ファイル名を受け取り、入力ファイルを解析してテキスト情報を抽出し、UTF-8でエンコーディングされたFINDSPOTの所定のXML型式により、抽出したテキスト情報やプロパティの内容を出力します。文書フィルタのプログラムは実行できればシェルスクリプトでも、PerlやRubyなどのスクリプトでも、C/C++/Java等で書いたプログラムでも構いません。

FINDSPOTには、テキストファイル用の文書フィルタ、HTMLの文書フィルタ、IFilterというインターフェースを利用したMicrosoft Word/Excel/PowerPoint, PDF, 一太郎などのファイルを対象とする文書フィルタが付属しています。また、FINDSPOTを使う開発案件などでは検索対象となるXMLファイルの専用の文書フィルタを開発することもあります。それぞれについて、どのような構造で実現しているかについて説明していきます。

テキストファイル文書フィルタ

テキストファイルはテキストの実体なので、そのままではないかと思われるかもしれませんが、さまざまな文字セット・エンコーディング(以降文字コード)や改行コードに対応するとなると、意外と大変です。

FINDSPOTのテキストファイル用の文書フィルタでは、次の文字コードのテキストファイルに、コマンドラインから文字コード名を指定することで対応しています。

us-ascii
utf-7
utf-8
utf-16
utf-32
utf-16be
utf-16le
utf-32be
utf-32le
iso-2022-jp
euc-jp
Shift_JIS
cp932
EUC-JP-MS
iso-8859-1
iso-8859-2
iso-8859-3
iso-8859-4
iso-8859-5
iso-8859-6
iso-8859-7
iso-8859-8
iso-8859-9
iso-8859-10
iso-8859-13
iso-8859-14
iso-8859-15

複数の文字コードのテキストファイルが検索対象となっている場合にも対応できるようにするために、FINDSPOTのテキスト文書フィルタは日本語で使われる文字コードの自動判定機能も備えています。自動的に判定できる文字コードは次の通りです。

utf-8
utf-16
utf-16le
utf-16be
iso-2022-jp
Shift_JIS
euc-jp

文字コードの自動判定

文字コードの自動判定機能は、テキストファイルの情報から特定のエンコーディングルールで特徴的に現れるパターンや、固有の文字コード領域の文字が使われているか等を手がかりにして判定します。このあたりの日本語文字コードの検出に関しては、次の本の解説が参考になります。

『日本語情報処理』Ken Lunde著/春遍雀來、鈴木武生訳/ソフトバンク/ISBN4-89052-708-7

さらに、Unicodeの場合にはファイルの先頭にBOMというエンディアンを指定するための特殊文字が付いていることもあるので、これも大きな手がかりになります。

日本語の文字コードの自動判定はどうしても100%完全というわけにはいかず、時折誤判定することがあります。たとえばShift_JISとeuc-jpでは、お互いに重なった領域を文字コードの表現として利用します。テキストファイルの中に重なった領域の文字しか存在しない場合には、Shift_JISなのかeuc-jpなのかという特定は困難なため、文書によっては正確に判定できないことがあるのです。

たとえば、琥珀という文字列はeuc-jpでは16進表現では、E0 E8 E0 E1というバイト列になります。このバイト列は、Shift_JISでは琲珮という文字列に該当します。テキストファイルの本文がE0 E8 E0 E1というバイト列だった場合には、このファイルがeuc-jpなのかShift_JISなのかを区別するには、形態素解析を行うなど別の方法に頼らなければなりません。このあたりのロジックに関してはまだまだ研究の余地がありそうです。

ファイル情報

ファイルサーバ上に置かれたファイルの場合には、ファイルシステム上の属性である所有者情報や、ファイルの作成日付時刻の情報なども検索の手がかりになります。テキストファイル文書フィルタでは、所有者情報、日付時刻情報を取得して、文書プロパティの所有者情報、日付時刻情報として出力しています。このような文書プロパティは、所有者を指定した検索や、日付範囲を指定した検索に利用できます。

HTML文書フィルタ

HTML文書フィルタは、文字コードの判定と変換を行った後に、構文解析を行うという2つのステップで動作しています。

文字コードの判定と変換

HTML用の文書フィルタでは、テキストファイルと同様に文字コードの判定や、変換処理の機能が必要です。HTMLの場合には<head>のエレメントの中に次のような文字コードの指定表現があるので、これを手がかりにできます。

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

XHTMLの場合にも同様に、ファイルの先頭の次のような文字コードの指定を元に判定できます。

<?xml version="1.0" encoding="UTF-8"?>

また、Unicodeの場合にはファイルの先頭にBOMが付いていることもあるので、こちらも手がかりにできます。XMLファイルの文字コード判定に関しては、次の本に詳しく解説されています。

『改定版標準XML完全解説(上)』中山幹敏、奥井康弘著/技術評論社/ISBN4-7741-1186-4

文字コード指定が記述されていないHTMLやXHTMLの場合には、前述のテキストファイル用文書フィルタでのロジックで自動判定を行っています。

文字コードが判定できたら、ファイルの内容をUTF-16の内部表現に変換します。次にHTMLパーサで構文解析をしながら、テキスト情報を抽出します。HTMLはタグを単純に外してテキスト情報を抽出するだけではだめで、かなり構文に応じた処理が必要になります。

実体参照の展開

内部表現のUTF-16のイメージに変換した状態では、実体参照がそのままになっています。実体参照とは次の表のようなものです。

実体参照文字
&amp; &
&lt; <
&gt; >
&apos; '
&quot; "

HTML内には直接&などのいくつかの文字を記述できないために、これらを実体参照という表現に置き換えて記述します。実体参照で記述されたテキストは、元の文字に戻す作業を構文解析と同時に行っています。

タグ関する特殊処理

構文解析を行いながら、いくつかのタグについて特殊な処理を行っています。

まずは、headの要素中のtitleタグです。<title>と</title>で挟まれた文字列は文書のタイトルの情報なので、タイトルのプロパティとして出力します。

body要素から下のテキスト情報に関しては、script, styleタグに注意しなければなりません。HTMLに埋め込まれたJavaScript等のスクリプトは、次のように記述されます。

<script>
 // JavaScript等のスクリプト
 document.write("Hello!");
</script>

body要素から下のテキスト情報を単純に抜き出してしまうと、このようなスクリプトまで抜き出されてしまいます。当然、全文検索の対象としてはふさわしくない文字列なので(スクリプトだけ検索したいという特殊な用途もあるかもしれませんが⁠⁠、scriptのタグで挟まれた範囲は全て出力対象から外します。

同様の理由で、styleタグで挟まれているCSSの記述範囲も出力対象外とします。styleタグを認識しないブラウザのために、次のようにCSSの記述範囲を<!--と-->で挟んでコメントにすることが推奨されていますが、コメントとせずにCSSを記述し、body内にstyleタグを記述してしまう誤用への対応です(styleタグはhead内で使われることになっていますが、body内に記述される例も良く見受けられます⁠⁠。

<style type="text/css">
<!--
  H1 { color: blue; }
-->
</style>

リンク情報の抽出

HTMLの中に、次のようなリンクの記述が存在したとします。

<a href="....">だるま市</a>

このリンクは、周囲の文章が「だるま市」に関して言及していることを示しています。リンクの記述は、局所的なトピックを詳しく示している可能性があります。そこで、aタグで挟まれているテキストに関しては、本文のテキストとして抽出する以外に、リンク情報のプロパティとして抽出する機能を設けています。

検索時に、検索対象をリンク情報プロパティのみにして検索を行うことができ、目的の文書が意外とよく見つかります。

本文範囲の指定

gihyo.jpのようなCMS(コンテンツマネージメントシステム)では、本文以外に、タイトルやメニューバー、他の記事へのリンク、広告などが入ります。これらをすべて抽出して、全文検索を行うと、目次や関係のないページがヒットしてしまい、検索ノイズが紛れ込みがちです。

この問題に対処するために、本文範囲が指定されていれば、この範囲のみをHTML文書フィルタの出力とするような拡張機能を設けています。残念ながら本文範囲がどこかを明示するようなタグはHTMLでは規定されていないため、次のようなコメントで本文の箇所を示すことにしました。

<!-- CONTENTS:START -->
...本文...
<!-- CONTENTS:END -->

もちろん、この本文範囲を示すコメントは、一般的なルールではありません。CMSのシステム側に手を入れて、本文範囲を明示できれば、検索ノイズを大幅に減らすことができます。イントラネット等での全文検索システムではかなり有効な手段ではないかと考えています。

おすすめ記事

記事・ニュース一覧