変なプログラマ#5

またまた恥をかきに参加しますた。変なプログラマ第5回。

今回は「地図」でのプレゼンターということで、以前http://tomoshibi.mozilla.jp/とかで使われて不思議だったIPからだいたいどの辺からのアクセスがあったのかというのを解析して表示するスクリプトを書きました。

まあ、例によってロジックの間違いの為に「実家のページを見ているユーザがなぜか全て海外からのアクセスであった。」という恥ずかしい結果になりました。

過去3年間のアクセス元(笑)
http://maps.google.com/maps?q=37.8113,-122.3008
http://maps.google.com/maps?q=45.5184,-122.6554
http://maps.google.com/maps?q=36.5412,-121.3956
http://maps.google.com/maps?q=41.8822,-87.6309
http://maps.google.com/maps?q=37.4192,-122.0574
http://maps.google.com/maps?q=37.5155,-121.8962
http://maps.google.com/maps?q=38.6164,-121.4962
http://maps.google.com/maps?q=40.7619,-73.9763
http://maps.google.com/maps?q=17.7452,-64.7072
http://maps.google.com/maps?q=34.0530,-118.2642
http://maps.google.com/maps?q=33.9415,-118.1307
http://maps.google.com/maps?q=47.5839,-122.2995
http://maps.google.com/maps?q=37.4249,-122.0074
http://maps.google.com/maps?q=38.6312,-90.1922
http://maps.google.com/maps?q=26.2794,50.2083
http://maps.google.com/maps?q=45.6868,-122.5756
http://maps.google.com/maps?q=40.6290,-80.0793
http://maps.google.com/maps?q=45.5000,-73.5833
http://maps.google.com/maps?q=34.1738,-118.1702
http://maps.google.com/maps?q=49.2500,-122.9500
http://maps.google.com/maps?q=37.3042,-122.0946
http://maps.google.com/maps?q=37.8091,-122.2702
http://maps.google.com/maps?q=29.7523,-95.3670
http://maps.google.com/maps?q=38.0000,-97.0000

#!/usr/bin/perl
# IPから発信地を推測するスクリプト(IPのDBが要るよ)
use strict;
use warnings;

open GINDEX, "<", './????1.csv' or die $!;

my @ipmap;

# ここのロジックが怪しい?
while (<GINDEX>) {
   next if ! m/^"(\d+)","(\d+)","(\d+)"/;
   my ($saddr, $eaddr, $code) = ($1, $2, $3);
    
   my $range = ($eaddr - $saddr + 1) / 2;
   my $caddr = $range + $saddr;

   push @ipmap, [$caddr, $range, $code];
}

@ipmap = sort(@ipmap);

my %codeMap;
#一般的なApacheログを読み、IPデータベースとつきあわせる
while (<>) {
    next if ! m/\d+\.\d+\.\d+\.\d+/;

    my $ip = &getIpSerial($&);
    #print "$ip\n";
    my $blockCode = &findCode(\@ipmap, $ip);

    $codeMap{$blockCode} ++;
}

print map { "$_:$codeMap{$_}\n" } keys(%codeMap);

sub findCode ($$) {
    my $list = shift;
    my $ip = shift;

    my $len = int($#{$list} / 2 + 0.5);
    my $i = $len;
    while ($len > 0) {
	my $delta = $ip - $$list[$i][0];
	return $$list[$i][2] if abs($delta) <= $$list[$i][1];
	$len = int($len / 2);
	$i += $len * ($delta < 0 ? -1 : 1);
    }
    return -1;
}

sub getIpSerial($) {
    return -1 if ! m/(\d+)\.(\d+)\.(\d+)\.(\d+)/;
    my ($a,$b,$c,$d) = ($1, $2, $3, $4);

    return ($a<<24) + ($b<<16) + ($c<<8) +$d;
}
#!/usr/bin/env perl
#さっき作ったリストを地名DBとつきあわせて、GoogleMapsのリンクを生成するスクリプト
open GCITY, '<', './????2.csv' or die $!;

my %maps;
while (<GCITY>) {
    next if ! m/^\d/;
    my ($code, $country, undef, $city, undef, $ido, $keido) = split(",", $_);

    $maps{$code} = {
	'country' => $country,
	'city' => $city,
	'ido' => $ido,
	'keido' => $keido,
	};
}

while (<>) {
    my ($code, $count) = split(":", $_);
    next if $code == -1 || !exists($maps{$code});
    print "http://maps.google.com/maps?q=";
    print $maps{$code}->{ido}.",".$maps{$code}->{keido};
    print "\n";
}

ちなみに、GeoIPというサイトで入手出来る????.csvスクリプトにかけると動きます。使い方はそれぞれ短いソースなので、適当に推察してください。
GeoIPが提供する.pmモジュールAPIで対応していない(?)機能を実現(ちゃんと動かないけどね)しているので、規約違反だ!とか、間違いだらけやんけ!臭っ!などなにか問題があればご連絡願います。m(__)m