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
    }
  }
]

沒有留言:

張貼留言