Определение географического положения по IP-адресу

Вячеслав Гринин, March 10, 2009

Задался я недавно вопросом, как определить страну, город и координаты юзера по его IP-адресу, погуглил немного и вот хочу поделиться результатами моих научных изысканий.

Первым мне на глаза попался сайт http://ipgeobase.ru/ . Скачал я их скрипт и БД. Протестировал и обнаружил:

  1. Скрипт http://ipgeobase.ru/files/soft/search_cgi.tar.gz при поиске по базе для начала загружает всю базу в память а потом уже производит по ней поиск, что очень плохо по вполне очевидной причине большого и неразумного потребления ресурсов памяти. На моем хостинге этот скрипт вообще отваливался, так и не дорабатывая до конца загрузку базы в память.
  2. Сама база данных создана совершенно нерациональным образом, обладая неразумной избыточностью. Вы и сами можете оценить этот факт, скачав базу по адресу http://ipgeobase.ru/files/db/Main/db_files.tar.gz

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

Вот здесь http://www.maxmind.com/ я обнаружил действительно быстрый и эффективный алгоритм определения географического положения пользователя по его IP-адресу.

Причем, эта компания предоставляет как платные, так и бесплатные версии ПО. Платная версия стоит $370, плюс по $90 в месяц за обновления. Бесплатная версия покрывает 99.5% территории, а платная – 99.8%  на уровне стран, и 79% и 83% соответственно на уровне городов. Как видим, разница в точности несущественная и для наших скромных некоммерческих целей подойдет и бесплатная версия ПО. Обновления баз для обеих версий производятся раз в месяц, поэтому надо периодически скачивать новую версию базы данных.

Использовать ПО достаточно просто.

  1. Качаем здесь саму библиотеку.
  2. Качаем здесь последнюю версию базы данных. В распакованном виде база занимает около 28 мегабайт.
  3. Заливаем на хостинг файлы GeoLiteCity.dat, geoipcity.inc, geoip.inc, geoipregionvars.php. Пусть они лежат в одной директории на сервере.
  4. В той же директории создаете файл whereami.php имеющий следующее содержимое:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Где я?</title>
</head>
<body>
<?php
include "geoipcity.inc";
$gi = geoip_open("GeoLiteCity.dat", GEOIP_STANDARD);
$res = GeoIP_record_by_addr($gi,$_SERVER['REMOTE_ADDR']);
geoip_close($gi);
foreach ($res as $key => $val)
{
    print "$key = $val<br>\n";
}
?>
</body>
</html>

Как можно видеть, в строке 8 подключается сама библиотека, в строке 9 создается объект GeoIP, которому в качестве параметра передается имя файла, содержащего базу данных, в строке 10 происходит сам поиск по базе данных. Функции GeoIP_record_by_addr в качестве параметров передаются объект GeoIP и IP-адрес (в данном случае это адрес хоста, вызвавшего страницу, то есть адрес пользователя, открывшего в бразуере вашу страницу). В строке 11 происходит корректное освобождение ресурсов, занятых под объект GeoIP. Следующий цикл foreach выводит на экран все поля объекта, который вернула функция GeoIP_record_by_addr.

Структура результата имеет вид:

country_code = RU
country_code3 = RUS
country_name = Russian Federation
region = 48
city = Moscow
postal_code =
latitude = 55.7522
longitude = 37.6156
area_code =
dma_code =

Вот и все. Не боги горшки обжигают.

В тему:

46комментариев

Ваш сайт суперовый!!! Очень интересная и нужная инфа, которая редко где встречается. Все доступно написано и главное: действительно РАБОЧИЕ примеры. Спасибо! Продолжайте в том-же духе! И порекламируйте себя, вас много людей ищет!

Cаня, June 14, 2009 4:36 pm Reply

Очень понравился этот метод опр. Geo IP. Мы раньше использовали метод реализующий подключение к некой базе данных на удалённом сервере. Попробовали 2 какие то бесплатные, работают не стабильно. Мы зависимы от их сервера. А в данном примере мы работаем с базой лежащей у нас же на сервере. Обновляйся только раз в месяц и всё. Очень удобно.
Но. Немного не разобрался я. Нам бы нужен конечный результат в другом виде. Во первых нам достаточно только кода страны. Во вторых нам нужна именно переменная $country_code, чтобы использовать её дальше в своих скриптах. Что то не сообразил я где взять эту переменную. Не подскажете ли ?

Juri, October 7, 2009 12:25 pm Reply

Попробуйте после строки:
$res = GeoIP_record_by_addr($gi,$_SERVER['REMOTE_ADDR']);
взять из результата нужный Вам код:
$res['country_code'];

Вячеслав Гринин, October 8, 2009 7:09 pm Reply

Здравствуйте, к сожалению так не получилось.
Но, поразмыслив, я пришёл к изящному решению своей проблемы.
Я просто прерываю исполнение цикла foreach сразу после первой строки массива (а это по любому и есть код страны), и дальше использу просто переменную $val.

$val)
{
if ($key==country_code) break;
}

$my_code = $val;
echo ” $val”;

?>

Juri, October 9, 2009 4:35 pm Reply

Извиняюсь, немного не точно изложил свою идею.
Я прерываю цикл не после первой строки, а после того как значение $key будет равно country_code.
Вот полный рабочий код.

$val)
{
if ($key==country_code) break;
}

$my_code = $val;
echo ” $my_code”;
?>

Juri, October 9, 2009 4:41 pm Reply

Да, я ошибся. Переменная $res это не ассоциированный массив, а объект. А потому обращение к его полям производится при помощи оператора “->”, таким образом нужно делать так:
$res = GeoIP_record_by_addr($gi,$_SERVER['REMOTE_ADDR']);
echo $res->country_code;

Вячеслав Гринин, October 9, 2009 6:20 pm Reply

Выражаю, свою благодарность автору! Информация действительно, познавательная. Заранее приношу извинения, но собрать все это, у меня так и не получилось,вероятно все дело в настройках PHP. Подскажите, как должен быть собран PHP,чтоб все корректно отображалось?

Vermut, October 11, 2009 4:02 pm Reply

Нужно понять, где именно происходит ошибка. У меня, например, с первого раза тоже ничего не заработало :) .
Попробуйте в папке, где лежит скрипт, создать файл .htaccess с таким содержимым:
php_flag display_errors 1
php_flag display_startup_errors 1
чтобы PHP Отдавал вам ошибки прямо в браузер.
В моем случае в файле geoip.inc мне пришлось переименовать функции: geoip_country_name_by_name и geoip_country_code_by_name, я просто приписал им суффиксы 2. Просто эти функции уже где-то ранее были объявлены у хостера и происходла попытка переопределения имен.

Вячеслав Гринин, October 11, 2009 5:51 pm Reply

Ошибка происходит, с возращением аргумента функции foreach(),
В браузере выглядит следующим образом:
“Warning: Invalid argument supplied for foreach()”

Vermut, October 12, 2009 6:09 am Reply

Значит у вас установлен PHP версии ниже пятой. Дело в том, что именно в PHP5 встроены итераторы для свойств классов, годные для использования в циклах foreach. Для решения вашей проблемы вам нужно либо перейти на PHP5, либо вместо цикла foreach использовать доступ к нужным полям сразу по имени, вот так:

$gi = geoip_open(“GeoLiteCity.dat”, GEOIP_STANDARD);
$res = GeoIP_record_by_addr($gi,$_SERVER['REMOTE_ADDR']);
geoip_close($gi);

echo(“country_code=”.$res->country_code);
echo(“country_code3=”.$res->country_code3);
echo(“country_name=”.$res->country_name);
echo(“region=”.$res->region);
echo(“city=”.$res->city);

Вячеслав Гринин, October 12, 2009 8:34 am Reply

Спасибо за советы; рискну предположить, что ошибка вызываемая в моем случае, связана с отсутствием информации по моему ip в GeoLiteCity.dat
в результате чего функция возращает нулевое значение.

Vermut, October 12, 2009 4:20 pm Reply

Такой вариант тоже возможен. Мой тестовый пример на то и тестовый, что не проверяет возвращаемый результат на допустимость. Действительно, если айпишник в базе не существует, то функция GeoIP_record_by_addr возвращает нулевое значение и цикл foreach выдает Warning: Invalid argument supplied for foreach(). Поэтому предлагаю немного модифицировать пример:
include “geoipcity.inc”;
$gi = geoip_open(“GeoLiteCity.dat”, GEOIP_STANDARD);
$res = GeoIP_record_by_addr($gi,$_SERVER['REMOTE_ADDR']);geoip_close($gi);
if(!$res)
{
echo(“IP в базе не обнаружен”);
return;
}
foreach ($res as $key => $val)
{
print “$key = $val
\n”;
}

Вячеслав Гринин, October 12, 2009 6:00 pm Reply

Вячеслав, извините за беспокойство. Прекрасная статья, спасибо! Очень помогла! Все доходчиво написано. На счет ошибки все верно, именно с вышеперечисленной проблемой я и столкнулся.(И еще раз мои извинения, в примере выше не ровно легли “кавычки”)
P.S Сайту нужен форум!

Vermut, October 13, 2009 6:25 am Reply

Мы прислушаемся к Вашему совету и сделаем форум. :)
В комментариях действительно wordpress зачем-то заменяет кавычки так что тексты потом перед вставкой в PHP-документы редактировать надо.
Кстати, в предпоследней статье я написал про создание easyAPI с реализацией функций GeoIP, как считаете, насколько полезным может быть подобное начинание? И какие еще полезные сервисы можно реализовать в рамках GeoIP?

Вячеслав Гринин, October 13, 2009 9:18 am Reply

Из сервисов задействующих GeoIP, могу порекомендовать разобраться с блогом Андрея Цюпко: http://blog.zupko.info/?p=221&cpage=1#comment-3161
Но он основан на flash если быть точнее на Action Script 3, Flex, программировании. Вобщем, с flex-ом я разобрался, а вот что касается серверной части, есть вопросы…
По поводу создания easyAPI продолжайте трудится,ведь проиграл не тот, у кого не получилось, а тот, кто опустил руки :)

Vermut, October 13, 2009 11:09 am Reply

Неплохо описано.
Название области можно показать так:
echo $GEOIP_REGION_NAME[$res->country_code][$res->region];
Видимо, файл geoipregionvars.php для этого и предназначен.

wins, October 22, 2009 5:57 pm Reply

Да, вы совершенно правы. :)

Вячеслав Гринин, October 25, 2009 4:41 pm Reply

Спасибо. Очень хорошая статья. Я воспользовался с небольшой правкой под свои задачи. Подружил GeoIP и Google Maps API. Как раз то что мне нужно было получилось. Если интересно вот определитель местоположения http://mitasych.com/speed-test-ip, а вот описание http://mitasych.com/kak-opredelit-geograficheskoe-polozhenie-po-ip-i-svyazat-ego-s-google-maps Не сочтите за рекламу. Это я по теме “какие еще полезные сервисы можно реализовать в рамках GeoIP”.

mitas, February 19, 2010 8:00 am Reply

Если реклама в тему, то мы ее с радостью публикуем. :) Особенно, когда она – взаимная… Ваш блог, кстати, по духу сходен с нашим, потому что мы пишем только о том, что попробовали сами.

admin, February 19, 2010 9:23 am Reply

Статья действительно очень полезная – сейчас такое редкость.
Из отзывов видно, что у всех всё получилось.
Но мне чёт легко эта тема не даётся.
А именно: если в апаче подключен модуль геоайпи, то при выполнении кода возникает ошибка 500.
Её действительно можно избежать двумя путями: либо менять названия функций, либо убирать подключение модуля.
Но дальше этого я не смог уйти.
У меня определяется только часть данных, а именно:
country_code = RU
country_code3 = RUS
country_name = Russian Federation
region =
city =
postal_code =
latitude = 60
longitude = 100
area_code =
dma_code =
При этом, на данному сайти, и на сайте http://mitasych.com/speed-test-ip мой айпи определяется, и показыает координаты адекватно.
Думал, что бд устарела. Скачал новую. Бесполезно. Может права не те- установил 777.
Одна ерунда – не работает.
Очевидно, существует ещё что-то, что помогает работать данному коду (моды, модули, хитрые настройки апача).
Кто-нибудь встречался с такой ошибкой?
Буду признателен за любые мысли по теме, т.к. мои мысли закончились.

Олег, May 7, 2010 7:18 am Reply

И все таки очень похоже на то, что вашего IP в базе нет. Чаще всего в этом случае БД возвращает очень круглые координаты (60, 100). Ваш IP моим блогом определился, как 217.114.4.178. Попробуйте, зайдите на мой сервис EasyAPI вот по этой ссылке http://easyapi.ru/geoip/get.php и посмотрите, выдаст ли он название города. А потом еще попробуйте вызвать его задав жестко Ваш IP http://easyapi.ru/geoip/get.php?ip=217.114.4.178 и посмотрите что выйдет. Сравните, кстати IP которые выдаются моим скриптом и скриптом на http://mitasych.com/speed-test-ip. У меня например EasyAPI про Ваш IP адрес сказал следующее: country_code=RU;country_code3=RUS;country_name=Russian Federation;region=71;city=Yekaterinburg;latitude=56.85;longitude=60.6;

Собираюсь, кстати в ближайшем будущем модифицировать http://easyapi.ru/geoip/get.php чтобы он выдавал еще и русские названия городов(по возможности), а также русские названия области и страны.

Можете в своих целях пользоваться сервисом http://easyapi.ru/geoip/get.php, его описание вот здесь http://easy4web.ru/?p=607

admin, May 7, 2010 8:58 am Reply

Ваш скрипт выдал вот что:

country_code=RU;country_code3=RUS;country_name=Russian Federation;region=71;city=Yekaterinburg;latitude=56.85;longitude=60.6;

По ходу, дело в настройках самого сервера.
Айпи определился правильно.
Текущая база весит 30 метров. Именно она и не работает.
Дело в скорее в настройках PHP.

Олег, May 7, 2010 9:14 pm Reply

Вам стоит попробовать установить уровень отображения ошибок таким образом чтобы он отображал Errors, Warnings & Notices, возможно это даст дополнительную информацию к размышлению. Проверьте еще также совпадает ли размер базы с точностью до байта.

admin, May 10, 2010 9:13 pm Reply

Здравствуйте! Вижу Вы модифицировали http://easyapi.ru/geoip/get.php чтобы он выдавал русские названия городов, как и обещали. Скажите пожалуйста с помошью чего делали?

Дмитрий, October 3, 2010 8:19 pm Reply

Недолгий поиск по интернету привел меня к базе в которой я обнаружил соответствия русских и английских названий городов в одной таблице. Собственно, эту таблицу я и использовал для апдейта своей базы. Правда, возникает ряд тонкостей – не везде английские названия городов написаны одинаково, а потому пришлось поступить так:
Если посмотрите в правый верхний угол сайта easy4web.ru то увидите там информер, который отображает погоду в городе, где проживает пользователь. Каждый день я имею некоторое количество посетителей из разных городов, и некоторые из них не имеют соответствия английского и русского названия (их к счастью не так много). Именно такие города я сохраняю в отдельную таблицу, которую периодически просматриваю вручную и нахожу им соответствующие русские названия. Таким образом, база постепенно становится более полной.

admin, October 4, 2010 8:37 am Reply

Добрый день,
озабочен выбором сервиса для определения города по айпи. Хотелось бы четко определять крупные города РФ + населенные пункты Московской области. Погуглил, из рекомендаций понял, что есть смысл обратить внимание на http://techinfo.net.ru/ip2ruscity/ (коммерческая) http://ipgeobase.ru и http://www.maxmind.com/ (некоммерческая)
Какая БД на Ваш взгляд наиболее информативная? Если есть какие-то более достойные БД?
Заранее спасибо! Вопрос очень актуален

Allexander, December 20, 2010 2:11 pm Reply

О первом варианте ничего не знаю. Попробуете – расскажете мне. :) Остальные два вариант пробовал. IPGeoBase – жутко ресурсоемкая, алгоритмы поиска не оптимизированы, прежде чем работать, она загружает в память всю базу, а на хостинге вам вряд ли кто-то даст столько места, она у меня на домашнем компе не пошла, не хватило памяти. MaxMind -наиболее оптимальна по качеству, тем более у нее есть бесплатная версия, вы особо и не почувстствуете, что она ограничивает вас по объему бд.

Вячеслав Гринин, December 20, 2010 10:16 pm Reply

Спасибо за оперативность. А что можете сказать по поводу http://easyapi.ru ? Как много она знает сетей и как часто она обновляется?

Allexander, December 21, 2010 9:58 am Reply

Она использует тот же MaxMind, обновляю базы где-то раз в 1-2 месяца. Единственное ее отличие от базы MaxMind – отображение еще и русских названий городов.

Вячеслав Гринин, December 21, 2010 12:32 pm Reply

Здравствуйте. “пробил” адрес через базу MaxMind и через вашу – результат вышел немного разный – на MaxMind не нашел город. Если вы говорите, что базу взяли у них – как такое возможно?

Виталий, December 24, 2010 1:52 pm Reply

И для меня загадка. :) Но скорее всего они в каждом релизе базы просто случайным образом убирают несколько процентов адресов, так что в моем релизе ваш адрес попал, а в новом релизе этот адрес удалили.

Вячеслав Гринин, December 24, 2010 4:28 pm Reply

Еще 2 вопроса:
1. Ваша база недоступна для скачивания (та что с русскими названиями)?
2. Есть же еще вариант csv-файла БД, но он чего-то весит 120 мб (выше предложена в фаормате dat и весом 28 мб). От чего такая разница?

Виталий, December 25, 2010 10:56 am Reply

1 варианта базы на скачиваение нет.
2 разница заключена в самом вопросе, csv – это текстовый формат, с огромной избыточностью, по сути это просто таблица текстом, нечто вроде этого:
111.222.111.111; Москва; Московская область; Россия;
111.222.111.214; Одинцово; Московская область; Россия;

Как видите здесь очень много повторов. А в формате dat – это двоичная база, упакованная, без избыточности.

Вячеслав Гринин, December 25, 2010 2:53 pm Reply

А как же вы тогда совместили две базы – русскую и вот эту в формате dat? Ее ведь не отредактировать. Дополнительные запросы чтоли делаете для выборки из русской версии?

Виталий, December 25, 2010 3:15 pm Reply

Да, делаю дополнительные запросы к MySQL базе данных.

Вячеслав Гринин, December 25, 2010 6:28 pm Reply

А можно как то использовать в цыкле?

while(база ip в мускуле)
{
$res = GeoIP_record_by_addr($gi,$ip);
$city = $res->city;
}

или в функции?

Alexandr, August 9, 2011 10:03 am Reply

Ну так, ничего не мешает использовать это ровно так как вы написали.

Вячеслав Гринин, August 9, 2011 7:27 pm Reply

Скачал БД по ссылке “здесь”. Всё работает, но моего воронежского IP-адреса (212.45.225.43) в базе данных вообще нет, а если я работаю на ноутбуке (85.26.184.118), определяется Пятигорск (должен быть Краснодар, так как IP связан c мегафоновской SIM-картой в модеме). Подскажите, где можно скачать обновлённую БД.

gudwi1, August 16, 2011 7:06 pm Reply

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

Вячеслав Гринин, August 17, 2011 9:46 am Reply

Непонятно, новсё перестало работать. Работало 3 года, теперь нет.
Скачанные свежие файлы скриптов с maxmind.com выдают просто fatal error на строке 438 в файле geoip.inc

А если использовать старые файлы трёхлетней давности выдаёт вот что –

country_code =
country_code3 =
country_name =
region = 18
city = Tartu
latitude = 58.3661
longitude = 26.7361

Нет ни кода страны, ни имени страны. Почему ?

Юрий, January 15, 2013 2:59 pm Reply

Добрый день. Искал долго как реализовать мою идею, она схожа с вашей. Но мне надо сделать так чтобы пользователю выдавалась инфа не его местоположения, а, например, ближайшего к нему (по его местурасположению) филиала магазина. Как это сделать?

Андрей, February 11, 2013 8:03 am Reply

Извините, но мне кажется вы пытаетесь почесать левой ногой правое ухо. Не проще ли для вычисления вашего обидчика (или просто интересующую личность) воспользоваться платным вариантом антивируса Avast internet s. Он вам неплохо покажет то, что вас интересует. Если хотите на халяву, то на сайте [censored].ucoz.ru выложена информация как это сделать. И, вообще, мне кажется администратор того сайта интересуется такими вопросами. Попробуйте с ним напару выдать что-нибудь толковое на эту тему. То, что вы предлагаете трудоемко, и не поможет определить отправителя прошедшего анонимный сервер. А вообще тема перспективная.

frafe, April 19, 2013 9:25 pm Reply

Мой блог создан не для тех категорий, которые “я тебя по айпи вычислю!”, а для простых веб-программистов, которые хотят на своем сайте (а не на локальной машине) сделать хоть какой-нибудь сбор статистики по регионам посещения. Так что каммент не в тему. Вы, кажется, просто попытались попиарить свой сайт?

Вячеслав Гринин, April 20, 2013 7:48 pm Reply

Сделал всё, как в статье. Всё сработало. Выдало список возможных значений. Но вот уже за четыре года никто так и не написал, как это практически применить-то? Видимо, для программистов (это не я) это так очевидно, что и говорить нечего. А могли бы вы написать, как применить всё это дело в таком ключе: какие сниппеты вставлять в нужных местах страницы, чтобы получилось типа: Hello, visitor from (название региона, например California). Причём желательно не код региона, а его имя чтобы вставлялось. И как то же самое для “город, регион” ?

Robot, April 21, 2013 7:47 pm Reply

Вопрос отменяется, можете удалить. Мучался-мучался, сутки лазил по инету, тыкался-тыкался и разобрался.

Robot, April 21, 2013 9:52 pm Reply

а где скачать файлы geoipcity.inc, geoip.inc, geoipregionvars.php

Анатолий, December 5, 2014 10:55 am Reply
Ваше имя
Ваш email*
Ваш сайт
Текст вашего комментария:

Поиск по блогу:
Подписаться:
Популярные:
Облако тегов:
Разное:
Счетчик: