Как стать автором
Обновить

GeoIP, попытки нетрадиционного использования

Время на прочтение5 мин
Количество просмотров14K
Делать было вечером, делать было нечего...

Предыстория


В один прекрасный момент, случайно наткнулся на старый сервис, который позволяет вешать картинку на свой сайт, которая показывает географическое положение посетителей. Их много и разных.
Вещь сама по себе не такая уж полезная, мне как то данные awstats'а хватает за глаза.
Но для демонстрации посетителю, что он не случайный идиот прохожий забредший на мертвый сайт это дело подходит.
Дальше как и полагается раскинул мозгами на тему, а как же они это делают, разобрался и вроде успокоился…

Но больная голова покоя рукам не дает, так что сейчас я вам покажу один забавный и не несущий смысловую нагрузку эксперимент.
image
Это выборка всех возможных координат IP-шников (с округленные до целых).
Можно сказать, что это фотография интернета ареал обитания TCP/IP

Внимание, автор не является кодером в хорошем смысле этого слова, поэтому лиц, которых может шокировать копрокод прошу удалиться.


Откуда брать данные, сразу становилось ясно, это все известная GeoIP, которая ни раз выручала нас, благо в GeoLiteCity есть такая жизненная важная вещь как координаты.
#geoiplookup -f /usr/local/share/GeoIP/GeoLiteCity.dat 90.155.128.74
GeoIP City Edition, Rev 1: RU, 48, Moscow, N/A, 55.752201, 37.615601, 0, 0

Правда она считает, что я из бутово переехал в Александровский сад. Но ничего, нам такая точность ни к чему.

Осмотр файла GeoLiteCity.dat показал, что очень похоже на бинарное дерево, а его как парсить быстро и безболезненно я к сожалению пока не раскурил.
Первой мыслей сразу же пришел вариант перебора всех возможных ip адресов в занесении в БД уникальных координат.
256^4=4294967296 даже за минусом резервных, локальных и т.д. все равно приводит к оценкам времени, которые никак не радуют. Закончим за полгода, это уже хорошо.
После активного подхода к задачке вспомнилось, что у GeoIP вроде были и другие варианты хранения их данных, и бинго CSV формат спас отца русской демократии.

Пример файла:
5751,"US","NY","Albany","12209",42.6390,-73.7890,532,518
5752,"US","NY","Hillsdale","12529",42.2164,-73.5413,532,518
5753,"US","NY","Albany","12225",42.6706,-73.7791,532,518


Всего 310070 запись.
Так как генерить картинку по всем трехсотдесяти тысяч точкам как то не кошерно, то делаем скрипт, который будет парсить CSV файл и заносить уникальные округленные позиции в БД, ну а потом уже делать выборку из БД и рисовать картинку.
Почему округленные, мне не нужна была высокая точность, так что точка длины и широты была достаточна.
Если же захочется сделать что то подобное размером 3600 на 1800, то ничто не запрещает вам повторить данный эксперимент дочитать пост до конца.

Выбор инструментов.
MySQL — уже стоит, уже под рукой. там создается одна таблица, со 4 колонками (x, y, kol, достаточно было двух, но последнее я добавил, что бы посмотреть где больше всего сконцентрировано записей)
Perl — для написания парсера CSV и занесения в БД, Так же под рукой, плюс в нем удобней писать в joe через SSH с коммуникатора. Проффесионалов перлистов прошу сильно не смотреть на код.
PHP с библиотекой GD, собственно для генерации собственно картинки. Почему? опыт работы с GD библиотекой именно из php большой.

Итак, качаем последний CSV файл.

Создаем БД и таблицу:
CREATE DATABASE IF NOT EXISTS `geo`;
CREATE TABLE IF NOT EXISTS `all` (
`x` int(3) NOT NULL,
`y` int(3) NOT NULL,
`kol` int(64) NOT NULL
);


Делаем парсер:
#!/usr/local/bin/perl
use Mysql;
print «start\n»;
my $host=«localhost»;
my $database=«geo»;
my $user=«geo»;
my $table=«all»;
my $password=«ололопаролько»;
my $db = Mysql->Connect($host,$database,$user,$password);
$sql=«SELECT * FROM `$table`»;
$sth=$db->Query($sql);
arr=$sth->FetchRow;
my $vsego=0;
my $unik=0;
my $ok=0;
#открываем файл, кидаем данные в массив и обрабатываем.
open (GL, "/home/klef/geo/GeoLiteCity-Location.csv");
@geo_arr=;
close (GL);
$max=$#geo_arr;
for ($i=0;$i<=$#geo_arr;$i++) {
$vsego++;
@geo_a=split(",",$geo_arr[$i]);
if ($#geo_a>6) {
$ok++;
$x=sprintf("%.0f",$geo_a[5]);
$y=sprintf("%.0f",$geo_a[6]);
#ищем есть ли такие координаты
$sql=«SELECT * FROM `$table` WHERE x=$x AND y=$y»;
$sth=$db->Query($sql);
arr=$sth->FetchRow;
if ($#arr>=0) {
#есть? отлично меняем счетчик
$nov_s=$arr[3]+1;
$sql=«UPDATE `$database`.`$table` SET `kol` = '$nov_s' WHERE `$table`.`x` = $x AND `$table`.`y` =$y AND `$table`.`kol` =$arr[3] LIMIT 1 ;»;
$db->Query($sql);
} else {
#нет, ну ничего, это поправимо
$sql=«INSERT INTO `$database`.`$table` (`x` ,`y` ,`kol`)VALUES ('$x', '$y', '1');»;
$db->Query($sql);
print " New";
$unik++;
}
}
}
#это чтоб в конце нам радостно показал сколько он отлапатил
$vsego_p=sprintf("%.2f",$vsego*100/$max);
$ok_p=sprintf("%.2f",$ok*100/$max);
$unik_p=sprintf("%.2f",$unik*100/$max);
$itog="\nObrabotano: $vsego_p% ($vsego)\nNormalnih: $ok_p% ($ok)\nUnikalnih: $unik_p% ($unik)\n";
print $itog;
print «END\n»;


И понеслось, основная проблема во времени обработки была, не в том что данных много, а то что мы их тупо и без красивых решений перебираем по порядку.

php скрипт по генерации картинки:
#!/usr/local/bin/php
<?php
$host=«localhost:3306»;
$user=«geo»;
$data=«geo»;
$pass=«опять паролько»;
//куда сейвим картинку
$patch="/data/web/geo.ip.png";
$z=0;
//создаем имайдж и цвет
$img = imagecreatetruecolor(600, 400);
$ink = imagecolorallocate($img, 0, 255, 0);
//обращаемся к БД и выбираем данные
$db = mysql_connect($host,$user,$pass) or die («Bolt MySQL „);
$db_sel=mysql_select_db($data,$db) or die (“Bolt BD „);
$sql = mysql_query(“SELECT * FROM `all`;») or die («Bolt zapros»);
$num_rows = mysql_num_rows ($sql) or die («Bolt Num Stroki»);
$vib=mysql_num_rows($sql) or die («Bolt Stroki»);
for ($i=1;$i<=$num_rows;$i++) {
$tmp=mysql_fetch_array($sql);
$y=$tmp['x'];
$x=$tmp['y'];
//ставим точку с нужной кординатой
imagesetpixel($img,300+$x,200-$y,$ink);
}
mysql_close($db);
//напоследок ставим пометку и сохраняем файл
ImageString($img, 2, 10, 5, «Geo IP: ».date(«Y M j H:i»)." Kolvo: ".$num_rows.", $ink);
imagetruecolortopalette ($img, true, 255 );
imagepng($img,$patch);
imagedestroy($img);
?>


Итог парсера:
Всего 310072 строк в файле
уникальных координат (с округлением) 8226, что всего 2,65%
Размер таблицы 384 килобайт :)
Время обработки:
На заполнения БД: порядка пару часов.
На формирование картинки, в приделах миллисекунд.

p.s. позже произвел повторную обработку, но с округлением до первого знака после запятой, итог файл разрешением 3600 на 1800
При ресайзе до нужного разрешения рабочего стола получаем неплохую обоинку.

p.p.s. позже попробовал сгенерировать файл размером 36000 на 18000 (соответственно округление до двух знаков после запятой) но php на пару с GD малость надорвались с таким разрешением, разбираться почему и отчего уже не было ни желания, ни сил.
Теги:
Хабы:
+84
Комментарии45

Публикации