使用 Perl 从 Apache Domlogs 中提取特定的用户代理

Pulling a Specific Useragent out of Apache Domlogs with Perl

我目前正在构建一个正则表达式,它将能够提取访问站点的机器人的用户代理的名称。到目前为止,我已经能够让表达式匹配,但它没有 return 我期望的值。请检查以下示例:

#!/usr/bin/perl

use strict; use warnings;

while (<>)
{
#Remove any unseen whitespace
chomp($_);

my $i = 0;


#Open every file in turn
open(my $domlog, "<", "$_") or die "cannot open file: $!";

#these were used for testing the open/closing of files
#print "Opened $_";
#print "Closed $_";

#for now confirm the file I'm searching through
print "Opened $_\n";

#Adding the name of the domain to the @domaind array for data processing later
push (@domain, ) if $_ =~ m/(\/usr\/local\/apache\/domlogs\/.*\/)(.*)/;

#search through the currently opened domlog line by line
while (<$domlog>) {

#clear white space again
chomp $_;

#Print the the record in full, then print the IP address of the visitor and what should be the useragent name 
print "$_\n";
print "\n \n\n" if $_ =~ m/^(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})\s(.*)\s.*(\w+[crawl|bot|spider|yahoo|bing|google])?/i;

}

close $domlog;

}

我不确定我的正则表达式是否过于贪婪或者我是否错误地使用了通配符。任何意见,将不胜感激。谢谢。


我完全忘记了输入,因为我担心这里的代码,我 运行 我服务器上的一些 domlogs 上的脚本,这里有一些输出以及我得到的结果

输入
188.165.15.208 - - [13/Jan/2015:09:20:49 -0500] "GET /?page_id=2 HTTP/1.1" 200 10574 "-" "Mozilla/5.0 (compatible; AhrefsBot/5.0; +http://ahrefs.com/robot/)"

输出
188.165.15.208
- - [13/Jan/2015:09:20:49 -0500] "GET /?page_id=2 HTTP/1.1" 200 10574 "-" "Mozilla/5.0(兼容;AhrefsBot/5.0;

输入
180.76.4.26 - - [13/Jan/2015:10:16:24 -0500] "GET / HTTP/1.1" 200 8744 "-" "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)"

输出
180.76.4.26
- - [13/Jan/2015:10:16:24 -0500] "GET / HTTP/1.1" 200 8744 "-" "Mozilla/4.0(兼容;MSIE 7.0;Windows NT

没有示例预期输出,我只能猜测您可能想要实现的目标。但是关于您的脚本,这里有一些需要指出的地方:

push (@domain, ) if $_ =~ m/(\/usr\/local\/apache\/domlogs\/.*\/)(.*)/;

您已经在使用 m 运算符,您可以使用它更改分隔符。此外,还有 (?:…) 非匹配组,但在这种情况下,您甚至不需要它。 bare 中的正则表达式如果不与 =~ 一起使用,则始终匹配 $_,因此您可以摆脱它。在列表上下文中,他们返回匹配组的内容。现在这里全部合并:

push @domain, m~/usr/local/apache/domlogs/.*/(.*)~;

现在谈谈你的另一个表达方式。如果事情变得复杂,你应该使用 /x 标志,它以一种很好的方式提高了可读性。

. 是正则表达式中的一个特殊字符,它匹配任何东西,所以你可能想转义它。此外,对于 ip 地址匹配,您可以使用 (?:…):

(\d{1,3}(?:\.\d{1,3}){3})

[…] 匹配括号内的 个字符 所以你的

[crawl|bot|spider|yahoo|bing|google]`

可以减少到

[abcdeghilnoprstwy|]

并且会做同样的事情,这显然不是你想要的,而是强调,你哪里错了。您可能想要的是一个不匹配的组。如果你把它设为可选,它可能会不匹配(所以去掉组后面的 ?)。

(?:crawl|bot|spider|yahoo|bing|google)

所以这就是这个魔鬼的组合:

if (/^(\d{1,3}(?:\.\d{1,3}){3})                  #  - ip address
     \s(.*)\s*                                   #  - within spaces
     (\w*(?:crawl|bot|spider|yahoo|bing|google)) #  - some bot string
    /xi){                                        # end of regex
  print ("\n\n\n");
}

可能仍然不是您想要的,但我不知道那是什么。您可能想让 </code> 的组成为非贪婪的 <code>(.*?)。如果您想在其中进行匹配,也可以转义一些括号。

最后,看看 loghack,因为有人可能已经为您完成了工作。

这里是相关的文档(这些是 perldoc 页,所以如果你的系统上安装了 perldoc,你也可以 perldoc perlretut):

  • perlretut 正则表达式教程。
  • perlre 正则表达式的文档。
  • perlreref 如果您至少完成了 perlretut 此参考资料会派上用场。