こないだ作ったリクエストファイル集計部だが,ちょっと変えれば他のデータも集計できるはず.他に集計したいと言ったら,ユーザーエージェント(どんなブラウザが多いか分かれば表示確認の参考にできるからね)と,後は外部リンク(これは単純に興味で)かなあ.
またまたつまずきながら作ってみる.
#集計表示
#引数 adddata は,集計するデータの種類を指定
#page=リクエストファイル
#ua=ユーザーエージェント
#lnk=外部リンク
#all=上記3種を上位10番目まで表示
#引数logおよびdateは,adddata=allの時に全件データへのリンクを張る際に使う.
def addup(file, adddata, log, date)
case adddata #ヘッダー処理
when "page"
header_out("リクエストファイル")
when "ua"
header_out("ユーザーエージェント")
when "lnk"
header_out("外部リンク")
when "all"
header_out("1日の集計")
end
date_out(file) #処理ファイル表示
robot_re = robot_name
ignore = Regexp.new("#{IGNORE_FILE_END}$|#{IGNORE_FILE}",true)
logfile = open(file)
page_count = Hash.new(0) #リクエストファイルを数え上げるためのハッシュ
ua_count = Hash.new(0) #ユーザーエージェントを数え上げるためのハッシュ
lnk_count = Hash.new(0) #外部リンクを数え上げるためのハッシュ
ip_prev = String.new #同一IPからのデータを除く(UA処理で使用)
while logline = logfile.gets
line = Accesslog.new(logline)
#無視するファイル・エラーコード・クローラーは処理しない
unless ignore =~ line.requestURI || /^[4|5]/ =~ line.status || robot_re =~ line.user_agent
#リクエストファイルの数え上げ
if adddata == "page" || adddata == "all" then
tmp_uri = line.requestURI
#"/"と"/index.shtml"は一緒のファイルなので,同じにしておく
tmp_uri = "/" if tmp_uri == "/index.shtml"
unless page_count.key?(tmp_uri) #もし,まだ数えたことのないページなら
page_count[tmp_uri] = 1 #ページをキーとするハッシュの要素に1を設定.
else
page_count[tmp_uri] += 1 #すでに数えたことがあれば+1
end
end
#ユーザーエージェントの数え上げ
if adddata == "ua" || adddata == "all" then
tmp_ua = line.user_agent
#直近の同一IPからのアクセスは除く
ip_last = line.ip #IP保持
unless ip_last == ip_prev #IPが前と同じでなかったら
unless ua_count.key?(tmp_ua)
ua_count[tmp_ua] = 1
else
ua_count[tmp_ua] += 1
end
end
ip_prev = ip_last
end
#外部リンクの数え上げ
if adddata == "lnk" || adddata == "all" then
tmp_ref = line.referer
#リファラーが?も=も含んでおらず(検索サイト除外)
#リファラー無しでもなく,自サイトでなければ
unless /\A#{URI_SELF}/ =~ tmp_ref || tmp_ref == "-" || /[\?|=]/ =~ tmp_ref then
unless lnk_count.key?(tmp_ref)
lnk_count[tmp_ref] = 1
else
lnk_count[tmp_ref] += 1
end
end
end
end
end
logfile.close
#多い順に並べ替え・同数ならデータの名前順
page_count = page_count.to_a.sort{|a, b| (b[1] <=> a[1]) * 2 + (a[0] <=> b[0]) }
ua_count = ua_count.to_a.sort{|a, b| (b[1] <=> a[1]) * 2 + (a[0] <=> b[0]) }
lnk_count = lnk_count.to_a.sort{|a, b| (b[1] <=> a[1]) * 2 + (a[0] <=> b[0]) }
#リクエストファイル集計表示
if adddata =="page" || adddata == "all" then
print "<h3>リクエストファイル集計</h3>\n"
print "<table summary=\"リクエストファイル集計(エラーファイル・クローラーによるアクセス・画像等へのアクセスは除く)\">\n"
print "<tr><th class=\"data-name\">リクエストファイル</th><th class=\"number\">訪問数</th><th><br></th></tr>\n"
maxcount = page_count[0][1] #最大リクエスト数を保持
rank = 0 #UA集計を同時に表示するときのカウンタ
f_value = 0 #前のハッシュの値を保持
page_count.each{|key, value|
#UA等も同時に表示するときは上位データのみ表示
if adddata == "all" then
if rank >= 11 #adddateがallで11番目のデータが来たら
unless value == f_value #前のデータの値と同じでない限り
#全データへのリンクを張って
print "<tr><td colspan=\"3\"><a href=\"#{CGI_FILE}?log=#{log}&date=#{date}&mode=addup&adddata=page\">全リクエストファイル</a></td></tr>\n"
break #イテレータをぬける
end
end
rank += 1
f_value = value
end
if %r|^/diary/.+| =~ key #もしリクエストファイルがdiary内だったら
key = URI.decode(key) #キーをURIデコード
end
graphlen = 400 * value / maxcount #棒グラフの長さを設定
print "<tr><td>#{key}</td><td>#{value.to_s}回</td>\n"
print "<td><img src=\"graph.gif\" width=\"#{graphlen}\" height=\"10\" alt=\"\"></td></tr>\n"
}
print "</table>\n"
rank = 0 #次で使うので初期化
end
if adddata == "ua" || adddata == "all" then
print "<h3>ユーザーエージェント集計</h3>\n"
print "<table summary=\"ユーザーエージェント集計(エラーファイル・クローラーによるアクセス・画像等へのアクセス・直近の同一IPからのアクセスは除く)\">\n"
print "<tr><th class=\"data-name\">ユーザーエージェント名</th><th class=\"number\">訪問数</th><th><br></th></tr>\n"
maxcount = ua_count[0][1] #最大数を保持
ua_count.each{|key, value|
if adddata == "all" then #上位データのみ表示する処理
if rank >= 11
unless value == f_value
print "<tr><td colspan=\"3\"><a href=\"#{CGI_FILE}?log=#{log}&date=#{date}&mode=addup&adddata=ua\">全ユーザーエージェント</a></td></tr>\n"
break
end
end
rank += 1
f_value = value
end
graphlen = 400 * value / maxcount
print "<tr><td>#{key}</td><td>#{value.to_s}回</td>\n"
print "<td><img src=\"graph.gif\" width=\"#{graphlen}\" height=\"10\" alt=\"\"></td></tr>\n"
}
print "</table>\n"
rank = 0 #次で使うので初期化
end
if adddata == "lnk" || adddata == "all" then
print "<h3>外部リンク集計</h3>\n"
print "<table summary=\"外部リンク集計(エラーファイル・クローラーによるアクセス・画像等へのアクセス・検索サイトからのリンクは除く)\">\n"
print "<tr><th class=\"data-name\">リンクURI</th><th class=\"number\">訪問数</th><th><br></th></tr>\n"
maxcount = lnk_count[0][1] #最大数を保持
lnk_count.each{|key, value|
if adddata == "all" then #上位データのみ表示する処理
if rank >= 11
unless value == f_value
print "<tr><td colspan=\"3\"><a href=\"#{CGI_FILE}?log=#{log}&date=#{date}&mode=addup&adddata=lnk\">全外部リンク</a></td></tr>\n"
break
end
end
rank += 1
f_value = value
end
graphlen = 400 * value / maxcount
print "<tr><td><a href=\"#{key}\">#{key}</a></td><td>#{value.to_s}回</td>\n"
print "<td><img src=\"graph.gif\" width=\"#{graphlen}\" height=\"10\" alt=\"\"></td></tr>\n"
}
print "</table>\n"
end
footer_out() #フッタ表示
end
ぐはあ……美しくない,まったく美しくないよ!似たようなことを何回も書いているんだよなあ.もっとすっきりしたコードにできないものか.
今回,引数を付け加えました.adddataというのが一番大事で,これでどのデータを集計するかを分けます.ちなみに,allは最初は概観だけ表示されるのがよろしかろうと思って加えたもので,指定すると,データが多いものは,上位10位ほどだけ出て,残りは全データ表示へのリンクになります.このリンクを張るときに使うのがlogとdateの引数.
ユーザーエージェントの数え上げでは,同一の人の使うブラウザを何度も数えてもしょうがないなあと思い,前のデータとIPを比べて違っていたときだけカウントしています.もっとも,同時に違う人がアクセスしていて,アクセスログに交互に記録が残ると,結局,たくさんカウントされてしまうので,あんまり頭のいい処理ではない.いっそのこと,同一IPは省いちゃうのが吉かなあ.
外部リンクの数え上げは,リファラーを拾っています.自サイトからの移動は必要ないので除外,リファラーなしも除外.そして,検索結果からの移動も除外します.この検索結果からの移動というのはずいぶん迷ったのですが,URIに?や=があったら機械的に抜きました.ただ,これだと,掲示板やブログ等から飛んできた場合も抜いてしまうことになる場合があるわけで,大いに悩むところです.
検索結果と言えば,どんな単語の検索に引っかかっているかも気になるところなので,集計してみたいんですが,これはいろんな検索サイトのURIの規則が分からないとできないわけで,けっこう難題だと思われます.
あ,一応,上述のものの表示結果.