在Ruby中解析Apache格式化的URL

时间:2011-04-06 02:24:11

标签: ruby regex apache url

如何接收Apache Common Log文件并以简洁的直方图列出其中的所有URL:

/favicon.ico                      ##
/manual/mod/mod_autoindex.html        #
/ruby/faq/Windows/                    ##
/ruby/faq/Windows/index.html    #
/ruby/faq/Windows/RubyonRails   #
/ruby/rubymain.html                   #
/robots.txt                           ########

测试文件样本:

65.54.188.137 - - [03/Sep/2006:03:50:20 -0400] "GET /~longa/geomed/ppa/doc/localg/localg.htm HTTP/1.0" 200 24834
65.54.188.137 - - [03/Sep/2006:03:50:32 -0400] "GET /~longa/geomed/modules/sv/scen1.html HTTP/1.0" 200 1919
65.54.188.137 - - [03/Sep/2006:03:53:51 -0400] "GET /~longa/xlispstat/code/statistics/introstat/axis/code/axisDens.lsp HTTP/1.0" 200 15962
65.54.188.137 - - [03/Sep/2006:04:03:03 -0400] "GET /~longa/geomed/modules/cluster/lab/nm.pop HTTP/1.0" 200 66302
65.54.188.137 - - [03/Sep/2006:04:11:15 -0400] "GET /~longa/geomed/data/france/names.txt HTTP/1.0" 200 20706
74.129.13.176 - - [03/Sep/2006:04:14:35 -0400] "GET /~jbyoder/ambiguouslyyours/ambig.rss HTTP/1.1" 304 -

这就是我现在所拥有的(但我不知道如何制作直方图):

...
---

$apache_line = /\A(?<ip_address>\S+) \S+ \S+ \[(?<time>[^\]]+)\] "(?<method>GET|POST) (?<url>\S+) \S+?" (?<status>\d+) (?<bytes>\S+)/
$parts = apache_line.match(file)
$p parts[:ip_address], parts[:status], parts[:method], parts[:url]

def get_url(file)
    hits = Hash.new {|h,k| h[k]=0}
    File.read(file).to_a.each do |line|
    while $p parts[:url]
        if k = k
            h[k]+=1
            puts "%-15s %s" % [k,'#'*h[k]]
        end
    end
end

...
---

以下是完整的问题:http://pastebin.com/GRPS6cTZ伪代码很好。

2 个答案:

答案 0 :(得分:2)

  1. 您可以创建哈希映射到命中数的每个路径。为方便起见,我建议使用一个Hash,当你要求一个以前没见过的路径时,它将值设置为0。例如:

    hits = Hash.new{ |h,k| h[k]=0 }
    ...
    hits["/favicon.ico"] += 1
    hits["/ruby/faq/Windows/"] += 1
    hits["/favicon.ico"] += 1
    p hits
    #=> {"/favicon.ico"=>2, "/ruby/faq/Windows/"=>1}
    
  2. 如果日志文件非常庞大,而不是将整个内容放入内存中,请一次处理一行。 (查看File类的方法。)

  3. 由于Apache日志文件格式没有标准分隔符,我建议使用正则表达式来获取每一行并将其分成您想要的块。假设您正在使用Ruby 1.9,我将使用命名捕获来稍后对方法进行干净访问。例如:

    apache_line = /\A(?<ip_address>\S+) \S+ \S+ \[(?<time>[^\]]+)\] "(?<method>GET|POST) (?<url>\S+) \S+?" (?<status>\d+) (?<bytes>\S+)/
    ...
    parts = apache_line.match(log_line)
    p parts[:ip_address], parts[:status], parts[:method], parts[:url]
    
  4. 您可能希望根据状态代码选择过滤这些内容。例如,您是否希望在图表中包含有人输入错误的所有404次点击?如果你没有将所有行都插入内存,你就不会使用Array#select,而是在循环中跳过它们。

  5. 收集完所有热门后,是时候写出结果了。一些有用的提示:

    1. Hash#keys可以同时为您提供数组的所有键(路径)。您可能希望写出具有相同空白量的所有路径,因此您需要确定哪个路径最长。也许您希望map路径到达其长度,然后获取max元素,或者您可能希望使用max_by来查找最长路径,然后找到它的长度。

    2. 虽然令人讨厌,但使用sprintfString#%是制作格式化报告的好方法。例如:

      puts "%-15s %s" % ["Hello","####"]
      #=> "Hello           ####"
      
    3. 就像你需要找到最好的格式名称一样,可能想找到点击率最高的网址,这样你就可以将最长的哈希值扩展到该值。 Hash#values将为您提供所有值的数组。或者,您可能要求一个#必须始终代表100次点击或其他内容。

    4. 请注意,String#*允许您通过重复创建字符串:

      p '#'*10
      #=> "##########"
      
  6. 如果您对代码有任何疑问,请提出更多问题!

答案 1 :(得分:1)

由于这是作业,我不会给你确切的答案,但是Simone Carletti已经实现了Ruby class来解析Apache日志文件。你可以从那里开始看看他是如何做事的。