Ta strona używa ciasteczek (cookies), dzięki którym nasz serwis może działać lepiej. Dowiedz się więcej OK, rozumiem
WebHelp.pl Warsztat Artykuły Geolokalizacja użytkownika na podstawie adresu IP

Warsztat / Artykuły i tutoriale

Geolokalizacja użytkownika na podstawie adresu IP

Bartosz Romanowski 28 stycznia 2011 komentarze ()

Nie ma skutecznej w stu procentach metody na określenie w miarę dokładnej lokalizacji użytkownika na podstawie jego adresu IP. Istnieją jednak metody sprawdzające się w nieco mniejszym, ale wystarczającym do większości zastosowań stopniu.

Do zlokalizowania użytkownika potrzebujemy bazy wiążącej adresy IP z rzeczywistymi lokalizacjami. My skorzystamy z serwisu IPInfoDB, który bazuje na danych firmy MaxMind i udostępnia bezpłatne API geolokalizacyjne oraz zestawy danych w formie zrzutu bazy danych MySQL oraz w formacie CSV. Skuteczność bazy można sprawdzić na specjalnej stronie. Niestety, jeśli chodzi o Polskę, to dokładność określenia miejscowości użytkownika pozostawia wiele do życzenia - z moich doświadczeń wynika, że można co najwyżej brać pod uwagę województwo, a i to nie ze stuprocentową pewnością.

Korzystanie z API

Aby móc skorzystać z interfejsu konieczna jest darmowa rejestracja, po której otrzymamy nasz własny klucz API. Interfejs może zwracać dane w formatach XML i JSON - my skorzystamy z pierwszej opcji. IPInfoDB udostępnia gotową klasę w PHP, która ułatwia korzystanie z usługi, ale nie jest ona idealna, tak więc trochę ją sobie poprawimy i rozbudujemy.

Kod: Zaznacz cały
<?php
// plik: ipinfodb.class.php
class IPInfoDB
{
	protected $errors = array();
	protected $showTimezone = false;
	protected $showCity = true;
	protected $service = 'api.ipinfodb.com';
	protected $version = 'v2';
	protected $apiKey = '';
	protected $apiCity = 'ip_query.php';
	protected $apiCountry = 'ip_query_country.php';

	public function setKey($key)
	{
		if(!empty($key))
			$this->apiKey = $key;
	}

	public function showTimezone()
	{
		$this->showTimezone = true;
	}
	
	public function showCountryOnly()
	{
		$this->showCity = false;
	}

	public function getError()
	{
		return implode("n", $this->errors);
	}

	public function getGeoLocation($host)
	{
		$ip = @gethostbyname($host);

		if(preg_match('/^(?:25[0-5]|2[0-4]d|1dd|[1-9]d|d)(?:[.](?:25[0-5]|2[0-4]d|1dd|[1-9]d|d)){3}$/', $ip))
		{
			$request = 'http://'.$this->service.'/'.$this->version.'/';
			if($this->showCity)
			{
				$request .= $this->apiCity;
			}
			else
			{
				$request .= $this->apiCountry;
			}
			$request .= '?key='.$this->apiKey.'&ip='.$ip;
			if($this->showTimezone && $this->showCity)
			{
				$request .= "&timezone=true";
			}

			$xml = @file_get_contents($request);

			try
			{
				$response = @new SimpleXMLElement($xml);
				foreach($response as $field => $value)
				{
					$result[(string)$field] = (string)$value;
				}
				return $result;
			}
			catch(Exception $e)
			{
				$this->errors[] = $e->getMessage();
				return;
			}
		}
		$this->errors[] = '"'.$host.'" nie jest prawidłowym adresem IP lub nazwą hosta.';
		return;
	}
}
?>

Najprostszy przykład jej wykorzystania:

Kod: Zaznacz cały
require('ipinfodb.class.php');
$ipinfo = new IPInfoDB();
$ipinfo->setKey('_KLUCZ_API_');

$geodata = $ipinfo->getGeoLocation('83.19.70.178');

var_dump($geodata);

Zwrócone dane mają następującą postać:

Kod: Zaznacz cały
array
  'Status' => string 'OK'
  'CountryCode' => string 'PL'
  'CountryName' => string 'Poland'
  'RegionCode' => string '83'
  'RegionName' => string 'Slaskie'
  'City' => string 'Katowice'
  'ZipPostalCode' => string ''
  'Latitude' => string '50.2667'
  'Longitude' => string '19.0167'
  'Gmtoffset' => string '0'
  'Dstoffset' => string '0'
  'TimezoneName' => string ''
  'Isdst' => string ''
  'Ip' => string '83.19.70.178'

Większość tych danych nie wymaga dodatkowych wyjaśnień, warto jednak zwrócić uwagę na kilka z nich: RegionCode - kod regionu w standardzie FIPS (pełna lista kodów) City - jeśli w bazie nie ma informacji o miejscowości dla danego adresu IP, w tym polu może pojawić się "Poland" Latitude i Longitude - szerokość i długość geograficzna

Jak widać, pola dotyczące strefy czasowej są puste. Jeśli potrzebujemy tych informacji, wystarczy w naszym skrypcie dodać wywołanie metody showTimezone():

Kod: Zaznacz cały
$ipinfo->showTimezone();
$geodata = $ipinfo->getGeoLocation('83.18.70.178');

Jeśli potrzebujemy tylko informacji dotyczących kraju, wystarczy przed zapytaniem o dane wywołać metodę showCountryOnly() (w tym przypadku nigdy nie dostaniemy informacji o strefie czasowej, ponieważ niektóre kraje leżą w więcej niż jednej):

Kod: Zaznacz cały
$ipinfo->showCountryOnly();
$geodata = $ipinfo->getGeoLocation('83.18.70.178');

Korzystanie z interfejsów IPInfoDB jest bezpłatne i nie ma jakichś limitów ilości wykonywanych do niego zapytań. Jeśli jednak będziemy wywoływać API częściej niż 2 razy na sekundę, nasze zapytania trafią do "kolejki", co oznacza, że czas odpowiedzi na nie wydłuży się. Aby nie przekroczyć limitu dwóch zapytań na sekundę dobrze jest w jakiś sposób przechowywać informacje o lokalizacji użytkownika, na przykład w ciasteczku (cookie), tak aby nie wywoływać API dla każdej generowanej przez niego odsłony. Stwórzmy więc prosty mechanizm tego typu:

Kod: Zaznacz cały
require('ipinfodb.class.php');

if(!$_COOKIE["geolocation"])
{
    $ipinfo = new IPInfoDB();
    $ipinfo->setKey('_KLUCZ_API_');
    
    $geodata = $ipinfo->getGeoLocation($_SERVER['REMOTE_ADDR']);
 
    if($geodata['Status'] == 'OK')
    {
        setcookie("geolocation", base64_encode(serialize($geodata)), time() + 3600 * 24); // ustawiamy ciasteczko na 24 godziny
    }
}
else
{
    $geodata = unserialize(base64_decode($_COOKIE["geolocation"]));
}

var_dump($geodata);

W dokumentacji można znaleźć przykłady korzystania z API za pomocą innych niż PHP języków programowania (Ruby, Python, JavaScript, ASP), a także gotowe moduły dla popularnych CMSów (WordPress, Drupal, Joomla).

Własna baza geolokalizacyjna

Jak napisałem na wstępie, istnieje również możliwość pobrania zestawu danych geolokalizacyjnych i załadowania ich do własnej bazy, dzięki czemu nie będziemy musieli korzystać z API udostępnianego przez IPInfoDB. Możemy skorzystać z kilku zestawów, różniących się dokładnością oraz strukturą. Z uwagi na ilość danych, na potrzeby tego tekstu wykorzystamy bazę z danymi pozwalającymi tylko na określenie kraju. Sugeruję wybrać strukturę z jedną tabelą, która zajmuje więcej miejsca w bazie (na chwilę obecną 6,4 MB), ale zapytania do niej będą wykonywać się szybciej (brak złączeń).

Po pobraniu odpowiedniego zestawu danych (Complete Country, one data table, format: MySQL) musimy załadować je do bazy, na przykład za pomocą phpMyAdmina. W tym celu skorzystamy z zakładki "Import", gdzie wybieramy plik do zaimportowania i klikamy "Wykonaj". Gdy proces zakończy się, możemy przystąpić do napisania prostego skryptu pobierającego dane.

Kod: Zaznacz cały
$ip = '83.18.70.178';
$sql = "SELECT country_code, country_name FROM ip_group_country where ip_start <= INET_ATON('".$ip."') order by ip_start desc limit 1";
$result = mysql_query($sql) or die(mysql_error());
$row = mysql_fetch_array($result);
var_dump($row);

Wynikiem działania tego skryptu będzie kod i nazwa kraju. Zapytania do tabeli zawierającej dane dla miejscowości (ip_group_city) wykonuje się podobnie, z tym że zawiera ona dodatkowo nazwę miejscowości, nazwę i kod regionu (w przypadku Polski chodzi o województwo) oraz długość i szerokość geograficzną.

Więcej przykładów korzystania z zestawów danych można znaleźć w dokumentacji.

Masz pytania lub wątpliwości? Odwiedź nasze forum dyskusyjne.

Bartosz Romanowski

Programista, gadżeciarz, krytyczny miłośnik produktów Apple, fan ciężkich brzmień i niepoprawny pesymista.


Komentarze


HTML CSS JavaScript PHP bazy danych MySQL Flash grafika framework hosting domeny pozycjonowanie wordpress Facebook