Определение географического положения по IP-адресу
Задался я недавно вопросом, как определить страну, город и координаты юзера по его IP-адресу, погуглил немного и вот хочу поделиться результатами моих научных изысканий.
Первым мне на глаза попался сайт http://ipgeobase.ru/ . Скачал я их скрипт и БД. Протестировал и обнаружил:
- Скрипт http://ipgeobase.ru/files/soft/search_cgi.tar.gz при поиске по базе для начала загружает всю базу в память а потом уже производит по ней поиск, что очень плохо по вполне очевидной причине большого и неразумного потребления ресурсов памяти. На моем хостинге этот скрипт вообще отваливался, так и не дорабатывая до конца загрузку базы в память.
- Сама база данных создана совершенно нерациональным образом, обладая неразумной избыточностью. Вы и сами можете оценить этот факт, скачав базу по адресу http://ipgeobase.ru/files/db/Main/db_files.tar.gz
Так что лучше вообще не обращать внимания на предлагаемые алгоритм и базу, а сразу перейти к более корректным методам.
Вот здесь http://www.maxmind.com/ я обнаружил действительно быстрый и эффективный алгоритм определения географического положения пользователя по его IP-адресу.
Причем, эта компания предоставляет как платные, так и бесплатные версии ПО. Платная версия стоит $370, плюс по $90 в месяц за обновления. Бесплатная версия покрывает 99.5% территории, а платная – 99.8% на уровне стран, и 79% и 83% соответственно на уровне городов. Как видим, разница в точности несущественная и для наших скромных некоммерческих целей подойдет и бесплатная версия ПО. Обновления баз для обеих версий производятся раз в месяц, поэтому надо периодически скачивать новую версию базы данных.
Использовать ПО достаточно просто.
- Качаем здесь саму библиотеку.
- Качаем здесь последнюю версию базы данных. В распакованном виде база занимает около 28 мегабайт.
- Заливаем на хостинг файлы GeoLiteCity.dat, geoipcity.inc, geoip.inc, geoipregionvars.php. Пусть они лежат в одной директории на сервере.
- В той же директории создаете файл 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 =
Вот и все. Не боги горшки обжигают.
В тему:
Ваш сайт суперовый!!! Очень интересная и нужная инфа, которая редко где встречается. Все доступно написано и главное: действительно РАБОЧИЕ примеры. Спасибо! Продолжайте в том-же духе! И порекламируйте себя, вас много людей ищет!
Cаня, June 14, 2009 4:36 pmОчень понравился этот метод опр. Geo IP. Мы раньше использовали метод реализующий подключение к некой базе данных на удалённом сервере. Попробовали 2 какие то бесплатные, работают не стабильно. Мы зависимы от их сервера. А в данном примере мы работаем с базой лежащей у нас же на сервере. Обновляйся только раз в месяц и всё. Очень удобно.
Но. Немного не разобрался я. Нам бы нужен конечный результат в другом виде. Во первых нам достаточно только кода страны. Во вторых нам нужна именно переменная $country_code, чтобы использовать её дальше в своих скриптах. Что то не сообразил я где взять эту переменную. Не подскажете ли ?
Попробуйте после строки:
$res = GeoIP_record_by_addr($gi,$_SERVER[‘REMOTE_ADDR’]);
взять из результата нужный Вам код:
$res[‘country_code’];
Здравствуйте, к сожалению так не получилось.
Но, поразмыслив, я пришёл к изящному решению своей проблемы.
Я просто прерываю исполнение цикла foreach сразу после первой строки массива (а это по любому и есть код страны), и дальше использу просто переменную $val.
$val)
{
if ($key==country_code) break;
}
$my_code = $val;
echo ” $val”;
?>
Juri, October 9, 2009 4:35 pmИзвиняюсь, немного не точно изложил свою идею.
Я прерываю цикл не после первой строки, а после того как значение $key будет равно country_code.
Вот полный рабочий код.
$val)
{
if ($key==country_code) break;
}
$my_code = $val;
echo ” $my_code”;
?>
Да, я ошибся. Переменная $res это не ассоциированный массив, а объект. А потому обращение к его полям производится при помощи оператора “->”, таким образом нужно делать так:
$res = GeoIP_record_by_addr($gi,$_SERVER[‘REMOTE_ADDR’]);
echo $res->country_code;
Выражаю, свою благодарность автору! Информация действительно, познавательная. Заранее приношу извинения, но собрать все это, у меня так и не получилось,вероятно все дело в настройках PHP. Подскажите, как должен быть собран PHP,чтоб все корректно отображалось?
Vermut, October 11, 2009 4:02 pmНужно понять, где именно происходит ошибка. У меня, например, с первого раза тоже ничего не заработало :).
Попробуйте в папке, где лежит скрипт, создать файл .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. Просто эти функции уже где-то ранее были объявлены у хостера и происходла попытка переопределения имен.
Ошибка происходит, с возращением аргумента функции foreach(),
В браузере выглядит следующим образом:
“Warning: Invalid argument supplied for foreach()”
Значит у вас установлен 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);
Спасибо за советы; рискну предположить, что ошибка вызываемая в моем случае, связана с отсутствием информации по моему ip в GeoLiteCity.dat
в результате чего функция возращает нулевое значение.
Такой вариант тоже возможен. Мой тестовый пример на то и тестовый, что не проверяет возвращаемый результат на допустимость. Действительно, если айпишник в базе не существует, то функция 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”;
}
Вячеслав, извините за беспокойство. Прекрасная статья, спасибо! Очень помогла! Все доходчиво написано. На счет ошибки все верно, именно с вышеперечисленной проблемой я и столкнулся.(И еще раз мои извинения, в примере выше не ровно легли “кавычки”)
P.S Сайту нужен форум!
Мы прислушаемся к Вашему совету и сделаем форум. 🙂
В комментариях действительно wordpress зачем-то заменяет кавычки так что тексты потом перед вставкой в PHP-документы редактировать надо.
Кстати, в предпоследней статье я написал про создание easyAPI с реализацией функций GeoIP, как считаете, насколько полезным может быть подобное начинание? И какие еще полезные сервисы можно реализовать в рамках GeoIP?
Из сервисов задействующих GeoIP, могу порекомендовать разобраться с блогом Андрея Цюпко: http://blog.zupko.info/?p=221&cpage=1#comment-3161
Но он основан на flash если быть точнее на Action Script 3, Flex, программировании. Вобщем, с flex-ом я разобрался, а вот что касается серверной части, есть вопросы…
По поводу создания easyAPI продолжайте трудится,ведь проиграл не тот, у кого не получилось, а тот, кто опустил руки 🙂
Неплохо описано.
Название области можно показать так:
echo $GEOIP_REGION_NAME[$res->country_code][$res->region];
Видимо, файл geoipregionvars.php для этого и предназначен.
Да, вы совершенно правы. 🙂
Вячеслав Гринин, October 25, 2009 4:41 pmСпасибо. Очень хорошая статья. Я воспользовался с небольшой правкой под свои задачи. Подружил 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Если реклама в тему, то мы ее с радостью публикуем. 🙂 Особенно, когда она – взаимная… Ваш блог, кстати, по духу сходен с нашим, потому что мы пишем только о том, что попробовали сами.
admin, February 19, 2010 9:23 amСтатья действительно очень полезная – сейчас такое редкость.
Из отзывов видно, что у всех всё получилось.
Но мне чёт легко эта тема не даётся.
А именно: если в апаче подключен модуль геоайпи, то при выполнении кода возникает ошибка 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.
Одна ерунда – не работает.
Очевидно, существует ещё что-то, что помогает работать данному коду (моды, модули, хитрые настройки апача).
Кто-нибудь встречался с такой ошибкой?
Буду признателен за любые мысли по теме, т.к. мои мысли закончились.
И все таки очень похоже на то, что вашего 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Ваш скрипт выдал вот что:
country_code=RU;country_code3=RUS;country_name=Russian Federation;region=71;city=Yekaterinburg;latitude=56.85;longitude=60.6;
По ходу, дело в настройках самого сервера.
Айпи определился правильно.
Текущая база весит 30 метров. Именно она и не работает.
Дело в скорее в настройках PHP.
Вам стоит попробовать установить уровень отображения ошибок таким образом чтобы он отображал Errors, Warnings & Notices, возможно это даст дополнительную информацию к размышлению. Проверьте еще также совпадает ли размер базы с точностью до байта.
admin, May 10, 2010 9:13 pmЗдравствуйте! Вижу Вы модифицировали http://easyapi.ru/geoip/get.php чтобы он выдавал русские названия городов, как и обещали. Скажите пожалуйста с помошью чего делали?
Дмитрий, October 3, 2010 8:19 pmНедолгий поиск по интернету привел меня к базе в которой я обнаружил соответствия русских и английских названий городов в одной таблице. Собственно, эту таблицу я и использовал для апдейта своей базы. Правда, возникает ряд тонкостей – не везде английские названия городов написаны одинаково, а потому пришлось поступить так:
Если посмотрите в правый верхний угол сайта easy4web.ru то увидите там информер, который отображает погоду в городе, где проживает пользователь. Каждый день я имею некоторое количество посетителей из разных городов, и некоторые из них не имеют соответствия английского и русского названия (их к счастью не так много). Именно такие города я сохраняю в отдельную таблицу, которую периодически просматриваю вручную и нахожу им соответствующие русские названия. Таким образом, база постепенно становится более полной.
Добрый день,
озабочен выбором сервиса для определения города по айпи. Хотелось бы четко определять крупные города РФ + населенные пункты Московской области. Погуглил, из рекомендаций понял, что есть смысл обратить внимание на http://techinfo.net.ru/ip2ruscity/ (коммерческая) http://ipgeobase.ru и http://www.maxmind.com/ (некоммерческая)
Какая БД на Ваш взгляд наиболее информативная? Если есть какие-то более достойные БД?
Заранее спасибо! Вопрос очень актуален
О первом варианте ничего не знаю. Попробуете – расскажете мне. 🙂 Остальные два вариант пробовал. IPGeoBase – жутко ресурсоемкая, алгоритмы поиска не оптимизированы, прежде чем работать, она загружает в память всю базу, а на хостинге вам вряд ли кто-то даст столько места, она у меня на домашнем компе не пошла, не хватило памяти. MaxMind -наиболее оптимальна по качеству, тем более у нее есть бесплатная версия, вы особо и не почувстствуете, что она ограничивает вас по объему бд.
Вячеслав Гринин, December 20, 2010 10:16 pmСпасибо за оперативность. А что можете сказать по поводу http://easyapi.ru ? Как много она знает сетей и как часто она обновляется?
Allexander, December 21, 2010 9:58 amОна использует тот же MaxMind, обновляю базы где-то раз в 1-2 месяца. Единственное ее отличие от базы MaxMind – отображение еще и русских названий городов.
Вячеслав Гринин, December 21, 2010 12:32 pmЗдравствуйте. “пробил” адрес через базу MaxMind и через вашу – результат вышел немного разный – на MaxMind не нашел город. Если вы говорите, что базу взяли у них – как такое возможно?
Виталий, December 24, 2010 1:52 pmИ для меня загадка. 🙂 Но скорее всего они в каждом релизе базы просто случайным образом убирают несколько процентов адресов, так что в моем релизе ваш адрес попал, а в новом релизе этот адрес удалили.
Вячеслав Гринин, December 24, 2010 4:28 pmЕще 2 вопроса:
1. Ваша база недоступна для скачивания (та что с русскими названиями)?
2. Есть же еще вариант csv-файла БД, но он чего-то весит 120 мб (выше предложена в фаормате dat и весом 28 мб). От чего такая разница?
1 варианта базы на скачиваение нет.
2 разница заключена в самом вопросе, csv – это текстовый формат, с огромной избыточностью, по сути это просто таблица текстом, нечто вроде этого:
111.222.111.111; Москва; Московская область; Россия;
111.222.111.214; Одинцово; Московская область; Россия;
Как видите здесь очень много повторов. А в формате dat – это двоичная база, упакованная, без избыточности.
Вячеслав Гринин, December 25, 2010 2:53 pmА как же вы тогда совместили две базы – русскую и вот эту в формате dat? Ее ведь не отредактировать. Дополнительные запросы чтоли делаете для выборки из русской версии?
Виталий, December 25, 2010 3:15 pmДа, делаю дополнительные запросы к MySQL базе данных.
Вячеслав Гринин, December 25, 2010 6:28 pmА можно как то использовать в цыкле?
while(база ip в мускуле)
{
$res = GeoIP_record_by_addr($gi,$ip);
$city = $res->city;
}
или в функции?
Alexandr, August 9, 2011 10:03 amНу так, ничего не мешает использовать это ровно так как вы написали.
Вячеслав Гринин, August 9, 2011 7:27 pmСкачал БД по ссылке “здесь”. Всё работает, но моего воронежского IP-адреса (212.45.225.43) в базе данных вообще нет, а если я работаю на ноутбуке (85.26.184.118), определяется Пятигорск (должен быть Краснодар, так как IP связан c мегафоновской SIM-картой в модеме). Подскажите, где можно скачать обновлённую БД.
gudwi1, August 16, 2011 7:06 pmИменно по этой ссылке и выкладывается самая свежая база адресов. Я думаю, что в бесплатной базе могут отсутствовать некоторые адреса, авторы, возможно, тем самым побуждают нас купить у них платную версию.
Вячеслав Гринин, August 17, 2011 9:46 amНепонятно, новсё перестало работать. Работало 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Добрый день. Искал долго как реализовать мою идею, она схожа с вашей. Но мне надо сделать так чтобы пользователю выдавалась инфа не его местоположения, а, например, ближайшего к нему (по его местурасположению) филиала магазина. Как это сделать?
Андрей, February 11, 2013 8:03 amИзвините, но мне кажется вы пытаетесь почесать левой ногой правое ухо. Не проще ли для вычисления вашего обидчика (или просто интересующую личность) воспользоваться платным вариантом антивируса Avast internet s. Он вам неплохо покажет то, что вас интересует. Если хотите на халяву, то на сайте [censored].ucoz.ru выложена информация как это сделать. И, вообще, мне кажется администратор того сайта интересуется такими вопросами. Попробуйте с ним напару выдать что-нибудь толковое на эту тему. То, что вы предлагаете трудоемко, и не поможет определить отправителя прошедшего анонимный сервер. А вообще тема перспективная.
frafe, April 19, 2013 9:25 pmМой блог создан не для тех категорий, которые “я тебя по айпи вычислю!”, а для простых веб-программистов, которые хотят на своем сайте (а не на локальной машине) сделать хоть какой-нибудь сбор статистики по регионам посещения. Так что каммент не в тему. Вы, кажется, просто попытались попиарить свой сайт?
Вячеслав Гринин, April 20, 2013 7:48 pmСделал всё, как в статье. Всё сработало. Выдало список возможных значений. Но вот уже за четыре года никто так и не написал, как это практически применить-то? Видимо, для программистов (это не я) это так очевидно, что и говорить нечего. А могли бы вы написать, как применить всё это дело в таком ключе: какие сниппеты вставлять в нужных местах страницы, чтобы получилось типа: Hello, visitor from (название региона, например California). Причём желательно не код региона, а его имя чтобы вставлялось. И как то же самое для “город, регион” ?
Robot, April 21, 2013 7:47 pmВопрос отменяется, можете удалить. Мучался-мучался, сутки лазил по инету, тыкался-тыкался и разобрался.
Robot, April 21, 2013 9:52 pmа где скачать файлы geoipcity.inc, geoip.inc, geoipregionvars.php
Анатолий, December 5, 2014 10:55 am