地理定位 (Geolocation):比你想像的容易

Jeremy Kendall 在 PHP Architecture 有一篇文章介紹怎樣使你的網站加入地理定位 (geolocation) 的功能,好處是可以根據訪客的身處的位置,提供適合的內容和語言,令他們享受更貼心的服務。加入這項功能比你想像的容易,甚至完全免費,Jeremy 提供了兩個解決方案,任何一個都可以在十分鐘內搞定。

Jeremy 說,地理定位屬於一個完全解決了的問題,很多人提供了解決方案,我們沒有必要自行編寫這項功能,他介紹了兩個免費的應用程式界面 (API),一個是 Maxmind 的 GeoLite API,另一個是 Quova

GeoLite

GeoLite 的確有些獨特,他們的服務是免費的的,無須登記,他們提供了數據庫下載。這意味著數據存放在你自己的網站上,並利用他們提供的 PHP 函式庫來存取數據庫。

以下的步驟讓 GeoLite 在你的網頁應用程序中運行:

  • 下載 GeoLiteCity 數據庫
  • 把數據庫解壓在你的網頁應用程序中一個可讀取的子目錄
  • 安裝 PEAR GeoLite 函式庫( pear install Net_GeoIP
  • 編程!

你需要編寫的程式可說簡單得無法置信,下面是 Jeremy 測試用的程式:

 PHP |  copy code |? 
1
$geoip = Net_GeoIP::getInstance(dirname(__FILE__) . '/data/GeoLiteCity.dat');
2
$ipaddress = '72.30.2.43'; // Yahoo!
3
$location = $geoip->lookupLocation($ipaddress);
4
var_dump($location);

程式的輸出是這樣:

object(Net_GeoIP_Location)[2]
    protected 'aData' =>
        array
        'countryCode' => string 'US' (length=2)
        'countryCode3' => string 'USA' (length=3)
        'countryName' => string 'United States' (length=13)
        'region' => string 'CA' (length=2)
        'city' => string 'Sunnyvale' (length=9)
        'postalCode' => string '94089' (length=5)
        'latitude' => float 37.4249
        'longitude' => float -122.0074
        'areaCode' => int 408
        'dmaCode' => float 807

Net_GeoIP_Location 物件使用 __get()__set() 魔術成員函式,所以從這物件中檢索數據,只要編寫簡單的 $location->city;

Quova

GeoLite 的安裝和使用的確非常簡單,但為了有一個比較公道和全面的概念,Jeremy 找來另一個同樣是免費的 Quova 測試。

首先我們要申請一個免費的 Quova 開發者帳戶 ,並獲得 API 密鑰。Quova 有十分出色的文檔,Jeremy 在很短時間內便把它啟動和運作。很可惜,他們提供的 PHP 範例卻有點醜陋,Jeremy 花了數分鐘時間把它改寫得漂亮些:

 PHP |  copy code |? 
01
/**
02
 * Quova ipinfo API class
03
 *
04
 * @category    Example
05
 * @package     Example_Quova
06
 * @subpackage  GeoIP
07
 * @version     $Id$
08
 */
09
/**
10
 * Uses Quova's GeoIP API to get geographical location by IP address
11
 *
12
 * To obtain your Quova API key (apikey) and the shared secret
13
 * that you need to build a digital signature, register your
14
 * application at http://developer.quova.com/.
15
 *
16
 * @category    Example
17
 * @package     Example_Quova
18
 * @subpackage  GeoIP
19
 */
20
class Example_Quova_GeoIP
21
{
22
    /**
23
     * Quova API key
24
     *
25
     * @var string
26
     */
27
    private $_apiKey;
28
    /**
29
     * Quova shared secret
30
     *
31
     * @var string
32
     */
33
    private $_secret;
34
    /**
35
     * Default URL for Quova's GeoIP service
36
     *
37
     * @var string
38
     */
39
    private $_defaultService = 'http://api.quova.com/v1/ipinfo/';
40
    /**
41
     * Public constructor
42
     *
43
     * @param string $apiKey Quova API Key
44
     * @param string $secret Quova shared secret
45
     */
46
    public function __construct($apiKey, $secret)
47
    {
48
        $this->_apiKey = $apiKey;
49
        $this->_secret = $secret;
50
    }
51
    /**
52
     * Get geographical location of IP address from Quova's API
53
     *
54
     * @param  string $ipaddress
55
     * @param  string $format json or XML
56
     * @return string XML or json, depending on the value of $format
57
     */
58
    public function getLocation($ipaddress, $format = 'json')
59
    {
60
        $ch = curl_init();
61
        $parameters = array(
62
            'apikey' => $this->_apiKey,
63
            'sig' => $this->generateSig(),
64
            'format' => $format
65
        );
66
        $url = $this->_defaultService
67
            . $ipaddress
68
            . '?'
69
            . http_build_query($parameters);
70
        curl_setopt($ch, CURLOPT_URL, $url);
71
        curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
72
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
73
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
74
        $data = curl_exec($ch);
75
        $headers = curl_getinfo($ch);
76
        // Check headers here
77
        curl_close($ch);
78
        return $data;
79
    }
80
    /**
81
     * Checks headers for response code and issues error message if necessary
82
     *
83
     * @param mixed $headers
84
     */
85
    public function checkHeaders($headers)
86
    {
87
        // this is where I'd test HTTP response codes
88
    }
89
    /**
90
     * Generates sig, an MD5 hash of the API key, the shared secret, and Unix timestamp
91
     *
92
     * @return string MD5 hash
93
     */
94
    public function generateSig()
95
    {
96
        return md5($this->_apiKey . $this->_secret . gmdate('U'));
97
    }
98
}

以下是調用 Quova API 的簡單例子:

 PHP |  copy code |? 
1
$apikey = 'dummyApiKey';
2
$secret = 'dummySecret';
3
$ipaddress = '72.30.2.43'; // Yahoo!
4
$quova = new Example_Quova_GeoIp($apikey, $secret);
5
$location = json_decode($quova->getLocation($ipaddress));
6
var_dump($location);

這段程式的輸出如下:

object(stdClass)[4]
  public 'ipinfo' =>
    object(stdClass)[5]
      public 'ip_address' => string '72.30.2.43' (length=10)
      public 'ip_type' => string 'Mapped' (length=6)
      public 'Network' =>
        object(stdClass)[6]
          public 'organization' => string 'inktomi corporation' (length=19)
          public 'OrganizationData' =>
            object(stdClass)[7]
              public 'organization_type' => string 'Business Conglomerate' (length=21)
          public 'carrier' => string 'inktomi corporation' (length=19)
          public 'asn' => int 14777
          public 'connection_type' => string 'tx' (length=2)
          public 'line_speed' => string 'high' (length=4)
          public 'ip_routing_type' => string 'fixed' (length=5)
          public 'Domain' =>
            object(stdClass)[8]
              public 'tld' => string 'com' (length=3)
              public 'sld' => string 'yahoo' (length=5)
      public 'Location' =>
        object(stdClass)[9]
          public 'continent' => string 'north america' (length=13)
          public 'latitude' => float 37.33053
          public 'longitude' => float -121.83823
          public 'CountryData' =>
            object(stdClass)[10]
              public 'country' => string 'united states' (length=13)
              public 'country_code' => string 'us' (length=2)
              public 'country_cf' => int 99
          public 'region' => string 'southwest' (length=9)
          public 'StateData' =>
            object(stdClass)[11]
              public 'state' => string 'california' (length=10)
              public 'state_code' => string 'ca' (length=2)
              public 'state_cf' => int 94
          public 'dma' => int 807
          public 'msa' => int 41940
          public 'CityData' =>
            object(stdClass)[12]
              public 'city' => string 'san jose' (length=8)
              public 'postal_code' => string '95122' (length=5)
              public 'time_zone' => int -8
              public 'area_code' => string '408' (length=3)
              public 'city_cf' => int 90

調用 $location->ipinfo->Location->StateData->state_code; 送回兩個字母的美國州代號,太容易了!

陷阱

至於陷阱,也不是太多,比較大的問題是準確性。 免費地理定位數據集一般承諾的準確性大約是半徑 25 英里的範圍,而且不同的數據集也可能給出不同的答案。你會注意到在以上的例子中,同一個 IP 地址 GeoLite 和 Quova 便送回不同的城市和郵政編碼。 Jeremy 用他自己的 IP 地址 (密西西比州 Southaven 市) 測試,GeoLite 送回的地址是田納西州 Collierville 市,而 Quova 則認為在田納西州 Memphis 市。 這兩個城市都在25英里的準確性半徑內,但俱錯誤地報告州份。

結束語

要把地理定位功能添加到網頁應用程序嗎?你只需要 10 分鐘閒暇時間便足夠了。參照上面的例子,你可以毫不費力地啟動這項功能,你必須編寫的程式碼將是微乎其微的,相對於回報你投入的時間肯定是值得的。

發表回覆

  

  

  

您可使用下列 these HTML標籤

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>