Под термином Гео-таргетинг я подразумеваю возможность установить точный адрес посетителя, пришедшего к вам. Эту задачу я сделала год назад и вот хочу сейчас поделиться итогом.
Для этого я собралась использовать язык PHP. Я не очень хорошо знаю этот язык, поэтому попросила помочь моих коллег-программистов — вот она веб-разработка по-женски. Хорошие программисты любят делать то чего никто до них не делал, или что сделать чрезвычайно трудно, а в одиночку кажется нереально, технологи нашей славной фирмы Analog-group (в частности спасибо mr.troll) не исключение. Сайт компании конечно чрезвычайно ужасен, но это хитрый ход чтобы не привлекать клиентов, а то приходят и платят много денег, приходится работать =). Вероятно начальство запретит эту статью в бложике публиковать, но всё же написать надо.
Итак, о гео-таргетинге, в россии его умеют делать только огромные стат-серверы, типа liveinternet, spylog, и российские почтовики. Ещё модуль geo-ip есть можно купить в составе umi-cms, этот модуль весьма распростаранён, его разрабатывала CN-Software, заявленная скорость работы 500 запросов в секунду. На практике я не думаю что это так, а вот у меня скорость работы получилась выше (например вот тут за секунду определение городов, группировка и вывод таблицы, полюбуйтесь) =), кроме того их база данных российских адресов всегда оставляла желать лучшего, по моему, до сих пор часть диапазона московского стрима считается канадскими адресами.
Единственные кто знают точную базу данных российских ip-адресов это регистраторы, в частности nic.ru, который в бытность свою сделал ipgeobase.ru, и автоматически выкладывает свежие базы данных каждый день. На сайте предложены программные способы поиска в базе данных, но во-первых, они на языке Perl, во-вторых, совершенно не оптимизированны.
Сейчас я с некоей помощью напишу как оптимизировать и выполнить поиск данных на языке PHP.
Итак, задача: сделать поиск по странам, а для россии ещё и по городам. Для поиска по странам качаем файл с ip стран и распаковываем из него файл GeoIPCountryWhois.csv.
Софт который предлагается для поиска по этому файлу тоже не оптимизирован, поэтому оптимизируем сам файл:
<? $handle = @fopen('GeoIPCountryWhois.csv', "r"); $troll = @fopen('GeoIPCountry_troll.txt', "w"); $cn = @fopen('country_names.txt', "w"); $str=''; $i=0; $cnts=array(); while (!feof($handle)) { $i++; $buffer = fgets($handle); if(strlen($buffer)<4)break; $temp=explode(',',$buffer,6); $ip = explode('.',substr($temp[0],1,-1)); $ip = chr($ip[0]).chr($ip[1]).chr($ip[2]).chr($ip[3]); $str.=$ip; $ip = explode('.',substr($temp[1],1,-1)); $ip = chr($ip[0]).chr($ip[1]).chr($ip[2]).chr($ip[3]); $temp[4]=substr($temp[4],1,-1); $str.=$ip.$temp[4]; if(!$cnts[$temp[4]]){ $cnts[$temp[4]]=substr($temp[5],1,-2); fwrite($cn,$temp[4].'|'.$cnts[$temp[4]]."\r\n"); } #if(strlen($str)<10)die($buffer.'fuck'.$i.$str); } print_R($cnts); fwrite($troll,$str); fclose($handle); fclose($troll); fclose($cn); ?>
Вместо 8ми мегабайт данных получили всего 1 мегабайт, неплохо, да? Теперь о том что мы тут сделали, исходный файл представляет собой записи типа:
"2.6.190.56","2.6.190.63","33996344","33996351","GB","United Kingdom" "3.0.0.0","4.17.135.31","50331648","68257567","US","United States" "4.17.135.32","4.17.135.63","68257568","68257599","CA","Canada"
А мы на каждый такой блок отводим всего 10 байт, оставляя только 4 байта начала блока, 4 байта конца, и 2х буквенный код страны. Так же этот файл создаёт файл с расшифровками 2х буквенных кодов стран. Который можно потом отредактировать в блокноте и перевести наиболее частые названия, у нас получился такой файл: http://top.2s.ru/geo_ip/country_names.txt
C сайта ipgeobase.ru надо скачать файл db_files.tar.gz (или .zip). Распаковать оттуда файл cidr_ru_slave_index.db в папку geo_ip, туда же скинуть остальное хозяйство. Этот файл небольшой 300кб, так что его мы не оптимизировали.
Для поиска по российским ip нужно загрузить этот файл в оперативную память (массив), а потом уже искать по массиву, функции для поиска такие:
<? function load_master(){ global $master; if(is_array($master) and count($master)>0)return; $master=array(); $master_index = 0; $handle = @fopen('geo_ip/cidr_ru_master_index.db', "r"); if ($handle) { while (!feof($handle)) { $buffer = fgets($handle, 4096); list($start,$stop,$inetnum,$country,$city,$region,$district,$status,$slave,$n_slave) = split("\t",$buffer); $master[$master_index] = array('start' => $start,'stop' => $stop,'inetnum' => $inetnum,'country' => $country, 'city'=>$city,'region'=>$region,'district'=>$district,'status' => $status,'slave' => $slave,'n_slave' => $n_slave); $master_index++; } fclose($handle); } } load_master(); function ip_geo($ip){ global $master; if(is_numeric($ip)) $ADDRESS=$ip; else { $bits=explode('.',$ip,4); $ADDRESS = $bits[0]*256*256*256+$bits[1]*256*256+$bits[2]*256+$bits[3]; } $MIN = 0; $MAX = count($master); $master_block_index = -1; $ok = 0; $main_index = -1; while($MIN<$MAX){ $master_block_index = (int)(($MIN+$MAX)/2); if ($ADDRESS > $master[$master_block_index]['stop']){ $MIN = $master_block_index+1;continue; } if ($ADDRESS < $master[$master_block_index]['start']){ $MAX = $master_block_index; continue; } if($ADDRESS >= $master[$master_block_index]['start'] && $ADDRESS <= $master[$master_block_index]['stop']){ $main_index = $master_block_index; $ok=1; break; } } return $main_index; } function ip_geo_world($ip){ if(is_numeric($ip)) $ADDRESS=$ip; else { $bits=explode('.',$ip,4); $ADDRESS = $bits[0]*256*256*256+$bits[1]*256*256+$bits[2]*256+$bits[3]; } $path='geo_ip/GeoIPCountry_troll.txt'; $fp = fopen($path, 'rb'); $MIN = 0; $MAX = filesize($path)/10; $master_block_index = -1; $ok = 0; $main_index = -1; while($MIN<$MAX){ $master_block_index = (int)(($MIN+$MAX)/2); fseek($fp, $master_block_index*10); $tmp=fread($fp,10); $start=ord($tmp[0])*256*256*256+ord($tmp[1])*256*256+ord($tmp[2])*256+ord($tmp[3]); $stop=ord($tmp[4])*256*256*256+ord($tmp[5])*256*256+ord($tmp[6])*256+ord($tmp[7]); if ($ADDRESS > $stop){ $MIN = $master_block_index+1;continue; } if ($ADDRESS < $start){ $MAX = $master_block_index;continue; } if($ADDRESS >= $start && $ADDRESS <= $stop){ $main_index = substr($tmp,8,2); $ok=1; break; } } return $main_index; } function country_name($code){ global $country_names; if(!$country_names){ $country_names=file('geo_ip/country_names.txt'); } foreach($country_names as $temp){ if(substr($temp,0,2)==$code) return substr($temp,3,-2); } } ?>
Подробно о том как мы ищем по всему миру: Файл с данными не загружается в оперативную память, что сильно её экономит. Поиск и чтение производится сразу с файла на диске. Поиск производится методом дихотомии, т.е. после каждой итерации в цикле мы знаем большее число нам надо получить или меньшее. Получается что из 100000 диапазонов мы можем найти нужный с максимум 17й попытки. Скорость чтения из файла на самом деле колоссально быстрая, поэтому скорость поиска существенно опередит то что предложено umi-cms и CN software.
Пользоваться функциями можно так:
$ret=ip_geo($temp); $geo=''; if($ret>0){ $geo=$master[$ret]; $geo='<b>Россия, '.$geo['city'].' ('.$geo['region'].')</b>'; } else{ $geo=ip_geo_world($temp); $geo=country_name($geo); }
Где $temp может быть как в привычном формате 127.0.0.1 так и числом. Сначала выполняется поиск по россии, и если ничего не найдено, тогда уже ищется по всему миру.
Для того чтобы узнать точного владельца адресного пространства (провайдера) нужно искать по файлу cidr_ru_slave_index.db, но на практике это абсолютно не нужно. Ещё в том же файле может быть какое-нибудь название деревни или села, которое в мастер файле названо ближайшим городом. Все наиболее крупные города в мастер файле есть. Файл содержит 232 городов, 76 областей и 7 регионов россии.
P.S. Щас ещё попробую оптимизировать мастер файл чтобы 300 килобайт массив в оперативную память не загружать, а искать прямо на диске, хотя сейчас поиск тоже использует дихотомию.
Комментарии:
svoloch
12.11.2009 21:46:59
svoloch
12.11.2009 21:48:09
Moony (Елена Лунная)
13.11.2009 01:39:35
Алексей
07.03.2010 19:35:38
Елена Лунная
07.03.2010 21:18:21
TEH3OP (Илья)
27.07.2011 14:24:55
Елена Лунная
28.07.2011 15:31:05
Дешевые авиабилеты - акции авиакомпаний (Дешевые авиабилеты - акции авиакомпаний)
22.08.2011 12:55:08