PHP 使用 GeoLiteCity 库解析 IP 为地理位置

关于把 IP 地址转换为地理位置可以使用网络上很多的 API,好处就是不用在本地存储一个 IP 数据库,而且一般网络上的 IP 库会自动更新,不利的地方就是太依赖于网络,性能表现也可能会弱些。比如像下面的 API:

http://api.hostip.info/get_html.php?ip=58.63.236.31
http://api.hostip.info/flag.php?ip=58.63.236.31

这里介绍 PHP 如何使用 GeoLiteCity.dat 库把 IP 转换为地理位置,GeoLiteCity.dat 可以在 http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz 下,解压出 GeoLiteCity.dat,即可,我们可以手动去更新新的 IP 库。

下面 PHP 解析 IP 的过程参考自 WordPress 插件 Visitor Maps and Who’s Online 的实现。可以找到该插件的两个文件 include-whos-online-geoip.php 和 visitor-maps.php 告诉了我们怎么做。你可以点击这里的链接下载到这两个文件,我这里把 include-whos-online-geoip.php 改名为 geoipcity.inc.php,然后参考 visitor-maps.php 中的 get_location_info($user_ip) 函数,那么我们可以写出自己的解析 IP 地址的程序 resolve_ip.php:

city)) ? $record->city : '';  //城市
    $location_info['state_name']   = (isset($record->country_code) && isset($record->region)) //州名
          ? $GEOIP_REGION_NAME[$record->country_code][$record->region] : '';
    $location_info['state_code']   = (isset($record->region)) ? strtoupper($record->region) : ''; //州代号
    $location_info['country_name'] = (isset($record->country_name)) ? $record->country_name : '--'; //国家名
    $location_info['country_code'] = (isset($record->country_code)) ? strtoupper($record->country_code) : '--'; //国家代号
    $location_info['latitude']     = (isset($record->latitude)) ? $record->latitude : '0';   //维度
    $location_info['longitude']    = (isset($record->longitude)) ? $record->longitude : '0'; //经度
    //php 站点设置了 utf-8 字符集必要时进行转码
    $charset = 'utf-8';
    // this fixes accent characters on UTF-8, only when the blog charset is set to UTF-8
    if ( strtolower($charset) == 'utf-8' && function_exists('utf8_encode') ) {
        if ($location_info['city_name'] != '' ) {
            $location_info['city_name'] = utf8_encode($location_info['city_name']);
        }
        if ($location_info['state_name'] != '') {
            $location_info['state_name'] = utf8_encode($location_info['state_name']);
        }
        if ($location_info['country_name'] != '') {
            $location_info['country_name'] = utf8_encode($location_info['country_name']);
        }
    }
    return $location_info;
}
//查询一个 IP 测试下
$record = get_ip_record("206.19.49.154");
var_dump($record);
$location = get_location_info("206.19.49.154");
var_dump($location);
?>

执行后输出如下(可以作为系统脚本直接用 php resolve_ip.php 来执行):

object(geoiprecord_VMWO)#2 (10) {
  ["country_code"]=>
  string(2) "US"
  ["country_code3"]=>
  string(3) "USA"
  ["country_name"]=>
  string(13) "United States"
  ["region"]=>
  string(2) "TX"
  ["city"]=>
  string(10) "Richardson"
  ["postal_code"]=>
  string(5) "75080"
  ["latitude"]=>
  float(32.9722)
  ["longitude"]=>
  float(-96.7376)
  ["area_code"]=>
  int(972)
  ["dma_code"]=>
  float(623)
}
array(7) {
  ["city_name"]=>
  string(10) "Richardson"
  ["state_name"]=>
  string(5) "Texas"
  ["state_code"]=>
  string(2) "TX"
  ["country_name"]=>
  string(13) "United States"
  ["country_code"]=>
  string(2) "US"
  ["latitude"]=>
  float(32.9722)
  ["longitude"]=>
  float(-96.7376)
}

 

只要取你想要的数据就是了,里面还有诸如区号,邮编等数所,GeoLiteCity.dat 是个二进制文件,比普通文本要紧凑省空间。

不知道您有没有多留一份心,有无浏览链接:http://geolite.maxmind.com/download/geoip/api/php/,是这样的:

geopi_11

看到 GeoIp 给我们提供了不少的例子,那么多 sample.php,而实际上前面用到的 geoipcity.inc.php,就是 geopip.inc、geoipcity.inc 和 geoipregionvars.php 三个程序的内容合体。

再往上看:

geopi_21

官方提供的 API 何止 PHP 啊,几乎能全线满足您的实际需求了,c、java、perl、python、vb、ruby、tcl 等……,放其他程序里以后也不用愁了。

再进到 http://geolite.maxmind.com/download/geoip/database/ 瞧瞧:

geopi_31

正考虑着呢,不是说 IPv4 快用净了吗?IPv6 的数据也正为我们准备着呢?当然,天朝的 IPv9 恐怕永远不会有的。

本只是把 Visitor Maps and Who’s Online 里的解析 IP 的做法抽出来用用,可总能不断 深入再深入,不知道可喜还是可怕了。

其實很久以前就注意到,某些計數器可以很準確的判定IP來源的位置,甚至連所在城市都可以顯示出來
後來才知道是因為有GeoIP這樣的東西
MaxMind – GeoIP | IP Address Location Technology
這是MaxMind開發的東西,有不同平台的API和定期更新的”IP-位置”資料庫
如果要在網站上應用的話,可以找Apache module,perl和php的API

在Apache上面的模組叫”mod_geoip
さすが,租用的主機空間不可能給我隨便安裝模組,所以先在自己的電腦試…
不過仔細看才發現那檔案是linux上面用的,只有個c檔要自己make install
好吧…只好跳過…不想為了這東西去裝linux

再來試試perl,它的module是”Geo::IP
這也是要安裝的,所以先在自己的電腦試..
不過不知道是不是activeperl的版本問題,根本就裝不起來…orz
在目前的主機上面雖然可以自己安裝perl module,但是卻限制CPAN上面的東西,還好搜尋了一下就發現這個模組
Apache2::Geo::IP 
但是不知道為什麼,安裝到最後出現錯誤:

 CODE:
  CPAN.pm: Going to build R/RK/RKOBES/Apache-GeoIP-1.99.tar.gz

Please install either mod_perl 1 or mod_perl 2 first at Makefile.PL line 118.
No 'Makefile' created  RKOBES/Apache-GeoIP-1.99.tar.gz
/usr/bin/perl Makefile.PL PREFIX=/home/jwintaiw/perl/usr -- NOT OK
Running make install
Make had some problems, won't install

呃阿…orz
還好仔細看,發現主機裡面有預裝另外一個類似功能的”Geo::IPfree
它會從ip或主機名尋找之後回傳三個變數的陣列:二文字國碼,國家全名,IP
小試了一下可以動作,不過速度無敵慢!

最後嘗試PHP API
網頁上面有geoip和geoipcity兩種,前者是從ip/主機名尋找之後回傳二文字國碼和國家全名;後者是回傳更詳細的位置資訊,包含國家,程式和經緯度
不過兩者資料庫差很多,geoip需要的資料庫大約1MB附近,但是geocity的資料庫快30MB…|||
這邊照著該網頁上放的範例自己弄了一個測試的程式,有興趣可以試試…
http://snow-sugar.net/lookup.php (會放多久不確定,因為database太大)
同樣的東西也在自己電腦的自架網站動作確認,感覺大概像這樣
8184315216_f2s

其實最重要的應用不在這裡,而是可以利用辨識來源IP的國家進而讓server作不同的動作,例如台港的IP就自動轉入中文頁面,歐美的IP就自動轉入英文頁面,韓國的IP就block等等…因為IP對應位置的資料庫有持續更新,也讓誤動做的可能減少

CC BY-NC-SA 4.0 PHP 使用 GeoLiteCity 库解析 IP 为地理位置 by 桔子小窝 is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

发表回复

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据