|
~ To be, or not to be, or to let it be. ~ null-i.net |
| Linux/Apache/アクセスログの数や国を見る | |
|
アクセスログの確認につかう、awk、perlのサンプル(2017-02-23、更新:2023-07-09) いつかは見ようと思ったアクセスログにようやく着手できました。 日ごとのアクセス件数の確認†まず、前提となる Apache のアクセスログの書式ですが "%h %l %u %t \"%r\" %>s(以下省略)" 私の環境では以下のように、自分が加工しやすいように変えていますので "%{%y%m%d/%H:%M:%S}t %h %u %>s (以下省略)"
そんなわけで、 # cat ./aclog_count.sh
#!/bin/sh
# アクセスログの場所を指定し、
# 今月の今日までのログを読み込む
ACLOG=/var/log/httpd/access_log*
min=1
ym=`date +%y%m`
max=`date +%d`
# 年月日指定する場合は以下をいじる
#ym=`date +%y11`
#min=1
#max=30
while [ $min -le $max ]; do
ii=`printf '%02d' $min`
echo "$ym$ii -----------------"
egrep "^$ym$ii" $ACLOG | \
grep -v "Googlebot" |\
awk '
BIGIN{
cnt200 = 0
cnt300 = 0
cnt400 = 0
cnt500 = 0
}
{
if($4 ~ /2[0-9][0-9]/){
cnt200++
}
else if($4 ~ /3[0-9][0-9]/){
cnt300++
}
else if($4 ~ /4[0-9][0-9]/){
cnt400++
}
else if($4 ~ /5[0-9][0-9]/){
cnt500++
}
}
END{
printf(" 200: %d\n", cnt200)
printf(" 300: %d\n", cnt300)
printf(" 400: %d\n", cnt400)
printf(" 500: %d\n", cnt500)
}'
min=`expr $min + 1 `
done
HTTPレスポンスについてざっくり説明すると、
上の例では「grep -v Googlebot」で無視するログを指定していますが、
こんなイメージで、
アクセスログから国の特定†流れとしては、各国のIP割り当てを調べて、 # wget ftp://ftp.apnic.net/pub/stats/apnic/delegated-apnic-extended-latest # wget ftp://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-extended-latest # wget ftp://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest # wget ftp://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-extended-latest # wget ftp://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest
#!/usr/bin/perl -w
# 現状はIPv4だけで足りそうなので、
# v6は必要になったら考えます
my %ip_range;
my %ip_cntry;
# まず、割り当て一覧を読み込む
# このツールと同じ場所に rir_listディレクトリを作って
# 以下のファイルを一式置いておく
# ftp://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest
# ftp://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest
# ftp://ftp.apnic.net/pub/stats/apnic/delegated-apnic-extended-latest
# ftp://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-extended-latest
# ftp://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-extended-latest
use File::Basename;
my $base_dir = dirname(__FILE__) . "/rir_list/";
@rir_lists = (
$base_dir . "delegated-afrinic-extended-latest",
$base_dir . "delegated-apnic-extended-latest",
$base_dir . "delegated-arin-extended-latest",
$base_dir . "delegated-lacnic-extended-latest",
$base_dir . "delegated-ripencc-extended-latest"
);
foreach my $r_list (@rir_lists){
open($list, "< $r_list") or die "$! [r_list]\n";
while(<$list>){
# 上記の、こんな感じのファイルを読みたい
# apnic|JP|ipv4|1.0.16.0|4096|20110412|allocated|A92D9378
if(/^[^\|]+\|(\w+)\|ipv4\|([\d\.]+)\|(\d+)\|/){
$ip=$2; $num=$3; $cnt=$1;
if($ip =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/){
$bin=$1*256*256*256
+$2*256*256
+$3*256
+$4; # IP表記から数値に戻す
$ip_range{$bin} = ($bin + $num);
$ip_cntry{$bin} = $cnt;
}
}
}
}
# 引数にアドレスを渡した場合は、国判定してすぐ終了
if($ARGV[0] =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/){
$bin=$1*256*256*256
+$2*256*256
+$3*256
+$4; # IP表記から数値に戻す
while(my ($start, $end) = each(%ip_range)){
if($start <= $bin && $bin <= $end){
printf("%s is %s\n", $ARGV[0], $ip_cntry{$start});
exit;
}
}
printf("%s is not found.\n", $ARGV[0]);
exit;
}
# 次にアクセスログを読み込みつつ
# IPv4 アドレス部 を 国名に変換する
my $file;
if($ARGV[0] eq '-') { $file = 'STDIN';}
else{ open($file, "< $ARGV[0]") or die "$! [$ARGV[0]]\n";}
$i=0;
while(<$file>){
my $hit = 0;
if(/^(.*[ \[\(:])(\d\d?\d?\.\d\d?\d?\.\d\d?\d?+\.\d\d?\d?+)([ \]\)].*)$/){
# アクセスログの書式に合わせて if 文内の正規表現を調整
# 上記の例だと、11.22.33.44 とか [11.22.33.44] が出てきたら無節操に変換を試みる
$pre=$1; $ip=$2; $suf=$3;
if($ip =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/){
$bin=$1*256*256*256
+$2*256*256
+$3*256
+$4; # IP表記から数値に戻す
foreach my $start (keys(%ip_range)){
if($start <= $bin && $bin <= $ip_range{$start}){
printf("%s%s[%s]%s\n",
$pre,
$ip,
$ip_cntry{$start},
$suf );
$hit++;
last;
}
$i++;
}
}
}
if(! $hit){
# 変換できない場合はそのまま出力
print $_;
}
}
これ(convert.pl)を使って、 # ./convert.pl /var/log/httpd/access_log | head あるいは標準入力から読込 # grep keyword /var/log/httpd/access_log | ./convert.pl - # lastb | head -100 | ./convert.pl - 国が英語二文字(JPとか)なので、それを調べるのがめんどくさい方は これで念願の、国別アクセス状況を知ることができました。 おまけ:URLの日本語デコード†Wikiで日本語のURLクエリが「%~」になりますが、nkf コマンドで日本語で戻せます。 echo "%E3%81%AE" |\
perl -e 'while(<STDIN>){ s/%([0-9a-fA-F]{2})/pack("H2",$1)/ge; print $_; }'
おまけ:検索ボットを数える†さきほど、grep -v で検索エンジンのアクセスは除外する、 以下、bot_count.sh # cat bot_count.sh
#!/bin/sh
# アクセスログの場所を指定し、
# 今月の今日までのログを読み込む
ACLOG=/var/log/httpd/access_log*
mm=`date +%m`
min=`date +%-d --date "1 day ago"`
max=`date +%-d`
# 月日指定する場合は以下をいじる
# 今回は 2月の統計をとりたいので、変数を上書き
mm="02"
min=1
max=28
regex="Googlebot|webmeup-crawler|bingbot|siteexplorer|msnbot|MJ12bot|Baiduspider|Yahoo! Slurp|Uptimebot|SemrushBot"
while [ $min -le $max ]; do
ii=`printf '%02d' $min`
echo "$mm$ii -----------------"
# 一行目の mm ii の部分はアクセスログの書式に合わせて整形する
egrep "^17$mm$ii" $ACLOG | \
egrep -i "$regex" |\
awk -v keyword="$regex" '
{
# 各キーワードごとの件数をカウントする
# (変数keyword で、正規表現指定。今回はロボットですね)
if(match($0, keyword) ){
keyword_list[substr($0,RSTART,RLENGTH)]++
}
}
END{
# 配列 keyword_list の
# 添え字i は キーワード、中身はヒット数(アクセス数) になる
for (i in keyword_list) {
printf(" %16s : %d\n", i, keyword_list[i]);
}
}' | tee robot_list$mm$ii.txt
min=`expr $min + 1 `
done
# 過去何回アクセスしてきているかを調べる
echo "--- total days ----------------"
awk -F ":" '{
a[$1]++;
} END{
for (i in a){
printf("%16s :%02ddays\n", i, a[i]);
}
}' robot_list$mm* |\
sort -t: -k2 |\
tee robot_list_days.txt
実行イメージはこちら # ./bot_count.sh
0201 -----------------
bingbot : 12
Baiduspider : 1
Uptimebot : 2
webmeup-crawler : 63
Googlebot : 6
MJ12bot : 2
0202 -----------------
(中略)
0228 -----------------
bingbot : 2
Baiduspider : 2
Uptimebot : 3
siteexplorer : 9
Googlebot : 70
MJ12bot : 23
--- total days ----------------
siteexplorer :06days
SemrushBot :07days
msnbot :08days
webmeup-crawler :10days
Yahoo! Slurp :13days
MJ12bot :21days
Uptimebot :26days
Baiduspider :27days
bingbot :28days
Googlebot :28days
他にも botっぽいのは要るのですが、ざっと見ただけでもこれだけあります。 Google Analytics と比べて†Analytics は JavaScript や Cookie やらを使っているはずなので セッション数、つまり 余談ですが、
さいごに、 色々書いて長くなってしまいましたが・・・ 結論:
|
|