顯示具有 country 標籤的文章。 顯示所有文章
顯示具有 country 標籤的文章。 顯示所有文章

2024年12月5日 星期四

PHP 開發筆記 - 產生 CountryCode, CountryName, CityName, GeoLocation 列表

原本一直盧 AI 產出,發現產出的品質很難控,最後就轉個彎去把 GeoIP DB 內的資料輸出即可,而策略也很簡單,單純用 IP 暴力輪詢。理論上可以優雅一點去了解 DB Record format,總之,暴力解也很快,就順手先記錄一下,此例僅列出部分資訊(非輪詢所有 IPv4):

```
<?php
if (!extension_loaded('geoip')) {
    die("GeoIP extension is not installed\n");
}

class GeoIPParser {
    private $dbPath;
    
    public function __construct($dbPath = '/tmp/db.dat') {
        $this->dbPath = $dbPath;
        geoip_setup_custom_directory('/tmp');
    }
    
    public function parse() {
        if (!file_exists($this->dbPath)) {
            throw new Exception("GeoIP City database not found at {$this->dbPath}");
        }
        
        $locations = [];
        $processed = [];
        
        try {
            // 遍歷 IP 範圍,每個 /16 subnet 取樣幾個 IP
            for ($first = 1; $first <= 255; $first++) {
                fprintf(STDERR, "\rProcessing IP block: %d/255", $first);
                
                for ($second = 0; $second <= 255; $second += 5) {
                    $ip = "$first.$second.1.1";
                    $record = geoip_record_by_name($ip);
                    
                    if ($record && 
                        !empty($record['country_code']) && 
                        !empty($record['city'])) {
                        
                        $key = $record['country_code'] . '|' . $record['city'];
                        
                        if (!isset($processed[$key])) {
                            $location = [
                                'country_code' => $record['country_code'],
                                'country_name' => $record['country_name'],
                                'city_name' => $record['city'],
                                'geo_location' => [
                                    'latitude' => round($record['latitude'], 4),
                                    'longitude' => round($record['longitude'], 4)
                                ]
                            ];
                            
                            $locations[] = $location;
                            $processed[$key] = true;
                        }
                    }
                }
            }
            
            fprintf(STDERR, "\nProcessing completed. Total locations found: " . count($locations) . "\n");
            
            // 按國家代碼和城市名稱排序
            usort($locations, function($a, $b) {
                $countryComp = strcmp($a['country_code'], $b['country_code']);
                return $countryComp === 0 ? 
                    strcmp($a['city_name'], $b['city_name']) : 
                    $countryComp;
            });
            
            return $locations;
        } catch (Exception $e) {
            fprintf(STDERR, "Error during processing: " . $e->getMessage() . "\n");
            throw $e;
        }
    }
}

try {
    $parser = new GeoIPParser();
    $locations = $parser->parse();
    
    // 輸出為格式化的 JSON
    echo json_encode($locations, 
        JSON_PRETTY_PRINT | 
        JSON_UNESCAPED_UNICODE | 
        JSON_UNESCAPED_SLASHES
    );
    
} catch (Exception $e) {
    fwrite(STDERR, "Error: " . $e->getMessage() . "\n");
    exit(1);
}
```

產出:

```
% php83 -ini | grep geoip
/opt/local/var/db/php83/geoip.ini,
geoip
geoip support => enabled
geoip extension version => 1.1.1
geoip library version => 1005000
geoip.custom_directory => no value => no value
% php83 geo-lookup.php 
Processing IP block: 255/255
Processing completed. Total locations found: 2657
...
```

片段資料: 

% php83 list-geo-city.php | jq '[.[]|select(.country_code=="TW")]'         
Processing IP block: 255/255
Processing completed. Total locations found: 8020
[
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Anping District",
    "geo_location": {
      "latitude": 22.9965,
      "longitude": 120.1617
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Bade District",
    "geo_location": {
      "latitude": 24.9259,
      "longitude": 121.2763
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Banqiao",
    "geo_location": {
      "latitude": 25.0104,
      "longitude": 121.4683
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Beitou",
    "geo_location": {
      "latitude": 25.1403,
      "longitude": 121.4948
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Chang-hua",
    "geo_location": {
      "latitude": 24.0759,
      "longitude": 120.5657
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Chiayi City",
    "geo_location": {
      "latitude": 23.4815,
      "longitude": 120.4498
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Chiyayi County",
    "geo_location": {
      "latitude": 23.4461,
      "longitude": 120.5728
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Daan",
    "geo_location": {
      "latitude": 25.0316,
      "longitude": 121.5345
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Dacun",
    "geo_location": {
      "latitude": 23.9978,
      "longitude": 120.547
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Dawan",
    "geo_location": {
      "latitude": 23.2073,
      "longitude": 120.1906
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Daya",
    "geo_location": {
      "latitude": 24.2226,
      "longitude": 120.6493
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Douliu",
    "geo_location": {
      "latitude": 23.7125,
      "longitude": 120.545
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "East District",
    "geo_location": {
      "latitude": 22.9721,
      "longitude": 120.2224
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Guishan",
    "geo_location": {
      "latitude": 25.0273,
      "longitude": 121.359
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Hsinchu",
    "geo_location": {
      "latitude": 24.8065,
      "longitude": 120.9706
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Hsinchu County",
    "geo_location": {
      "latitude": 24.673,
      "longitude": 121.1614
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Hualien City",
    "geo_location": {
      "latitude": 23.9807,
      "longitude": 121.6115
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Jian",
    "geo_location": {
      "latitude": 23.9516,
      "longitude": 121.5639
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Jinshan",
    "geo_location": {
      "latitude": 25.0613,
      "longitude": 121.5705
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Kaohsiung City",
    "geo_location": {
      "latitude": 22.6148,
      "longitude": 120.3139
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Keelung",
    "geo_location": {
      "latitude": 25.1322,
      "longitude": 121.742
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Linkou District",
    "geo_location": {
      "latitude": 25.0738,
      "longitude": 121.3935
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Miaoli",
    "geo_location": {
      "latitude": 24.5641,
      "longitude": 120.8275
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Nantou City",
    "geo_location": {
      "latitude": 23.9082,
      "longitude": 120.6558
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Neihu District",
    "geo_location": {
      "latitude": 25.0811,
      "longitude": 121.5838
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "New Taipei City",
    "geo_location": {
      "latitude": 24.9466,
      "longitude": 121.586
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Penghu County",
    "geo_location": {
      "latitude": 23.5748,
      "longitude": 119.6098
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Pingtung City",
    "geo_location": {
      "latitude": 22.6745,
      "longitude": 120.491
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Puli",
    "geo_location": {
      "latitude": 23.9678,
      "longitude": 120.9644
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Sanchong District",
    "geo_location": {
      "latitude": 25.0691,
      "longitude": 121.4878
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Sanxia District",
    "geo_location": {
      "latitude": 24.9336,
      "longitude": 121.372
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Shiding District",
    "geo_location": {
      "latitude": 24.9956,
      "longitude": 121.6546
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Shoufeng",
    "geo_location": {
      "latitude": 23.8341,
      "longitude": 121.521
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Taibao",
    "geo_location": {
      "latitude": 23.4603,
      "longitude": 120.3284
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Taichung",
    "geo_location": {
      "latitude": 24.144,
      "longitude": 120.6844
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Taichung City",
    "geo_location": {
      "latitude": 24.1547,
      "longitude": 120.6716
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Tainan City",
    "geo_location": {
      "latitude": 22.9917,
      "longitude": 120.2147
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Taipei",
    "geo_location": {
      "latitude": 25.0504,
      "longitude": 121.5324
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Taitung",
    "geo_location": {
      "latitude": 22.7563,
      "longitude": 121.1418
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Taoyuan",
    "geo_location": {
      "latitude": 24.9977,
      "longitude": 121.2965
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Taoyuan District",
    "geo_location": {
      "latitude": 24.9889,
      "longitude": 121.3175
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Xizhi District",
    "geo_location": {
      "latitude": 25.0696,
      "longitude": 121.6577
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Yilan",
    "geo_location": {
      "latitude": 24.7574,
      "longitude": 121.7421
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Yongkang District",
    "geo_location": {
      "latitude": 23.0204,
      "longitude": 120.2591
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Yunlin",
    "geo_location": {
      "latitude": 23.7113,
      "longitude": 120.3897
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Zhongli District",
    "geo_location": {
      "latitude": 24.9614,
      "longitude": 121.2437
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Zhubei",
    "geo_location": {
      "latitude": 24.8351,
      "longitude": 121.0056
    }
  },
  {
    "country_code": "TW",
    "country_name": "Taiwan",
    "city_name": "Zuoying",
    "geo_location": {
      "latitude": 22.6868,
      "longitude": 120.2971
    }
  }
]

2014年6月25日 星期三

[Linux] Apache/GeoIP/mod_geoip: 依據 Client IP Country 回傳指定 Server Location @ Ubuntu 14.04、Apache 2.4.7

$ apt-cache show libapache2-mod-geoip
Package: libapache2-mod-geoip
Priority: optional
Section: universe/web
Installed-Size: 86
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Original-Maintainer: Prach Pongpanich <prachpub@gmail.com>
Architecture: amd64
Version: 1.2.8-2
Depends: apache2-api-20120211, libc6 (>= 2.3.4), libgeoip1
Filename: pool/universe/liba/libapache2-mod-geoip/libapache2-mod-geoip_1.2.8-2_amd64.deb
Size: 18748
MD5sum: fc16528f6d8acabaf8d40c70fe47b1b2
SHA1: 457df303c09556297a8b1f6887fe7a901d8bd063
SHA256: 26eabfe728014a506186f2856a3149f00e7bfcb6db9226dbfe13976ab3b4d584
Description-en: GeoIP support for apache2
 This is an apache2 module for finding the country that a web request
 originated from. It uses the GeoIP library and database to perform
 the lookup. The module allows manipulation of client requests from within
 Apache based on the country of origin.
 .
 This module only works on Apache 2 servers.
Description-md5: e4085008663af571952df21045e8534a
Homepage: http://www.maxmind.com/app/mod_geoip
Bugs: https://bugs.launchpad.net/ubuntu/+filebug
Origin: Ubuntu

$ sudo apt-get install libapache2-mod-geoip

$ ls -la /usr/share/GeoIP/
total 3852
-rw-r--r--   1 root root  827301 Apr  7 14:20 GeoIP.dat
-rw-r--r--   1 root root 3105495 Apr  7 14:21 GeoIPv6.dat


基本上安裝完就等同啟動 mod_geoip ,可以在 /etc/apache2/mods-enabled 看到 geoip.conf, geoip.load 蹤影。

編輯 Apache configure file:

<IfModule mod_geoip.c>
        GeoIPEnable On
        <IfModule mod_rewrite.c>
                RewriteEngine On
                RewriteCond %{ENV:GEOIP_COUNTRY_CODE} ^CN$
                RewriteRule ^(.*)$ http://example.com.cn$1 [L]
        </IfModule>
</IfModule>


如此一來,當 Client IP 判斷是 CN 時,透過 Web Server 導向 CN 區的機器。

使用這招的目的是...大陸架設服務時,需要"備案",在備案還沒通過時,使用 domainname 連線過去的機器會被 ban 掉,這時候可以先稍微用已備案的或是 IP 來頂替了。

當備案成功後,就可以大方地使用 GeoDNS 的解法,讓使用者不必先連到指定機器再轉址。

最後,如果要留一些 debug 模式,可以多加上 hostname 的判斷,限定特定 hostname 才會依照使用者 IP 位置轉址。

<IfModule mod_geoip.c>
        GeoIPEnable On
        <IfModule mod_rewrite.c>
                RewriteEngine On
                RewriteCond %{HTTP_HOST} .
                RewriteCond %{HTTP_HOST} ^www\.example\.com$
                RewriteCond %{ENV:GEOIP_COUNTRY_CODE} ^CN$
                RewriteRule ^(.*)$ http://www.example.com.cn$1 [L]
        </IfModule>
</IfModule>


參考資料: