Артём Мáлков

Получение местоположения (геопозиции) пользователя по IP-адресу на PHP+API

18 июн1 комм

Если вы задались вопросом о том, как (каким образом) можно определить город, регион или страну посетителя сайта зная его IP-адрес, то этот пост будет ответом на него.

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

В его основе (не считая PHP) будет лежать API. С помощью него, мы будем посылать определенные команды выбранному сервису, и в ответ на них получать нужные нам данные.

Такой подход я выбрал не случайно: он (особенно для новичков в программировании), на мой взгляд, проще в реализации + мы получаем актуальную (регулярно обновляемую) информацию (частота обновления зависит от выбранного сервиса). Здесь, конечно, как и в других вариантах решения поставленной задачи, есть свои минусы, в числе которых зависимость от внешних ресурсов, разная точность определения геопозиции, а также разного рода лимиты (ограничения). Говоря о последнем, поясню. Лимиты, как правило, представляют собой ограниченное количество (в час, день, месяц и так далее в зависимости от сервиса) запросов и распространяются они чаще всего на бесплатное использование сервисов, в платных же тарифах возможностей больше, и это логично, поскольку подобным (и не только) сервисам необходимо каким-то образом существовать и, на мой взгляд, бороться с недобросовестным использованием предоставленных инструментов.

Что-то опять много букв.

В общем, о нюансах я написал, техническую составляющую мы рассмотрим на примере бесплатной версии сервиса ip-api.com (предоставляет 45 запросов в минуту, подходит только для личного использования), но для более или менее серьезных проектов (даже для своих) рекомендую всё же посмотреть в сторону платных тарифов. Ниже я также приложу список других подобных сервисов, но сразу оговорюсь, что, возможно, потребуются корректировки в предложенный код из-за формата возвращаемых данных.

Поехали!

1. Первое, что нам нужно для того, чтобы определить местоположение пользователя – это получить его IP-адрес. Каким образом это делается, мы уже разбирали ранее. Все подробности можете почитать там, здесь же я просто возьму часть:

if(!empty($_SERVER['HTTP_CLIENT_IP'])) {

	$userIP = $_SERVER['HTTP_CLIENT_IP'];
	
} elseif(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {

	$userIP = $_SERVER['HTTP_X_FORWARDED_FOR'];
	
} else {

	$userIP = $_SERVER['REMOTE_ADDR'];
	
}

2. Далее, собственно, составим свою небольшую функцию для получения данных от ip-api.com:

function getUserGeo() {

	if(!empty($_SERVER['HTTP_CLIENT_IP'])) {
	
		$userIP = $_SERVER['HTTP_CLIENT_IP'];
		
	} elseif(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
	
		$userIP = $_SERVER['HTTP_X_FORWARDED_FOR'];
		
	} else {
	
		$userIP = $_SERVER['REMOTE_ADDR'];
		
	}

	$ch = curl_init();

	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
	curl_setopt($ch, CURLOPT_HEADER, 0);
	curl_setopt($ch, CURLOPT_URL, 'http://ip-api.com/json/'.$userIP);  

	$data = curl_exec($ch);

	curl_close($ch);

	return json_decode($data, true);

}

Пример её использования:

$getUserGeo = getUserGeo();

if($getUserGeo['status'] == 'success') {

	echo 'Ваш город: '.$getUserGeo['city'];

} else {

	echo 'Ошибка получения данных: '.$getUserGeo['message'];

}

Основной список возвращаемых полей (не требуют явного указания):

status - статус получения данных (success или fail)
message - сообщение об ошибке (возвращается, если status = fail)
country - страна
countryCode - двухбуквенный код страны по ISO 3166-1 alpha-2
region - код региона/штата (FIPS или ISO)
regionName - регион/штат
city - город
zip - индекс
lat - широта
lon - долгота
timezone - часовой пояс
isp - имя провайдера
org - название организации
as - номер AS и организация через пробел (RIR). Пусто для IP-блоков, не объявленных в таблицах BGP
query - IP-адрес, используемый для запроса

Также можно получить (уже с явным указанием):

continent - континент
continentCode - двухбуквенный код континента
district - район города
offset - cмещение часового пояса UTC DST в секундах
currency - национальная валюта
asname - имя AS (RIR)
reverse - обратный DNS IP-адреса (может задержать ответ)
mobile - мобильное подключение
proxy - используется прокси, VPN или Tor
hosting - хостинг, колокация или центр обработки данных

Для перечисления возвращаемых полей в функции строку:

curl_setopt($ch, CURLOPT_URL, 'http://ip-api.com/json/'.$userIP);

заменяете на:

curl_setopt($ch, CURLOPT_URL, 'http://ip-api.com/json/'.$userIP.'?fields=continentCode,currency');

Где «continentCode,currency» - нужные вам поля.

Данные от сервиса приходят на английском языке, по необходимости язык можно поменять, например, на русский. Для этого в функции строку:

curl_setopt($ch, CURLOPT_URL, 'http://ip-api.com/json/'.$userIP);

замените на:

curl_setopt($ch, CURLOPT_URL, 'http://ip-api.com/json/'.$userIP.'?lang=ru');

Список поддерживаемых языков (на момент написания поста):

en - английский (язык по умолчанию)
de - немецкий
es - испанский
pt-BR - португальский
fr - французский
ja - японский
zh-CN - китайский
ru - русский

Что касается лимита в 45 запросов: он (лимит) действует только на бесплатное использование сервиса, и в случае преодоления его происходит временная блокировка доступа до обновления лимитов (как вы поняли, это происходит каждую минуту). Если же вы регулярно превышаете лимит, время блокировки возрастает до одного часа.

Не нарушайте и удачных вам разработок!

Обещанный список сервисов:

https://www.ipinfodb.com
http://geoiplookup.net
https://geo.ipify.org
https://ipbase.com
https://www.myip.com/api-docs/
https://www.abstractapi.com/ip-geolocation-api
https://ipwhois.io/ru/
https://ipstack.com
https://www.geoplugin.com
https://ipinfo.io

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

Рекомендуем к просмотру
Как получить API KEY для работы с сервисом YouTube?
Статьи и советы
Как разбить строку на массив на PHP/JavaScript?
Статьи и советы
Как ограничить доступ к сайту/элементу для определенной страны или города на PHP?
Посты
1
комментарий
Форма комментирования этого поста скрыта. Авторизуйтесь, чтобы расширить привилегии гостевого посещения и получить необходимую помощь от сообщества Pandoge.
    • 1
    1067
      •  Команда Pandoge
    18 июн в 21:07

    Несколько нюансов по работе с сервисом ip-api.com:

    1. Если вы сами указываете список возвращаемых полей, стандартный набор полей пропадает.

    2. Если вы хотите и поменять язык и задать свой список возвращаемых полей, сделать это можно с помощью &:

    curl_setopt($ch, CURLOPT_URL, 'http://ip-api.com/json/'.$userIP.'?fields=continentCode,currency&lang=ru');

    3. Помимо PHP, определение геопозиции пользователя можно осуществить на JavaScript. Пример найдете здесь - https://ip-api.com/docs/api:json

    4. В документации сервиса говорится, что вместе с возвращаемыми данными приходят два заголовка - X-Rl и X-Ttl (количество оставшихся запросов и время до их обновления соответственно). Чтобы не увеличивать ваше время блокировки при достижении лимита и зря не тревожить сервис, рекомендуется проверять количество оставшихся запросов и если их значение равно 0, не запрашивать данные на время X-Ttl в секундах.

    По правде говоря, я не смог смоделировать ситуацию, когда лимит был бы достигнут, но не смотря на это, держите доработанную функцию с учётом требования сервиса:

    function getUserGeoLimits() {
    
    	if(file_exists('./X-Rl.txt') && file_exists('./X-Ttl.txt')) {
    
    		$X_Rl = @file_get_contents('./X-Rl.txt');
    		$X_Ttl = @file_get_contents('./X-Ttl.txt');
    
    		if($X_Rl == 0 && time() < $X_Ttl) {
    
    			return true;
    
    		}
    
    		return false;
    
    	} else {
    
    		return false;
    
    	}
    
    }
    
    function getUserGeo() {
    
    	if(!empty($_SERVER['HTTP_CLIENT_IP'])) {
    	
    		$userIP = $_SERVER['HTTP_CLIENT_IP'];
    		
    	} elseif(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
    	
    		$userIP = $_SERVER['HTTP_X_FORWARDED_FOR'];
    		
    	} else {
    	
    		$userIP = $_SERVER['REMOTE_ADDR'];
    		
    	}
    
    	if(!getUserGeoLimits()) {
    
    		$ch = curl_init();
    
    		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    		curl_setopt($ch, CURLOPT_HEADER, 0);
    		curl_setopt($ch, CURLOPT_URL, 'http://ip-api.com/json/'.$userIP);
    		curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($ch, $header) {
    
    			$len = strlen($header);
    
    			$header = explode(':', $header, 2);
    
    			if(count($header) < 2) {
    
    				return $len;
    
    			}
    
    			if(trim($header[0]) == 'X-Rl') {
    
    				@file_put_contents('./X-Rl.txt', intval(trim($header[1])));
    
    			} elseif(trim($header[0]) == 'X-Ttl') {
    
    				@file_put_contents('./X-Ttl.txt', time() + intval(trim($header[1])));
    
    			}
    
    			return $len;
    
    		});
    
    		$data = curl_exec($ch);
    
    		curl_close($ch);
    
    	} else {
    
    		$data = json_encode([
    			'status' => 'fail',
    			'message' => 'Достигнут лимит сервиса'
    		]);
    
    	}
    
    	return json_decode($data, true);
    
    }
Подняться наверх
«Pandoge» - помощник вебмастера