2015年9月27日

squidログから検索クエリーのテキスト解析

 

1.squidログのログ・クレンジング

 ログのクレンジングにはいろいろな方法がある。試行錯誤で次のようなログのクレンジングを行った。

 

1.1 squidログの読み込み

 ログの項目数が正しいこと、転送バイト数がゼロでないこと、リクエストメソッドが所定のものに合致していること(基本はGETとPOST)、ポート443の通信でないこと、ステータスコードが200であること、コンテンツタイプが”text/xxxx”または”-”であることを条件に読み込みを行った。

 

1.2 QueryStringのデコード

 GET文字列の中で、エンコードタイプが明示されているものもあれば、エンコードタイプがないものもある。検索クエリーでエンコードがきっちりできなければ、何を検索しようしているか、全く分からなくなる。いろいろ挑戦するが中々うまくエンコードできない中、”エンコード時の文字コードが不明なURLをJavaでデコード”(http://blog.grush.jp/?p=20)のサイトに従い、QueryStringのエンコード問題を解決した。

 

1.3 どこを対象とするか

 今回はクエリーの収集・解析であるが、対象とするサイトやそのサイトの検索パラメータが分からない。少し古いが、“検索エンジンのURL“(http://www.geocities.jp/hima_hibi/surl.html)のサイトでは、いろいろな検索サイトが紹介されていて、これをベースに、更に収集ログの目視により調査を行った。これらの調査を元に、”http://・・・/search?・・・”、”http://・・・/Suggestions?・・・”などの持つURL検索サイトをクエリー解析の対象とした。これらのurlの中で、”q=”、”p=”、”qu=”、”qry=”、”query=”、”keyword=”などに続く文字列がQueryStringである。尚、”/search?”で始まる次のようなサイトは対象外とした。

http://www.navitime.co.jp/transfer/search?orvStationName=東京&dnvStationName=名古屋・・・

 

1.4 やっかいな話

 日本語変換中のインクリメンタルサーチの検索では次のようなクエリーが流れる。これらは日本語変換中のもので、時系列的には同一のものである。変換中のそれぞれを検索ワードとするのはおかしく、検索クエリーとしては、最後のもの以外は検索クエリーとすることはできない。

 インクリメンタルサーチ型検索の例

              http://suggestqueries.google.com/complete/search?hl=ja&qu=wifi

              http://suggestqueries.google.com/complete/search?hl=ja&qu=wifis

              http://suggestqueries.google.com/complete/search?hl=ja&qu=wifiせ

              http://suggestqueries.google.com/complete/search?hl=ja&qu=wifiせt

              http://suggestqueries.google.com/complete/search?hl=ja&qu=wifiせつ

              http://suggestqueries.google.com/complete/search?hl=ja&qu=wifiせつz

              http://suggestqueries.google.com/complete/search?hl=ja&qu=wifiせつぞ

              http://suggestqueries.google.com/complete/search?hl=ja&qu=wifiせつぞk

              http://suggestqueries.google.com/complete/search?hl=ja&qu=wifiせつぞく

              http://suggestqueries.google.com/complete/search?hl=ja&qu=wifi接続

 

1.5 QueryStringに対する前処理

 日本語にはいろいろな”揺らぎ”があるため、多くの日本語解析ではテキストの前処理が成されている。今回QueryStringの解析に当たり、次のような前処理を施した。

(1) 日本語のノーマライズ

              日本語解析の最初の前処理として、次のような日本語のノーマライズを行う。

              ・スペースの半角化、連続スペースを1つにする。

              ・複数ある長音記号を”ー”に統一、並びに連続長音を1つにする。

              ・同様にダブルクォート、カンマも同様に対応する。

              ・英数字に挟まれた長音はハイフンに変換する。

              ・全角英数字、記号の半角変換及び英文字の小文字化をする。

              ・半角カタカナを全角カタカナに変換する。

 

(2) 漢字、カタカナのひらがな変換

日本語を、”Kakasi”(http://kakasi.namazu.org/index.html.ja)を用い、ひらがなに変換する。

 

(3) Hebon変換

ひらがな文字列をヘボン式英字列に変換する。

上記のインクリメンタルサーチ型クエリーは次のような入力ストリームとなる。

              wifi

              wifi s

              wifi se

              wifi set

              wifi setsu

              wifi setsuz

              wifi setsuzo

              wifi setsuzok

              wifi setsuzoku

              wifi setsuzoku

 

(4) 文字列比較

同一IPで、事前のクエリーq0と、次に来たクエリーq1を比較し、次の判定式に従い、包含関係から”大”なものを最終的なクエリーとして採用する。比較関数は、String.startsWith、String.endsWithを用いた。

                            q0 ⊆ q1  →  q0 = null、q0 = q1

                            q0 ⊃ q1  →  q1 = null

 

この処理を順次繰り返すと、変換過程の文字列はなくなり、最終的に“wifi 接続”がインクリメンタルサーチ型クエリーとして残る。

但し、Hebon変換では、じしん(jishin)、自信(jishin)、自身(jishin)、地震(jishin)など同じ読み文字列は、同じアルファベット列となる。

 

(5) 比較関数の改良

 ログを調べる中から、次のようなログを発見した。”ハンディクリーナー おすすめ”で検索した後、”おすすめ”の前から新しいインクリメンタルサーチを始めている例である。前述の比較関数では、包含関係はなく、異なる文字列となる。

 インクリメンタルサーチを行っている文字列では、1文字しか追加されないため、Levenshtein距離(編集距離)による判定を検討した。しかし汎用的に考えると、異なるところが1文字のみならず、複数個ある場合も考慮しなければならない。そこでunix流のdiffの流れを汲んでいるdiff.jar(http://aarkiton.blogspot.jp/2009/05/diff.html)を利用した。

 diff.jarを利用すると、文字列a、文字列bから成る文字列Aと、文字列a、文字列c、文字列bから成る文字列Bとの間で、文字列A ⊂ 文字列Bの関係を見出すことができる。

                            文字列A              文字列a 文字列b

                            文字列B              文字列a 文字列c 文字列b

 次の例では、Hebon式変換を経て、最終的クエリーとして”ハンディクリーナー コードレス おすすめ”を得ることができた。

              1401409079706 192.168.161.87 bing.com ハンディクリーナー おすすめ

              1401409080824 192.168.161.87 bing.com ハンディクリーナー kおすすめ

              1401409080824 192.168.161.87 bing.com ハンディクリーナー koおすすめ

              1401409081217 192.168.161.87 bing.com ハンディクリーナー ko-おすすめ

              1401409081217 192.168.161.87 bing.com ハンディクリーナー ko-dおすすめ

              1401409081334 192.168.161.87 bing.com ハンディクリーナー ko-doおすすめ

              1401409081524 192.168.161.87 bing.com ハンディクリーナー ko-dorおすすめ

              1401409081613 192.168.161.87 bing.com ハンディクリーナー ko-doreおすすめ

              1401409081817 192.168.161.87 bing.com ハンディクリーナー ko-doresおすすめ

              1401409082072 192.168.161.87 bing.com ハンディクリーナー ko-doresuおすすめ

              1401409103219 192.168.161.87 bing.com ハンディクリーナー コードレスおすすめ

              1401409103219 192.168.161.87 bing.com ハンディクリーナー コードレス おすすめ

 

(6) 形態素解析

              ・形態素解析はJava環境で動作する”Igo”(http://igo.sourceforge.jp/)を利用。

            ・1.5(1)のノーマライズ処理及び英字の小文字化の後、String.split(' ')により得られた文字列配列に対して、検索対象となり難い文字列の除外を行う。

                            数字列及び記号列の除外

                                          数字のみ、記号のみ、郵便番号、電話番号、IPアドレスなどの文字列を除外

                            1文字の英字、ひらがな、カタカナの除外

                            数詞+助数詞で表される数値の除外

                                          500円、千五百円、10代、40台、37度などの数値を除外

              ・英数記号列はキーワードとして採用する。

              ・日本語混じりのテキストは形態素解析し、名詞を中心にそれぞれをキーワードとして採用する。この処理で、助詞などのストップワードが除外できる。尚、ストップワード除外辞書を利用してないため、”これ”、”それ”などのキーワードとして採用される。

              ・キーワードの連結

                            ”接続エラー”、”データ削除”などは分離せず、連結操作を行った。

                            ”子供の病気”などで、連体化助詞”の”を挟む名詞も連結操作を行った。

              ・ダブルクォートや括弧で囲まれている場合、これらの削除を行った。

 

   形態素解析の例

1401423101943 192.168.161.71 yahoo.co.jp 【本日の一枚】この写真を出すか迷ったのですが、あまりに衝撃写真だったので(*´ω`*)ねこじゃらしをキャッチできなかった表情と姿がポイントです(笑)

                                          ↓  形態素解析を行うと。

                            本日

                            写真

                            衝撃写真

                            ねこ

                            キャッチ

                            表情

                            姿

                            ポイント

                            笑

 

2.ログの保存と集計

 ログの保存にはMongoDBを採用した。MongoDBはドキュメント指向のデータベースで、JSON形式のドキュメントが簡単に保存でき、JSONオブジェクトとして参照や変更も簡単にできる。JSONが分かるプログラマーにとって大変扱い易いDBである。

 今回。MongoDBの扱いに慣れていないため、単純なシングルノード構成とした。

 

2.1 squidのログ保存

 squidのログデータから、"time", "year", "month", "day", "hour", "duration", "clientAddress", "resultCode", "statusCode", "byte", "requestMethod", "url", "domain", "type"を読み込み、これらをJSONデータとしてMongoDBに登録した。squidログに加え、"year", "month", "day", "hour"は"time"から、"domain"は"url"から求めたもので、以後の検索・集計を簡便に行うためのデータである。

 

2.2 登録・集計スピード

squidログ(2014/4/10から5/9)1,700万件のMongoDB登録時間は、70分強(ノートPC)であった。

また、1,700万件の収集ログに対してMapReduce(本来aggregateを使うべきであろう)による12時台で、各ドメインへのアクセス数集計は、i5-3337U CPU, 1.8GHzのPC環境で、14分強の時間で終わった(総なめでこの速さ)。

 

3.結果

 多くの検索サイトが採用しているインクリメンタルサーチではタイピングの都度、クエリーが流されていて、クレンジングしなければ正しいログ収集ができない。今回、テキスト解析を行い、有効なクレンジングができた。

 

4.リソース

 利用ライブラリー

  mongo-2.10.1.jar

  commons-lang.jar

  kakasi.jar

  igo-0.4.5.jar

  kuromoji-0.7.7.jar

  diff.jar

 

 プログラム(querystring.zip)

  FileReadSquidQuery.java

  FileSearch.java

  HebonConvert.java

  LogString.java

  QueryString.java

  StringCompare.java

  StringUtil.java

  TokenizerModeSample.java

  TwoByteToOneByte.java

 

(記 大坂 哲司)