php blog magyarul

Geotargeting: avagy IP helymeghatározás

Talán többeket foglalkoztat, hogy miként lehet IP cím alapján meghatározni, hogy az adott IP címhez tartozó gép melyik városban van. Az IP helymeghatározás szempontjából elengedhetetlen, hogy rendelkezzünk a helymeghatározáshoz szükséges úgynevezett földrajzi helymeghatározó adatbázissal (geotargeting database). Cikkünkben az IP helymeghatározás ingyenesen kivitelezhető lehetőségeit boncolgatjuk.

Személy szerint a MaxMind - GeoLite City adatbázisát preferálom, tehát a péda is erre az adatbázisra fog épülni. Ez az adatbázis a GeoIP City nevű fizetős adatbázis egy kevésbé pontos, ingyenes változata. A fizetős változatból létezik webszolgáltatáson keresztül nyújtott megoldás is, kedvező áron: 20$/50.000 lekérdezés. Ne feledjük el, hogy a lekérdezések eredményei saját gyorsítótárazással megfejelve tárolhatjuk, ezzel elérve a sebességnövekedést, illetve a lekérdezések számának drasztikus csökkenését.

Na de térjünk vissza az ingyenes megoldásra. Két megoldás létezik az adatbázis használatára. Az egyik, hogy letöltjük CSV formátumban és importáljuk a geotargeting adatbázist a saját adatbázisunkba. A másik, hogy bináris formátumban töltjük le az adatokat, és emellé még PHP modult is kapunk, amellyel egyszerűen tudunk lekérdezéseket intézni a geotargeting adatbázis felé. A mellékelt modul képes a Shared Memory-ben gyorsítótárazni a lekérdezések eredményét, ezzel jelentősen megnövelve a lekérdezések sebességét. A bináris állomány minden hónap elején frissül, emiatt közvetlen linket nem adok rá, de a MaxMind - GeoLite City oldaláról letölthető. Ugyaninnen kiindulva találjuk meg a csak PHP forrásból álló modult is, amit a http://www.maxmind.com/download/geoip/api/php/ címről tölthetünk le. A következő állományokra lesz szükségünk: geoip.inc, geoipcity.inc és geoipregionvars.php. Természetesen a bináris adatbázist tartalmazó fájlt ki kell tömörítenünk, ekkor megkapjuk a használható, GeoLiteCity.dat nevű állományunkat.

Szóval ha ezekkel végeztünk, kezdjünk is bele, nézzünk néhány példát arra, hogy hogyan tudjuk felhasználni a letöltött állományokat. A példa letölthető innen: Geotargeting példa (adatbázis nélkül), és kipróbálható a következő címen: http://www.php-blog.hu/peldak/geotargeting/geotargeting.php

<?php
 
header("Content-Type: text/html; charset=utf-8");
 
require_once("geoipcity.inc");
 
function getIP()
{
    if ($ip = $_SERVER["HTTP_CLIENT_IP"])
    {
    }
    elseif ($ip = $_SERVER["HTTP_X_FORWARDED_FOR"])
    {
        $multi = array_reverse(explode(",",$ip));
        $ip = trim($multi[0]);
    }
    elseif ($ip = $_SERVER["REMOTE_ADDR"])
    {
    }
    else
    {
        $ip = "UNKNOWN";
    }
    return $ip;
}
 
function cs_conv($string) {
    return mb_convert_encoding($string, 'utf-8');
}
 
//Kliens ip címe
$client_ip = getIP();
 
// Ha használni szeretnénk a Shared Memory-t
// geoip_load_shared_mem('GeoLiteCity.dat');
// $gi = geoip_open('GeoLiteCity.dat',GEOIP_SHARED_MEMORY);
 
// Ha nem akarjuk használni a Shared Memory-t
$gi = geoip_open('GeoLiteCity.dat',GEOIP_STANDARD);
 
// lekérdezzük az IP címhez tartozó adatokat
$record = geoip_record_by_addr($gi,$client_ip);
 
echo '<b>IP címed</b>: '.
    cs_conv($client_ip).
    '<br />';
 
echo '<b>Ország kódja és neve</b>: '.
    cs_conv($record->country_code).', '.
    cs_conv($record->country_code3).', '.
    $record->country_name.
    '<br />';
 
echo '<b>Régió (megye) kódja és neve</b>: '.
    $record->region.', '.
    cs_conv($GEOIP_REGION_NAME[$record->country_code][$record->region]).
    '<br />';
 
echo '<b>Város</b>: '.
    cs_conv($record->city).
    '<br />';
 
echo '<b>WGS_latitude (szélességi fok)</b>: '.
    $record->latitude.
    '<br />';
 
echo '<b>WGS_longitude (hosszúsági fok)</b>: '.
    $record->longitude.
    '<br />';
 
echo '<b>DMA code (csak USA)</b>: '.
    $record->dma_code.
    '<br />';
 
echo '<b>Area code (csak USA)</b>: '.
    $record->area_code.
    '<br />';
 
geoip_close($gi);
 
?>

Tehát nézzük a példát az elejétől. Elsősorban azt mondjuk, hogy az oldalunk utf-8 kódolású. Mindenkinek javaslom hogy utf8 kódolást használjon ahol csak teheti, aminek több előnye van, mint kellemetlensége. Egyik ilyen kellemetlenség, hogy néhány esetben az adatokat utf8 kódolásba kell alakítani, mint jelen esetben is. Az átalakításra nagyon ritkán van szükség, de ez most pont beletartozik. Erre a cs_conv() függvényt használjuk, amin szerintem nincs mit magyarázni. A másik, már érdekesebb függvény a getIP(). Ezzel próbáljuk megmondani, hogy mi a kliens IP-jé. A $_SERVER globális változóból veszi ki az értékeket, a megfelelő sorrendben, ha esetleg nem tudná a legelsőből, megpróbálja a következőből, hátha Proxy-n keresztül (nem anonym) kapcsolódik a kliens az internetre.

Kétféleképpen tudjuk inicializálni a GeoIP API-ját, az egyik a standard, a másik az amikor használja a PHP Shared Memory kiterjesztését gyorsítótárazásra. Mi a standard megoldást válaszottuk. Ezt követő részben pedig példát láthatunk az egyes adatok lekérdezésére. Első körben az IP cím alapján lekérdezzük az adatokat, amelyeket egy objektumban kapunk meg. Az objektum egyes változói tartalmazzák a Ország kódot, ország nevét, megyét, várost stb.

Remélem, hogy sokaknak sikerült segítséget nyújtani ezzel a kis példával. Ha bármilyen kérdésed vagy észrevételed felmerült, kérdezz nyugodtan a hozzászólások között.

UPDATE @2008.09.18

Újabb getIP függvény, ANDREI ajánlásával, köszönjük!

26 hozzászólás, szólj hozzá Te is!

  1. Ezeket a megoldásokat alapvetően a dinamikus ip-s dsl szolgáltatások csapják agyon. Korábban próbálkoztam már én is ilyennel, traceroute-ok elemzésével, statisztikagyűjtéssel, de semmire nem lehet vele menni.

    Mondok egy példát: győri T-Online DSL kapcsolatom általában 84.0.0.0/16-ból szokott kapni ip címet, de van, amikor csúcsidőben tárcsázom újra, és ilyenkor nincs elég még ebből a tartományból sem. Ilyenkor kapok valami 84.1xx.0.0/16-ból, ami természetesen tökéletesen máshol van (mondjuk Békéscsabán). Mivel ilyen gyakran változik, és véletlenül sem tudod megmondani, hogy mit mikor ki kap meg, sajnos használhatatlan (egyedül az isp-kkel együttműködve lehetne ilyen szolgáltatást létrehozni, de nekik meg nem érdekük, privacy, vagy mi…).

    Amúgy most is a győri munkahelyemen ülve a script kiírta, hogy budapesti ip-m van.

    A másik meg, hogy mi alapján mondja azt, hogy egy adott IP cím melyik városban van? Megkérdezte az ISP-t, vagy csak a ripe-ot? Mert ripe-ből szinte minden magyarországi dinamikus ip cím budapesti lesz.

  2. IP címed: 84.0.236.98
    Ország kódja és neve: HU, HUN, Hungary
    Régió (megye) kódja és neve: 18, Szabolcs-Szatmar-Bereg
    Város: Nyíregyháza

    Ehhez képest én Fertődön, Gy-M-S-ben ülök egy irodában, hát kb. az ország egyik legmesszebbi pontja innen Nyíregyháza :D

  3. Szia, igen ez a megoldás nem tökéletes. A fizetős egy fokkal jobb, de mint a legtöbb dolog, magyarországon természetesen ez sem tud tökéletes lenni. Az adatbázist egyébként a következő módszerrel építik: “The idea behind GeoIP is simple but the process is complex. We employ user-entered location data from sites that ask web visitors to provide their geographic location. We then run millions of these datasets through a series of algorithms that identify, extract, and extrapolate location points for IP addresses” Tehát az egyes oldalakon ahol a felhasználó megadja a tartózkodási helyét, párosítják az IP-vel. Magyarországon a fő probléma az, hogy vannak olyan IP tartományok, amik több városban is használatosak. Például a Te esetedben. Azért kíváncsiságból érdemes lenne egy statisztikát mérni. Ki fogok rakni az oldalra egy igen/nem választós kérdést, hogy helyesen tippelt-e a script az ingyenes adatbázis alapján. Mert lehet hogy pont Te vagy az 1 a 100-ból, akinek nem jó, mondjuk kétlem :). Minden esetre azért jó lenne tudni milyen arányban mutat jó értéket, legalábbis Magyarországon.

  4. Szia Feki, az IP címed egyébként kitalálta rendesen?

  5. Amennyiben csak az ország meghatározása a fontos, tudom ajánlani a Soft77 adatbázisát, amely elég jónak mondható.

    http://software77.net/cgi-bin/ip-country/geo-ip.pl

    A címről le is tölthető csv formátumban, de van hozzá sql konverter is a letöltés szekcióban.

    A másik amitől meghalnak az ilyen mutatványok, a különböző proxyk. Például én hiába ülök Hollandiában, ha te egy belga IP címet fogsz látni (tányéros net). Persze lehet proxy bármely saját szerver vagy gép is. Sőt, a chello net egy csomó esetben Magyarország helyett Ausztriát vagy Hollandiát ír ki.

  6. Mindez a saját adatbázis azért jó, mert akkor nem kell külső forrástól függened állandóan. Az update szine mindennapos a keresztény webhosztingnál :). Szóval egyszerű és nagyszerű. Én például ez alapján tiltom Kínát, Vietnámot és Korea jelentős részét. Eddig bevállt és nem nagyon panaszkodnak emiatt.

  7. Csak egy apróság: miért getenv (függvényhívás) a $_SERVER tömb helyett, illetve miért mindig duplán meghíva? ;)

    Illetve a nevezéktan az lehetne egységes: camelcase vs aláhúzás.

    Üdv,
    Felhő

  8. thgab

    maxmind ez is
    http://j.maxmind.com/app/geoip.js
    de ez pontos.
    legalábbis nálam.

  9. Hodicska Gergely:
    THX, észre sem vettem, illetve nem is figyeltem. Két különböző helyről copy+paste miatt nem egységes a nevezéktan. :) Egyébként a $_SERVER-t szoktam használni, de ez egy nagyon-nagyon régi kódból megmaradt függvény.

    THGAB:
    A JS, amit mutattál szerintem a fizetős adatbázist használhatja, és nálam tényleg pontosabb. Módosítottam a példát: http://www.php-blog.hu/peldak/geotargeting/geotargeting.php
    Ennek a második felében az általad mutatott linken elérhető JS kódot használva írja ki az adatokat.

    Kíváncsi vagyok a visszajelzésekre hogy melyik milyen pontosan határozza meg a városokat.

  10. Andrei: olvastam hogy támadták a szerveredet, és megértem hogy tiltod a kínai ip címeket :). Mondjuk ország szinten az adatbázis sokkal pontosabb. Azért kíváncsi vagyok arra, hogy a példa második része mennyivel pontosabb, mert esetleg, meg lehetne oldani curl-al letölteni a js fájlt, regexp-el kiszedni az adatokat, és gyorsítótárazni. Nyilván a MaxMind nem ilyen felhasználásra szánja a Javascript alapú szolgáltatását, emiatt nem tudom hogy limitálva vannak-e a kérések száma IP címenként vagy nem, tehát azt sem tudom, hogy egy nagy látogatottságú oldalnál meddig működne vagy nem. Viszont le fogom tesztelni és az eredményeket közzé fogom tenni.

  11. Szia Gábor,

    Támadták, de nem Kínából. Azt már régen kilőttem. A legutóbbi sorozat amcsiktól jött proxyn keresztül.

    Nagy látogatottságú oldalnál cachelni kell, mert különben agyonvágod a limitet. Vagy fizetsz.

    Én a free kategóriában a soft77-nél jobb bázist nem találtam. Igaz, hogy városokat nem tartalmaz, meg long-lat cuccot. Felépítve mySQL-nek így sem kicsi (kb 4-5 mega). Butítva beletolom 2 megába.

  12. Esetleg, ha tudod a postot módosítani (miért is ne), akkor én ezt javasolnám neked:

    function getIP()
    {

    if ($ip = $_SERVER[”HTTP_CLIENT_IP”]) {}
    elseif ($ip = $_SERVER[”HTTP_X_FORWARDED_FOR”]){ $multi = array_reverse(explode(”,”,$ip)); $ip = trim($multi[0]);}
    elseif ($ip = $_SERVER[”REMOTE_ADDR”]) {}
    else {$ip = “UNKNOWN”;}

    return $ip;
    }

    Az van ugyanis, hogy a HTTP_X_FORWARDED_FOR sokszor vesszővel elválasztott értékkel tér vissza, amelynek legtöbbször a legutólsó tagja kell.

    Ezekre amúgy megadhatod a $HTTP_X_FORWARDED_FOR, $REMOTE_ADDR vagy $HTTP_X_CLIENT_IP formátumot is.

    Azért érdemes egy szűrést csinálni, ha valaki be akarja tárolni az eredményt pl mySQL-be. Mert akkor lehet injektelni ezeknek az értékeknek a manipulálásával.

    Csak gyorsan dobtam. A két {} direkt maradt benne, ha le akarod kezelni például $ip[client_ip] vagy $ip[forwarded], esetleg $ip[rem_addr] cuccal, hogy milyen módon jött a látogató. Vagy amit akarsz. Ekkor viszont ugyebár az $ip egy tömbként jön vissza, amit le kell kezelni.

  13. Andrei: Köszönöm! Nem vagyok teljesen otthon abban, hogy proxyn keresztül milyen headerek érkeznek a szerverhez. De most hogy mondod, rémlik hogy egyszer már találkoztam olyan esettel amikor a HTTP_X_FORWARDED_FOR-ban vesszővel elválasztva két IP cím is szerepelt.

  14. linduc

    Kipróbáltam..

    Régió (megye) kódja és neve: 23, Veszprem
    Város: Várpalota

    a js megtalált.. :)

    Régió (megye) kódja és neve: 20, Jasz-Nagykun-Szolnok
    Város: Szolnok

  15. Nekem az az érzésem, hogy a JS a pontosabb fizetős adatbázist használhatja. Szerintetek?

  16. “Nekem az az érzésem, hogy a JS a pontosabb fizetős adatbázist használhatja. Szerintetek?”

    http://j.maxmind.com/app/geoip.js címen elérhető js include
    Ország kódja és neve: HU, Hungary
    Régió (megye) kódja és neve: 09, Gyor-Moson-Sopron
    Város: Sopron
    WGS_latitude (szélességi fok): 47.6833
    WGS_longitude (hosszúsági fok): 16.6000
    Irányítószám:

    Szerintem is… :)

  17. Dzsemirokváj

    Nalam Bp-n nem találta el a kerületet :D

  18. Zseeros

    Nekem egyik sem működött! Externetes vagyok és nekem mindig Budapest-et hozta be, közben Sz-Sz-B megyében egy kis faluban élek/netezek!
    Szóval ez van!

  19. Nem sem működött egyik sem. t-online-os netem van egyébként, a php Budapestre tett a js pedig Pomázra. Valójában Balassagyarmatról netezek.

  20. MArceLL

    Nekem Győrből nézve Fertőd meg Nyúl jött ki városnak, és ha belegondolunk a juzer adja meg hol él ebből a pontatlanság, ennyi erővel mondjuk Győrben csinál valamit egy debreceni gyerek és regel egy oldalra ahonnan ezek kapják az adatot kész is a fals adat.

    Amugy meg nem hinném hogy a T online olyan gyakran átszervezné a /24 es tartományait hogy tegnap még érdi ma meg mondjuk székesfehérvári.

    Gondoljunk bele a sok felhasználó csak azt venné észre hogy volt net nincs net.
    Itt Győrben kb hetente van bontás észre vettem volna ha hoppá nincs net.

    Egyébként a 84.1 84.2 meg a régi matávnetes tartománybol is van már a Győri régiónak kiosztva ja meg van egy 78 vagy hányas /16 os tartomány is abbol is van.

  21. Tehát lehet azt mondani, hogy Magyarországon csak elvétve működik a dolog, és csak arra, jó hogy megmondjuk: Dunán innen vagy Dunán túl netezik vki? :) Minden esetre érdekes lenne egy igen/nem szavazás, hogy hány százalékban sikerül eltalálni a felhasználó tartózkodási helyét? Mi a véleményetek erről?

  22. laura

    sziasztok help plízz
    én nem értek az ilyen ketyerékhez szóval segítséget szeretnék kérni
    van két IP cím amire nagyon kíváncsi lennék h honnan ír mmint na..

    41.234.68.108
    41.234.68.23

    nagyon fontos lenne
    ha külföldi IP akkor is szeretném tudni nagyon köszi annak aki segít

  23. Szia Laura!

    http://cqcounter.com/whois/ itt találsz némi információt az IP címekről. Elméletileg ez az IP az egyitomi TEDATA cég ADSL szolgáltatásához kapcsolódik. Ami szerintem a legvalószínűb (ha spam érkezett), hogy a TEDATA egyik ügyfelének gépére trójai és/vagy vírus került, és az csinálhatja a problémát. Ebben az esetben teljesen mind1, hogy hol volt a gép ami spammelt. Ha akarod tiltsd ki külföldet, vagy az adott IP-ket, IP tartományt, vagy használj captca-t, vagy bármilyen más eszközt a nemkívánatos jelenségek kiszűrésére.

  24. aurelius

    Hello!

    A végzett szakomnak készített kapcsolattartó portálomon kipróbáltam ennek a js-nek a beágyazását. Nem fikázni jöttem, előre szólok, csak megosztom a tapasztalataimat. Nekem nagyon tetszik a dolog, és egy ideig még benne is hagyom az oldalban :)
    Pontosabb, mint a free megoldás, ez tény. De még a nagy városoknál is hibázgat. Debrecen helyett, ahonnan a legtöbben használják az oldalam, gyakran Budapest, ritkábban más megyeszékhely szerepel. A kis vidéki településeknél pedig jellemzően egy másik, környéki települést határoz meg, kb 10-15 km-es körzetből. Ahogy írták fentebb, magyar viszonyok… De ennek ellenére nem rossz kis találmány!

    Köszönöm a cikket, és /fav a blognak! :)

    Üdv!

  25. Én a free kategóriában a soft77-nél jobb bázist nem találtam. Igaz, hogy városokat nem tartalmaz, meg long-lat cuccot. Felépítve mySQL-nek így sem kicsi (kb 4-5 mega). Butítva beletolom 2 megába.

  26. Russel

    Hali
    szeretnék segítséget kérni ezzel kapcsolatban
    nem értek hozzá nagyon de arra lenne szükségem h én tudjak bevinni 1 IP címet és a program pedig kiírná nekem az ip-hez tartozó, fent említett adatokat.
    előre is köszi

Hozzászólás írása: “Geotargeting: avagy IP helymeghatározás”