65.9K
CodeProject 正在变化。 阅读更多。
Home

如何从 Apache 日志文件生成完整的访问者计数。

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.86/5 (4投票s)

2012 年 1 月 10 日

CPOL

3分钟阅读

viewsIcon

29242

统计每个 IP 地址生成了多少次访问,并显示前 10 个来源。

引言

上一篇文章中,我描述了如何从 Apache 日志文件创建有关来自 localhost 与其他地方的点击次数的报告。只需将 IP 地址替换为另一个地址,就可以轻松更改该脚本,从而为任何单个 IP 地址与世界其他地方提供报告。

也可以对其进行更改以提供完整访问者计数的报告,显示每个 IP 地址的点击次数。 然后很容易显示前 10 个来源,或以其他方式过滤它们。

背景

只是为了回忆一下,在默认格式中,Apache 日志文件中的每一行都像这样开始

 127.0.0.1 - - [10/Apr/2007:10:39:11 +0300] ...
 127.0.0.1 - - [10/Apr/2007:10:39:11 +0300] ...
 139.12.0.2 - - [10/Apr/2007:10:40:54 +0300] ...
 217.1.20.22 - - [10/Apr/2007:10:40:54 +0300] ...

这意味着如果我们取任意一行并将其放入$line变量中,我们可以通过以下代码提取 IP 地址

my $length = index ($line, " ");
my $ip = substr($line, 0, $length);

使用代码

为了统计任意字符串集,我们需要一种可以将字符串映射到标量值的数据结构。在 Perl 中,这种数据结构称为“关联数组”或简称为“哈希”。在其他语言中,类似的东西可能被称为映射、字典或查找表。

哈希基本上是一个无序的键值对集合,其中键是唯一的字符串,值可以是任何标量值(数字、字符串或引用)。

在 Perl 中,哈希用百分号 (%) 标记。 因此,我们声明%count哈希来保存 IP 到“点击次数”的映射。 大部分代码与上一个示例相同,但我们没有增加两个单独的标量,而是使用以下构造来增加哈希的元素

$count{$ip}++;

当我们第一次遇到 IP 地址时,$count{$ip} 尚不存在。 如果某个值还不存在,Perl 会假定它具有“undef”值。 如果在某些数值运算中使用它,例如++自动递增,那么它会假装是数字 0。这变为 1,并且此操作还会在哈希中创建相应的条目。 键值对会自动出现。 这也称为自动激活

如您所见,哈希会自动增长。 Perl 完成所有内存管理。

完成此操作后,我们将拥有一个哈希,其中每个键都是一个 IP 地址,每个值是该 IP 地址在文件中出现的次数。 keys函数获取一个哈希作为参数,并返回哈希的键的无序列表。 此代码将打印所有 IP 地址以及相应的点击次数

foreach my $ip (keys %count) {
    print "$ip   $count{$ip}\n";
}

代码

完整的脚本在这里

#!/usr/bin/perl
use strict;
use warnings;

my $file = shift or die "Usage: $0 FILENAME\n";
open my $fh, '<', $file or die "Could not open '$file': $!";

my %count;

while (my $line = <$fh>) {
    my $length = index ($line, " ");
    my $ip = substr($line, 0, $length);
    $count{$ip}++;   
}

foreach my $ip (keys %count) {
    print "$ip   $count{$ip}\n";
}

关注点

当然,如果对它们进行排序会更好,这段代码可以做到

foreach my $ip (sort keys %count) {
    print "$ip   $count{$ip}\n";
}

但这会根据 ASCII 表对 IP 地址进行排序。 可能不是很有趣。

更好的排序可能是这样的

foreach my $ip (reverse sort { $count{$a} <=> $count{$b} } keys %count) {
    print "$ip   $count{$ip}\n";
}

在这里,我们根据相应的值对键进行排序,然后我们反转顺序以首先获取具有最大数字的 IP。 这是表达式,但让我们将其分解

reverse sort { $count{$a} <=> $count{$b} } keys %count

您可以对任何字符串列表进行排序。

sort @strings;

默认情况下,这将基于 ASCII 表比较每两个值进行排序。

您还可以使用任何其他条件对它们进行排序。 例如,字符串的长度

sort { length($a) <=> length($b) } @strings;

Perl 的sort()函数将获取它想要比较的任何两个值,将它们放入两个变量$a$b中,并评估该块。 根据结果,它将保持这两个值的顺序或交换它们。

sort { $count{$a} <=> $count{$b} } keys %count

此代码执行相同的操作,但它对哈希的键进行排序,并且在比较两个键时,表达式将比较两个键的值。 结果将按升序排列,但如果我们想显示点击次数最多的 IP,那么我们需要反转结果

reverse sort { $count{$a} <=> $count{$b} } keys %count

在最后一个示例中,我们执行相同的操作,但在显示时,我们使用辅助变量将项目数限制为前两个 IP 地址。

my $top = 2;
foreach my $ip (reverse sort { $count{$a} <=> $count{$b} } keys %count) {
    print "$ip   $count{$ip}\n";
    $top--;
    if ($top <= 0) {
        last;
    }
}
© . All rights reserved.