印刷用表示へ切り替え 通常表示へ切り替え 更新履歴を表示 更新履歴を隠す
LinuxPerl Webalizer 検索語の日本語コードの統一

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)
Trackbacks

  • 「手違いで複数トラックバックを送ってしまった!」という場合でも気にしないでください (重複分はこちらで勝手に削除させていただきます)
  • タイムアウトエラーは,こちらのサーバの処理能力不足が原因です (詳細は トラックバック送信時のエラー をご覧ください)
  • トラックバックする記事には,この記事へのリンクを含めてください(詳細は 迷惑トラックバック対策 をご覧ください)
[perl]webalizer用ログのデコード
今時webalizerを使っている人がどれだけいるのか知らないけど、仕事でデコードロジックを最適化する必要があったのでベンチマークを晒しておきます。 webalizerに食わせる前に、apacheログのURLをアンエスケープして、特定のエンコードに変換するというものです。 ちなみに Trackbacked from: lamanotramaの日記 at October 05, 2010 23:22
Comments

トラックバックありがとうございます。

>「fast」のコードは,このままではいけない。Apache の combined のアクセスログでは,リファラがある場合とない場合とで検索語のフィールドが変わるからである。リファラのデコードに対応していないのも惜しい。

ご指摘の件について、実は業務で使っているコードではちゃんと処理しています。
とりあえずボトルネックになっている部分の高速化と言う意味であのエントリを書いた次第です。
近いうちに完全版も追記しておきますね。

尚、
>from_to($line, 'utf8', 'euc-jp');
の部分は検索サイトによっては文字コードが異なっていたりするので、入力をutf8決め打ちにするのはあまり良くないです(大抵はいけますが)。この部分も完全版では解決しています。

Posted by: lamanotrama at October 05, 2010 22:34

すいません
>ちゃんと処理してます。
は嘘でした。理由は当方のエントリに追記しております。
「完璧にやるのは、無理なので、速度との兼ね合いもみてベストエフォートでの処理にした。」って感じでしょうか。

Posted by: lamanotrama at October 06, 2010 00:01

lamanotrama さん
> 近いうちに完全版も追記しておきますね。
お待ちしております。

>> from_to($line, 'utf8', 'euc-jp');
> の部分は検索サイトによっては文字コードが異なっていたりするので、入力をutf8決め打ちにするのはあまり良くないです(大抵はいけますが)。
uri_unescape した時点で全部 utf8 になってしまうのだと思ってました。だから決め打ちになっているのかなと。

Posted by: n at October 06, 2010 05:26
Post a comment
  • 電子メールアドレスは必須ですが,表示されません (気になる場合は「メールアドレスのような」文字列でもOKです)
  • URL を入力した場合はリンクが張られます
  • コメント欄内ではタグは使えません
  • コメント欄内に URL を記入した場合は自動的にリンクに変換されます
  • コメント欄内の改行はそのまま改行となります
  • 「Confirmation Code」に表示されている数字を入力してください (迷惑コメント対策です)


(必須, 表示されます)


(必須, 表示されません)


(任意, リンクされます)


Confirmation Code (必須)


Remember info (R)?