たのしいRuby 第22章 アクセスログの解析

今日から最後の節、「ツールを作ってみよう」に入ります。今回は、アクセスログの解析です。といっても・・・僕の自宅サーバーは外部公開していないので、僕のアクセスしかありません(汗

自分で自分のアクセスを解析しても相当意味が無いので、はてなカウンターのログを解析します。

はてなカウンターの仕様

はてなカウンターの仕様は、

  • 月ごとに1ファイルのCSV
  • CSVは、コンマ区切りでコンマがある場合は"hoge,moge"でエスケープしてある。
  • 各項目は、日付,IPアドレス,リファラー,UA,言語、画面サイズ、色、訪れたページ

となってます。

何を作るか

いきなり、はてなカウンターを作るのは大変過ぎるので、足りない機能を補います。

はてなカウンターで足りない情報は、

  • OS別統計がない。

Linuxを使っている人がどれくらいいるのか気になります。

ってことで、Hatena-Log.rb 0.0.1。

#!/usr/bin/ruby

# Hatena-Log.rb 0.0.1
# Usage  : ruby hatena-log.rb [000-0000-00.csv]"

class Log
  attr_reader :ua
  def initialize(time, ip, from, ua, lang, display, color, to)
    @time = time
    @ip = ip
    @from = from
    @ua = ua
    @lang = lang
    @display = display
    @color = color
    @to = to
  end
end

class HatenaLog
  def initialize
    @logs = Array.new
  end
  def append(log)
    @logs << log
  end
  def <<(log)
    self.append(log)
  end
  def length
    @logs.length
  end
  def dump_ua
    ua = Hash.new(0)
    @logs.each do |log|
      ua[log.ua] += 1
    end
    ua
  end
end


unless ARGV[0]
  puts "Usage: ruby log.rb [000-0000-00.csv]"
  exit 1
end

require "csv"
csv = CSV::Reader.create(File.open(ARGV[0]))

logs = HatenaLog.new
csv.each do |line|
  logs << Log.new(*line)
end

ua = logs.dump_ua

ua.sort {|a, b|
  b[1] <=> a[1]
}.each {|key, value|
  # BUG : パイプを通すと,SIGHUP (SignalException)が出る。
  printf "%s\t%s\n", value, key
}
  • 勉強のため無駄にクラス作ってます(笑
  • 命名がヘンなので直す必要あり。
  • 今のところUAを並べる機能しかありませんが、かなり重宝しそうです。

使い方

% ruby hatena-log.rb 001-2007-04.csv
657     Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3
203     Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)
159     Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
145     Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
82      Mozilla/5.0 (Windows; U; Windows NT 5.0; ja; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3
73      Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322) Sleipnir/2.49
70      Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)
69      Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11
60      Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
59      Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; ja-JP-mac; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3
45      Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; InfoPath.1)
44      DoCoMo/2.0 SH901iS(c100;TB;W30H15)
43      Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ja-jp) AppleWebKit/419 (KHTML, like Gecko) Safari/419.3
41      Mozilla/5.0 (Macintosh; U; Intel Mac OS X; ja-JP-mac; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3
40      Mozilla/5.0 (X11; U; Linux i686; ja; rv:1.8.0.10) Gecko/20070313 Fedora/1.5.0.10-5.fc6 Firefox/1.5.0.10
39      Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
38      Mozilla/5.0 (Macintosh; U; Intel Mac OS X; ja-jp) AppleWebKit/419 (KHTML, like Gecko) Safari/419.3

はい、殆んどLinuxユーザーいませんでした・・・。

ってことで、Linuxへのお誘い。

Linux最高

"自由を求めたいなら断然Linux。"

"プログラムやりたいなら断然Linux"

"ハッカーになりたいなら断然Linux"

How To Become A Hacker: Japaneseより。

オープンソース UNIX 類のひとつを入手し、使いかたと動かしかたをおぼえること

Linux最高デス。

手始めは書籍が充実しているFedora Coreがいいと思います。

バグ

実行は出来るけど、パイプを通すと、エラーが出ます。

% ruby hatena-log.rb 001-2007-04.csv | head -20
..(略
33      Mozilla/5.0 (X11; U; Linux i686; ja; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3
31      Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
hatena-log.rb:62:in `write': Broken pipe (Errno::EPIPE)
        from log.rb:62:in `printf'
        from log.rb:62
        from log.rb:58:in `each'
        from log.rb:58

イマイチ原因不明。

付加機能

OSごとの統計を取りました。

# HatenaLogに追加
  def dump_os
    ua = Hash.new(0)
    @logs.each do |log|
      case log.ua
      when /Windows/i
        os = $&
      when /Mac/i
        os = $&
      when /Linux/i
        os = $&
      when /FreeBSD|SunOS/i
        os = $&
      when /DoCoMo|KDDI/i
        os = $&
      when /w3m/i
        os = $&
      else
        os = log.ua
      end
      ua[os] += 1
    end
    ua
  end

# 最後の部分を入れ替え。
length = logs.length
logs.dump_os.sort {|a, b|
  b[1] <=> a[1]
}.each {|key, value|
  printf "%s\t%.1f%%\t%s\n", value, value.to_f * 100 / length, key
}
  • アクセスのあったものだけなので、かなり適当。
  • w3mは、OSを返さない。

実行すると、

% ruby hatena-log.rb 001-2007-04.csv
2839    76.8%   Windows
400     10.8%   Linux
249     6.7%    Mac
64      1.7%    DoCoMo
24      0.6%    FreeBSD
22      0.6%    KDDI
  • 8割 Windows
  • 1割 Linux
  • 0.6割 Unix
  • 0.2割 携帯
  • その他 ボットなど。

ログの解析も面白そうなので、少しづつバージョンアップしていこうかと思います。