アクセスログからドメインだけを取り出す

うちではクライアントからのウェブアクセスは全部Squidを使ったプロクシを通しています。このアクセスログからドメインだけを抽出して、統計をとりたいんだと。つまり、

  • d.hatena.ne.jp
  • b.hatena.ne.jp
  • hoge.hatena.ne.jp

って3つログがあったら、hatena.ne.jpの部分だけ取り出したいとのこと。はいはいと二つ返事で引き受けたのはいいが、ちょっと大変だった。アクセスログからアクセスURLを取り出すところとか、URLからホスト名(www.hogehoge.comみたいな部分)を取り出すところまでは正規表現ですらすらっと(実は悩みながら)書けたんだけど、その後なやみました。ドットで区切られた後ろ三つ(co.jp,ne.jpの場合),後ろ二つ(.comとか.net)とかってどうやって取り出せばいいんだろう。結局こんなコードになりました。

#!/usr/bin/perl

while(<>){
    my @fields = split(' ',$_) ;
    my $url = $fields[6];

    if ($url =~ m{^http://([^/:]+(:(\d+))?)(/.*)?$}i){
        my $host = $1;
        my @strings = split('\.',$host);
        my @rev_strings = reverse @strings;

        if( $host =~ /[.co.jp|.go.jp|.ne.jp]$/ ){
            print ".",$rev_strings[2],".",$rev_strings[1],".",$rev_strings[0],"\n";
        } elsif ($host =~ /[com|net|.jp|org]$/ ){
            print ".",$rev_strings[1],".",$rev_strings[0],"\n";
        }
    }
}

Squidの標準ログだとURLは6カラム目。host名を取り出すところでポート番号とかパスとかを取り出しているのは詳説 正規表現 第2版からのパクリ。そのあと$hostを.でsplitして、reverseして、最後にくっついているTLDによって最後から3つとるか、2つとるかを分岐。正確にやるのなら、もっと条件をきちんと整理しないといけないんだろうけど、それほどデータに精密性が求められるわけではないのでこんなところで。これで標準出力に吐き出してsort -uとかに喰わせればリスト出来上がり。

$ ./script.pl access.log | sort -u > list.log