#!/usr/local/bin/perl # Web Counter Ver. 0.31 # USAGE : # 1) webcnt.cgi?test --- テスト # 2) webcnt.cgi? --- カウント # 3) webcnt.cgi?hide --- 隠しカウント # 4) webcnt.cgi?cmt+xxx --- ログに'xxx'とコメントを付加します 指定無の場合はカウンター名 # 5) http://〜/cgi-bin/webcnt.cgi?syuukei --- アクセス状況の集計 # 6) webcnt.cgi?image+xxx.gif --- 隠しカウンター(gifイメージの表示) # *) SSIのコマンドモードやコマンドラインでの実行の場合は'?'や'+'は空白にしてください ####################### # 設定 ####################### $print_http_header = 0; $column = 6; $lock_timeout = 240; $file_lock_enable = 1; $address_check_enable = 1; $cnt_timeout = 60; @reject_domain = (""); @reject_address = (""); $create_history = 1; $log_max = 1000; $script_dir = ''; $my_url = ''; $image_dir = '../image/'; @robots = ("WWWC","WebFetch","Lycos","indexpert","Scooter","robot", "OpenTextSiteCrawler","Googlebot","Slurp","NEC-MeshExplorer", "InfoSeek","WebFetch"); $log_ip = 1; ####################### # 設定項目の説明 ####################### # $print_http_header : "Content-type: text/html"の出力 (0:Off / 1:On) # : 集計、テスト等のCGIモードでは強制で表示します # $column : 表示桁数 ('0'を補間して指定桁数で表示。0の場合は有効な数字のみ) # $lock_timeout : ロックファイルタイムアウト ファイルロックのタイムアウト (秒) # $file_lock_enable : ファイルロック (0: Off / 1: On) # $address_check_enable : アドレスチェック (0: Off / 1: On) # 以前にアクセスされたかをチェックしカウントを制限します # $cnt_timeout : カウントタイムアウト(アドレスチェックが有効のときに有効) # 最後のアクセスから次のカウントを有効にするまでの時間(分) # カウントを行う ( 0は日付が変わるまで) # @reject_domain : カウントしないドメイン # ex. @reject_domain = ("foo.var.com", "abc.org"); # *.foo.var.com, *.abc.org はカウントしません # @reject_address : カウントしないIPアドレス # ex. @reject_address = ("172","192.168.19.101-128","200.1.1.1"); # 172.*, 192.168.19.101〜192.168.19.128, 200.1.1.1 のアドレスからはカウントしない # "202.19"と指定すると 202.19.*.* や 202.19?.*.* が対象になります #   202.19.* を対象にしたい場合は "202.19." と指定してください # reject_domain,reject_address に書かれた場合はログも出力しません # $create_history : アクセスログのヒストリー作成 ( 0: Off / 1: On ) # $log_max : ログファイルの最大行数 # $script_dir : スクリプトのある絶対ディレクトリ cmd=で実行する場合に指定します # $my_url : 自分のウェブサイトのURL (リンク元がこの中のページの場合は # ログ中のリンク元を「OwnPage」とします # $image_dir : イメージファイルの置き場 # (スクリプトのあるディレクトリ (cgi-bin/等) からの相対パス) # @robots : カウントを行わないブラウザ (ロボット対策?) # リスト中の文字が含まれるブラウザ(HTTP_USER_AGENT)はカウントを行わない。 # ex. @robots = ("robot","WebFetch"); # $log_ip : ログファイルドメイン別集計で IP アドレスを CLASS B で集約する # (個別の IP アドレスでなく最下位の1オクテットをまとめる。) # ( 0: Off / 1: On ) # ex. 192.168.0.1 〜 192.168.0.254 → 192.168.0.X ################# # 使用ファイル ################# $my_name = 'webcnt'; $counter_file = "$script_dir" . "$my_name" . ".cnt"; $access_log = "$script_dir" . "$my_name" . ".acs"; $bkp_log = "$script_dir" . "$my_name" ."_bkp.acs"; $lock_file = "$script_dir" . "lock/$my_name" . "_lck"; $sum_dom = "$script_dir" . "$my_name" . ".s1"; $sum_idx = "$script_dir" . "$my_name" . ".s2"; $sum_pre = "$script_dir" . "$my_name" . ".s3"; $sum_bro = "$script_dir" . "$my_name" . ".s4"; $cmnt = "$script_dir" . "$my_name"; # $my_name : カウンター名 # $counter_file : カウンターファイル (カウント数を保存) # $access_log : アクセスログ(過去にアクセスのあったアドレスを保存) # $bkp_log : アクセスログヒストリー (1世代前のログ) # $lock_file : ロックファイル # $sum_dom : ドメイン毎集計ファイル # $sum_idx : ページインデックス毎の集計ファイル # $sum_pre : リンク元毎の集計ファイル # $sum_bro : ブラウザ毎の集計ファイル # $cmnt : ログに書き出すコメント(ページインデックス) ####################### # 処理 ####################### # オプションの判断 $mode = ""; for ($i = 0 ; $i <= $#ARGV ; $i++) { if ($ARGV[$i] eq "test") { # テストモード $mode="test"; } elsif ($ARGV[$i] eq "hide") { # 隠しカウンターモード $mode = "hide"; } elsif ($ARGV[$i] eq "name") { # 別名でカウンターを利用する $my_name = $ARGV[++$i]; $counter_file = "$script_dir" . "$my_name" . ".cnt"; $access_log = "$script_dir" . "$my_name" . ".acs"; $bkp_log = "$script_dir" . "lock/$my_name" .".acs.bkp"; $lock_file = "$script_dir" . "lock/$my_name" . "_lck"; } elsif ($ARGV[$i] eq "cmt") { # コメントの取得 $cmnt = $ARGV[++$i]; } elsif ($ARGV[$i] eq "syuukei") { # アクセス集計の簡易表示 $mode = "syuukei"; } elsif ($ARGV[$i] eq "image") { # 隠しイメージカウンター $mode = "image"; if ($image_dir =~ /\/$/) { $imagefile =$image_dir & $ARGV[++$i]; } else { $imagefile =$image_dir & "/" & $ARGV[++$i]; } } } if ($mode eq "test" ) { # テスト &cgi_test; } elsif ($mode eq "syuukei") { # 集計 &syuukei("text"); } else { # カウント &go_count; } exit(0); ############################################################################ # サブルーチン ############################################################################ # ロックファイルの残留防止 sub sigexit { rmdir($file_lock); exit(0); } $SIG{'PIPE'} = $SIG{'INT'} = $SIG{'HUP'} = $SIG{'QUIT'} = $SIG{'TERM'} = "&sigexit"; # CGIのテスト sub cgi_test { print "Content-type: text/html\n"; print "\n"; print "\n"; print "\n"; print "テスト\n"; print "\n"; print "\n"; print "\n"; print "スクリプトは動作可能です。\n"; print "\n"; print "


\n"; print "\n"; if ( -d $lock_file ) { print "ロックファイル : $lock_file が残っています

\n"; } if ( ! -r $counter_file ) { print "カウンターファイル : $counter_file が存在しません

\n"; } elsif ( -w $counter_file ) { print "カウンターファイル : $counter_file が書き込み可能でありません

\n"; } if ( ! -r $access_log ) { print "アクセスログ : $access_log が存在しません

\n"; } elsif ( -w $access_log ) { print "アクセスログ : $access_log が書き込み可能でありません

\n"; } print "\n"; print "\n"; } # カウント sub go_count { $count_step = 1; # タイムゾーンの設定 $ENV{'TZ'} = "JST-9"; # 日付の取得 ($sec, $min, $hour, $mday, $mon, $year) = localtime(time()); $year += 1900; $ndate = sprintf("%04d/%02d/%02d", $year, $mon + 1, $mday); $ntime = sprintf("%02d:%02d:%02d", $hour, $min, $sec); # クライアント情報 $raddr = $ENV{'REMOTE_ADDR'}; $rhost = $ENV{'REMOTE_HOST'}; $ragnt = $ENV{'HTTP_USER_AGENT'}; # ロボットはカウントしない foreach $rbt (@robots) { if (index($ragnt, $rbt) > -1) {exit(1);} } # リンク元 自分のページの場合は OwnPage と出力 ($refer, $remain) = split(/\?/, $ENV{'HTTP_REFERER'}); $refer =~ s/%[0-9a-fA-F][0-9a-fA-F]/pack("C", hex($1))/eg; if ( $refer =~ /$my_url/ ) { $refer = "OwnPage"; } if ( $file_lock_enable ) { # ファイルのロック &file_lock; } # カウンターの取得 if ( open(CNT, "< $counter_file") ) { $wrk = ; ($count, $acnt) = split(/:/, $wrk); close(CNT); } else { $count = -1; $acnt = -1; } # カウントしないドメインやアドレスのチェック $reject = &rej_chk; $count_step = $reject; if ($address_check_enable && $count_step == 1 ) { # アドレスチェック $count_step = &add_chk; } # カウントアップ $acnt : 全通しカウント、$count 通常カウント $acnt++; if ($count >= 0 && $count_step > 0) { $count++; } if ( $column == 0 ) { $scount = sprintf("%ld",$count); $acount = sprintf("%ld",$acnt); } else { $scount = sprintf(sprintf("%%0%dld", $column), $count); $acount = sprintf(sprintf("%%0%dld", $column), $acnt); } if ( $count > 0 && $reject == 1 ) { # カウンターの出力 if ( open(CNT, "> $counter_file") ) { print(CNT "$count:$acnt"); close(CNT); } # ログファイルへ記録 &logwrite; } # 出力 if ($mode eq "image") { # 隠しカウント用イメージの表示 print "Content-type: image/gif\n\n"; $size = -s $imagefile; open(IMG, $imagefile); binmode(IMG); binmode(STDOUT); read(IMG, $buf, $size); print $buf; close(IMG); } else { # カウントの表示 if ($print_http_header == 1) { print "Content-type: text/html\n\n"; } if ( $mode ne "hide" ) { printf("$scount"); } printf(" \n"); } # ロック解除 if ( $file_lock_enable ) { rmdir($lock_file); } } # ファイルロック sub file_lock { foreach $i (1, 1, 2, 4, 8, 16, 20) { if (mkdir("$lock_file", 755)) { last; } elsif ( $i == 1 ) { # ロックファイルのタイムアウト判定 ($lock_time) = (stat($lock_file))[9]; if ($lock_time < time() - $lock_timeout) { rmdir($lock_file); } } elsif ( $i < 20 ) { # ロック失敗 wait sleep($i); } else { # ロックあきらめ ABORT exit(1); } } } # カウントしないドメインやアドレスのチェック sub rej_chk { # ドメインのチェック if ($rhost ne "") { foreach $r (@reject_domain) { if ($r ne "" && $rhost =~ /$r/ ) { return 0; } } } # アドレスのチェック foreach $r (@reject_address) { if ($r ne "") { if ( index($r,'-') > 0 ) { @rmip = split(/\./, $raddr); @rjip = split(/\./, $r); $i = 0; foreach $ip (@rjip) { if ( index($ip,'-') > 0 ) { ($imin, $imax) = split(/-/, $ip); if ( $rmip[$i] <= $imax && $rmip[$i] >= $imin ) { return 0; } else { last; } } else { if ( $rmip[$i] != $ip ) { last; } $i++; } } } else { if ( $raddr =~ /^$r/ ) { return 0; } } } } return 1; } # アドレスチェック sub add_chk { # カウントしない時間中を検出すれば「0:カウントしない」を返す # ログを最後まで読んで非カウント時間中のログがない場合は「1:カウントする」を返す if ($cnt_timeout > 0) { # タイムアウトの有効期限の計算 ($tss, $tmm, $thh, $tdd, $tmo, $tyy) = localtime(time() - ($cnt_timeout * 60) ); $limdate = sprintf("%04d%02d%02d", $tyy + 1900, $tmo + 1, $tdd); $limtime = sprintf("%02d%02d", $thh, $tmm); } open(FADDR, "$access_log" ); while() { ($tcount, $tacnt, $tdate, $ttime, $taddr, $remainder) = split(/\t/, $_, 6); if ($taddr eq $raddr ) { ($lyy, $lmo, $ldd) = split(/\//, $tdate); $logdate = sprintf("%04d%02d%02d", $lyy, $lmo, $ldd); # カウントのタイムアウトチェック if ( $cnt_timeout > 0 ) { # 時間のチェック if ($logdate > $limdate) { close(FADDR); return 0; } else { ($thh, $tmm, $tss) = split(/:/, $ttime); $logtime = sprintf("%02d%02d", $thh, $tmm); if ($logdate == $limdate && $logtime >= $limtime) { close(FADDR); return 0; } } } else { # タイムアウト時間が0のときは日付が変わるまでカウントしない $nowdate = sprintf("%04d%02d%02d", $year, $mon + 1, $mday); if ($logdate >= $nowdate ) { close(FADDR); return 0; } } } } close(FADDR); return 1; } # ログファイルへ記録 sub logwrite { if ($acnt % $log_max == 0) { # ログファイルのバックアップ (ログの最大行数を超えた場合) if ( $create_history == 1 ) { &syuukei('file'); open(BLOG, "> $bkp_log"); open(FLOG, "$access_log"); while() { print(BLOG "$_"); } close(FLOG); close(BLOG); } open(FADDR, "> $access_log" ); } else { open(FADDR, ">> $access_log" ); } # ログの書き出し # 出力項目:カウント、カウント(全通し)日付、時間、クライアントIP、 # クライアント名、ブラウザ、ページ識別用コメント、リンク元 print(FADDR "$scount\t$acount\t$ndate\t$ntime\t$raddr\t$rhost\t$ragnt\t$cmnt\t$refer\n"); close(FADDR); } # 集計 (集計ファイルの作成/結果表示) sub syuukei { $smode = shift(@_); $pre_cnt = 0; $bro_cnt = 0; # ドメイン毎集計の読み込み open(FASUM, "$sum_dom" ); while() { ($tsdom, $tscnt) = split(/\t/, $_, 2); $wkacnt{$tsdom} = $tscnt; } close(FASUM); # ページインデックス毎集計の読み込み open(FISUM, "$sum_idx" ); while() { ($tsidx, $tscnt) = split(/\t/, $_, 2); $wkicnt{$tsidx} = $tscnt; } close(FISUM); # リンク元毎集計の読み込み open(FPSUM, "$sum_pre" ); while() { ($tspre, $tscnt) = split(/\t/, $_, 2); $wkpcnt{$tspre} = $tscnt; $pre_cnt += $tscnt; } close(FPSUM); # ブラウザ毎集計の読み込み open(FBSUM, "$sum_bro" ); while() { ($tsbro, $tscnt) = split(/\t/, $_, 2); $wkbcnt{$tsbro} = $tscnt; $bro_cnt += $tscnt; } close(FBSUM); $ocnt = 0; open(FLOG, "$access_log" ); while() { chop($_); ($scnt, $sacnt, $sdate, $stime, $sadd, $sdom, $sagnt, $sidx, $ref) = split(/\t/, $_, 9); # ドメイン毎のカウント if ( $ocnt != $scnt ) { $sdom = &cut_hostname($sdom); if ( defined $wkacnt{$sdom} ) { $wkacnt{$sdom}++; } else { $wkacnt{$sdom} = 1; } # リンク元ページ毎のカウント ($sref, $remain) = split(/\?/, $ref); if ( $sref ne "" ) { if ( defined $wkpcnt{$sref}) { $wkpcnt{$sref}++; } else { $wkpcnt{$sref} = 1; } $pre_cnt++; } # ブラウザ毎のカウント if ( $sagnt ne "" ) { if ( defined $wkbcnt{$sagnt}) { $wkbcnt{$sagnt}++; } else { $wkbcnt{$sagnt} = 1; } $bro_cnt++; } } $ocnt = $scnt; # ページインデックス毎のカウント if ( defined $wkicnt{$sidx} ) { $wkicnt{$sidx}++; } else { $wkicnt{$sidx} = 1; } } close(FLOG); if ($smode eq "file") { # 集計ファイルの出力 open(FASUM, "> $sum_dom" ); foreach $w ( sort keys %wkacnt) { print(FASUM "$w\t$wkacnt{$w}\n"); } close(FASUM); open(FISUM, "> $sum_idx" ); foreach $w ( sort keys %wkicnt) { print(FISUM "$w\t$wkicnt{$w}\n"); } close(FISUM); open(FPSUM, "> $sum_pre" ); foreach $w ( sort keys %wkpcnt) { print(FPSUM "$w\t$wkpcnt{$w}\n"); } close(FPSUM); open(FBSUM, "> $sum_bro" ); foreach $w ( sort keys %wkbcnt) { print(FBSUM "$w\t$wkbcnt{$w}\n"); } close(FBSUM); } else { # カウンターの取得 if ( open(CNT, "< $counter_file") ) { $wrk = ; ($count, $acnt) = split(/:/, $wrk); close(CNT); } else { $count = -1; $acnt = -1; } print "Content-type: text/html\n"; print "\n"; print "\n"; print "\n"; print "アクセス状況集計結果\n"; print "\n"; print "\n"; print "\n"; print "

アクセス状況集計結果

\n"; print"

アクセス数: $count ($acnt)


\n"; print"日計\n"; print"


\n"; print"\n"; print"\n"; print"\n"; @swkacnt = sort { $wkacnt{$b} <=> $wkacnt{$a} } keys %wkacnt; foreach $w ( @swkacnt ) { $ratio = $wkacnt{$w} * 100 / $count ; $sratio = sprintf("%3.1f", $ratio); $hosi = "*" x ($ratio / 4); print("\n"); } print"
クライアント毎アクセス数集計
クライアントホスト/アドレス アクセス     比  率     
$w$wkacnt{$w}$hosi ($sratio\%)
\n"; print"


\n"; print"\n"; print"\n"; print"\n"; @swkicnt = sort { $wkicnt{$b} <=> $wkicnt{$a} } keys %wkicnt; foreach $w ( @swkicnt ) { $ratio = $wkicnt{$w} * 100 / $count ; $sratio = sprintf("%3.1f", $ratio); $hosi = "*" x ($ratio / 4); print("\n"); } print"
ページ毎アクセス数集計(累計)
ページインデックス アクセス     比  率     
$w$wkicnt{$w}$hosi ($sratio\%)
\n"; print"


\n"; print"\n"; print"\n"; print"\n"; @swkpcnt = sort { $wkpcnt{$b} <=> $wkpcnt{$a} } keys %wkpcnt; foreach $w ( @swkpcnt ) { $ratio = $wkpcnt{$w} * 100 / $pre_cnt ; $sratio = sprintf("%3.1f", $ratio); $hosi = "*" x ($ratio / 4); print("\n"); } print"
リンク元ページ毎アクセス数集計(累計)
リンク元 アクセス     比  率     
$w$wkpcnt{$w}$hosi ($sratio\%)
\n"; print"


\n"; print"\n"; print"\n"; print"\n"; @swkicnt = sort { $wkbcnt{$b} <=> $wkbcnt{$a} } keys %wkbcnt; foreach $w ( @swkbcnt ) { $ratio = $wkbcnt{$w} * 100 / $bro_cnt ; $sratio = sprintf("%3.1f", $ratio); $hosi = "*" x ($ratio / 4); print("\n"); } print"
ブラウザ毎アクセス数集計(累計)
ブラウザ アクセス     比  率     
$w$wkbcnt{$w}$hosi ($sratio\%)
\n"; print"


\n"; print"日計\n"; print"\n\n"; } } # IPアドレスはそのまま、ホスト名は後ろから指定された数の名前?だけ残す # sub cut_hostname { $wkdomain = shift(@_); if ( $wkdomain =~ /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/) { # IPアドレスが含まれる場合 if ( $wkdomain =~ /.*\D$/ ) { # IP ADDRESS を含むドメインの IP ADDRESS 部を切り取る $wkdomain =~ s/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+//; $wkdomain =~ s/^\.//; $domain = $wkdomain; } else { # IP アドレスの場合 if ($log_ip) { # 下1オクテットをまとめる $wkdomain =~ s/([0-9]+)\.([0-9])+\.([0-9]+)\.([0-9]+)/$1\.$2\.$3\.X/; $domain = $wkdomain; } else { # IPアドレスをそのまま返す $domain = $wkdomain; } } } else { # 通常のドメインの場合はホスト部を切り取る ($host, $domain) = split(/\./, $wkdomain ,2) ; } return $domain; } # ------------- 使用に際してはこれ以降を切り取ってしまっても構いません ------------- #       但し、配布等に関してはオリジナルのままのファイルを配布してください ############### # 更新履歴 ############### # #1999/06/04 Ver. 0.10 完成 # #1999/06/07 Ver. 0.20 ログ出力方法の変更、通しカウントの追加 # Ver. 0.21 カウントタイムアウトの不具合を修正 # #1999/06/09 Ver. 0.22 タイムアウト時間の算出方法の修正 # #1999/06/11 Ver. 0.23 cmd モードで、ファイルの絶対パスを使用できるように修正 # #1999/06/17 Ver. 0.24 集計機能を追加 # #1999/07/23 Ver. 0.25 リンク元をログに追加 # #1999/08/19 Ver. 0.26 httpヘッダの記述ミスを訂正(^^; # #1999/08/20 Ver. 0.27 httpヘッダの出力方法を変更 # #1999/10/22 Ver. 0.28 集計方法の変更(ホスト毎集計をドメイン毎に変更) # #1999/10/27 Ver. 0.29 集計に比率を追加、集計のバグ取り # #1999/11/05 Ver. 0.30 集計プログラムを別に作成 # #2000/02/ Ver. 0.31 隠しカウンタとしてイメージを表示させる機能を追加 # 集計項目を追加(リンク元、ブラウザ) # #2001/12/11 Ver. 0.32 指定したロボットをカウントしない機能を追加 ############## # その他 ############## # # このカウンターはフリーソフトです。 # 使用、転載、再配布、改変、について、作者への問い合わせは必要ありません。 # 但し、改変した場合は、その履歴と改変者をドキュメントに付加してください。 # # このカウンターの使用にあたって発生した障害等について、作者は一切の責任を負いかねます。 # # 作者 しましま   e-mail : shima@propel.ne.jp # Web Page : http://www.propel.ne.jp/~shima/ # # このカウンターは とほほ氏の wwwcount.cgi Ver2.92 を参考に作りました # webcount.cgi 最新版入手先: http://www2e.biglobe.ne.jp/~s-hasei/