~ To be, or not to be, or to take a lunch anyway. ~ 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 やらを使っているはずなので セッション数、つまり 余談ですが、
さいごに、 色々書いて長くなってしまいましたが・・・ 結論:
|
|