Webalizer に処理させる「検索語」と「リファラ」の日本語コードを EUC-JP に統一する。
■ ■ ■
はじめに
Webalizer は,自前のウェブサーバのアクセス統計をまとめてくれるソフトである。以下では,統計情報に含まれる日本語を正しく表示するための方法について述べる。
「Webalizer の日本語化」と言ってしまうと2種類の解釈を許すことになり紛らわしくなるので避けるのがよい。解釈の1つは生成されたページのタイトルを日本語化するというもの。これは Webalizer のコンパイルオプションを指定することで実現できるが,中途半端な日本語になるため美しくないという問題がある (nlog(n): Webalizer のインストール)。Webalizer で日本語化したいのはタイトルではなく,「検索語」と「URLに含まれる日本語」なのである。これが解釈の2つ目である。
問題は,「検索語」と「URLに含まれる日本語」の日本語のエンコードが異なることである。これは実は Webalizer の問題ではなく,インターネット上の Web サイトが様々な漢字コードを使っていることに起因する問題である。
この問題については,Webalizerの漢字(日本語)コード統一 のスクリプトで対応することができ,このサイトでも使わせてもらってきた。しかし,最近アクセス数が増えたことで処理に時間がかかるようになってしまった。12か月分を解析するのに14時間くらいかかってしまう。そこで見直しをすることにした。
Webalizer のバージョンは 2.21-02 English,Webalizer の日本語出力コードは EUC-JP である。サーバの OS は Vine Linux 4.2。
デコード用スクリプト
参考にしたのは,webalizer用ログのデコード - lamanotramaの日記 の「simple」のコードである。「fast」のコードは,このままではいけない。Apache の combined のアクセスログでは,リファラがある場合とない場合とで検索語のフィールドが変わるからである。リファラのデコードに対応していないのも惜しい。やるのであれば,パターンマッチさせてから対象となるフィールドを絞るのがよい (Geekなぺーじ : アクセスログの読み方(apache combined logの場合))。
今回使うのは次のコードである。/usr/local/bin/webalizer-decoder.pl などに保存する。
#!/usr/bin/perl
use strict;
use warnings;
use URI::Escape::XS qw(uri_unescape);
use Encode qw(from_to);
my $line;
while (<>) {
$line = uri_unescape($_);
from_to($line, 'utf8', 'euc-jp');
print $line;
}
exit;
from_to は Encode パッケージの関数で,変換後の文字コードは 'eucjp' でも動作するがマニュアルに沿って 'euc-jp' とした (Encode - perldoc.perl.org)。URI-Escape-XS パッケージはインストールされていないことがあるので,なければインストールする必要がある。
スクリプトでは,入力用にダイヤモンド演算子「<>」を使っているので,標準入力か,なければ引数のファイル名から入力ができる (ダイヤモンド演算子 - Google 検索)。次のどの書式を使ってもよい。
$ cat access_log | webalizer-decoder.pl
$ webalizer-decoder.pl < access_log
$ < access_log webalizer-decoder.pl
$ webalizer-decoder.pl access_log
「< access_log webalizer-decoder.pl」という書き方はもしかして変態的?
Webalizer の定時起動
Webalizer を定期的に起動するには,cron に登録する必要がある。ここでは Vine Linux の流儀にしたがって,/etc/cron.* ディレクトリにシェルスクリプトを置いて起動することにする。それらのシェルスクリプトは /etc/crontab から呼び出される。Webalizer の処理ポリシーは以下の通りとする。
- 1日1回行う処理は1年分 (12か月分)
- 1時間に1回行う処理では当月分のみ
Apache のアクセスログは月毎に別ファイルとしておき,古い月は access_log.YYYYMM の形式でファイル名がつけてあり,当月は access_log というファイル名にしておく必要がある(nlog(n): Apache のログを月別に保存したい)。
1年分の処理 (1日1回)
1年分を処理するためのスクリプトは以前に書いているので (nlog(n): Webalizer のインクリメンタル設定を使い分ける),これに上の Perl スクリプトをかませるだけである。
次のスクリプトを /etc/cron.daily/00webalizer などに保存する。力技が炸裂。
#!/bin/sh
WEBALIZER="/usr/bin/webalizer"
OPTION="-i -c /etc/webalizer/webalizer.conf"
DECODER="/usr/local/bin/webalizer-decoder.pl"
LOGNAME="/var/log/apache2/access_log"
LOG="$LOGNAME.`date -d "$(date +%Y-%m-01) -11 month" +%Y%m`"
if [ -f $LOG ]; then LOGS="$LOGS $LOG"; fi
LOG="$LOGNAME.`date -d "$(date +%Y-%m-01) -10 month" +%Y%m`"
if [ -f $LOG ]; then LOGS="$LOGS $LOG"; fi
LOG="$LOGNAME.`date -d "$(date +%Y-%m-01) -9 month" +%Y%m`"
if [ -f $LOG ]; then LOGS="$LOGS $LOG"; fi
LOG="$LOGNAME.`date -d "$(date +%Y-%m-01) -8 month" +%Y%m`"
if [ -f $LOG ]; then LOGS="$LOGS $LOG"; fi
LOG="$LOGNAME.`date -d "$(date +%Y-%m-01) -7 month" +%Y%m`"
if [ -f $LOG ]; then LOGS="$LOGS $LOG"; fi
LOG="$LOGNAME.`date -d "$(date +%Y-%m-01) -6 month" +%Y%m`"
if [ -f $LOG ]; then LOGS="$LOGS $LOG"; fi
LOG="$LOGNAME.`date -d "$(date +%Y-%m-01) -5 month" +%Y%m`"
if [ -f $LOG ]; then LOGS="$LOGS $LOG"; fi
LOG="$LOGNAME.`date -d "$(date +%Y-%m-01) -4 month" +%Y%m`"
if [ -f $LOG ]; then LOGS="$LOGS $LOG"; fi
LOG="$LOGNAME.`date -d "$(date +%Y-%m-01) -3 month" +%Y%m`"
if [ -f $LOG ]; then LOGS="$LOGS $LOG"; fi
LOG="$LOGNAME.`date -d "$(date +%Y-%m-01) -2 month" +%Y%m`"
if [ -f $LOG ]; then LOGS="$LOGS $LOG"; fi
LOG="$LOGNAME.`date -d "$(date +%Y-%m-01) -1 month" +%Y%m`"
if [ -f $LOG ]; then LOGS="$LOGS $LOG"; fi
LOG="$LOGNAME"
if [ -f $LOG ]; then LOGS="$LOGS $LOG"; fi
$DECODER $LOGS | $WEBALIZER $OPTION - >/dev/null 2>&1
当月分の処理 (1時間に1回)
当月分は1時間に1回処理する。以下のシェルスクリプトを /etc/cron.hourly/00webalizer などに保存する。
#! /bin/bash
WEBALIZER="/usr/bin/webalizer"
OPTION="-p -c /etc/webalizer/webalizer.conf"
DECODER="/usr/local/bin/webalizer-decoder.pl"
LOG="/var/log/apache2/access_log"
if [ -s $LOG ] ; then
$DECODER $LOG | $WEBALIZER $OPTION - >/dev/null 2>&1
fi
まとめ
漢字コード変換のスクリプトを上記のものに変えたところ,速度的にはかなり改善され,14時間かかっていたものが2時間弱で終わるようになった (12か月分)。パターンマッチして,必要なフィールドだけデコードする方法は試していない。
関連記事:
Posted by n at 2010-10-05 21:52 | Edit | Comments (3) | Trackback(1)
トラックバックありがとうございます。
>「fast」のコードは,このままではいけない。Apache の combined のアクセスログでは,リファラがある場合とない場合とで検索語のフィールドが変わるからである。リファラのデコードに対応していないのも惜しい。
ご指摘の件について、実は業務で使っているコードではちゃんと処理しています。
とりあえずボトルネックになっている部分の高速化と言う意味であのエントリを書いた次第です。
近いうちに完全版も追記しておきますね。
尚、
Posted by: lamanotrama at October 05, 2010 22:34>from_to($line, 'utf8', 'euc-jp');
の部分は検索サイトによっては文字コードが異なっていたりするので、入力をutf8決め打ちにするのはあまり良くないです(大抵はいけますが)。この部分も完全版では解決しています。
すいません
Posted by: lamanotrama at October 06, 2010 00:01>ちゃんと処理してます。
は嘘でした。理由は当方のエントリに追記しております。
「完璧にやるのは、無理なので、速度との兼ね合いもみてベストエフォートでの処理にした。」って感じでしょうか。
lamanotrama さん
> 近いうちに完全版も追記しておきますね。
お待ちしております。
>> from_to($line, 'utf8', 'euc-jp');
Posted by: n at October 06, 2010 05:26> の部分は検索サイトによっては文字コードが異なっていたりするので、入力をutf8決め打ちにするのはあまり良くないです(大抵はいけますが)。
uri_unescape した時点で全部 utf8 になってしまうのだと思ってました。だから決め打ちになっているのかなと。