【PHP】IPアドレスの地域情報を取得する

PHP

PHPでGeolite2ライブラリを使って、IPアドレスから地域情報を取得する方法について記載します。

コンソール上でのGeoLite2の実行結果

環境

 Windows11 Home
 PHP 8.3.8 & composer 2.7.7

手順

①MaxmindからIPのデータベース情報を取得
 ・Maxmindアカウントの作成(無料ユーザ登録)
 ・データベース情報のダウンロード
②GeoLite2を使ってIPから地域情報を取得
 ・DBの保存形式を確認
 ・Composerを使ってGeolite2をインストール
   (インストールがうまくいかない場合)
 ・GeoLite2を使ってIPアドレスから地域情報を取得

データベース情報を取得

 Maxmindの公式ページは次のリンクからジャンプできます。 

   https://www.maxmind.com/en/geoip-databases
   表示すると、自身の地域情報が表示されます。
    国名だけでなく、都市名、GPS情報(多分サーバの位置かなと思います)、郵便番号も表示されています。

①Maxmindアカウントの作成
 データベース情報をダウンロードするためには、無料のアカウント登録が必要です。
 有料アカウントもありますが、精度がよくなるだけであり、ダウンロード制限もないので、私は無料アカウントでで充分です。
 ユーザ登録ページ GeoLite2 Sign Up | MaxMind

 ●ユーザ情報登録ページ

 入力項目(全部必須入力のようです) 
  名前 会社 業界  国  
  使用目的 不正の検知目的とかウェブの分析といったところでしょうか
  メールアドレス 有効なメールアドレスが必要です
  電話番号 必須ではありません
  Maxmindデータをどのように使用するか プラットフォームやソフトウェア、Web等
  データをどのように活用するか 統計資料や不正検知、ウェブの分析等

 送られてきたメールを使ってパスワードを設定すればアカウントを作成することができます。
 ログインは初期設定で二段階認証になっており、ログインするたびにメールにpinコードが送られてきます。(いいね)

②データベース情報のダウンロード
 ログイン後、画面の左にあるメニューバーから
   GeoIP2/GeoLite2
を選択するとダウンロード画面が開きます。
 圧縮されているので、解凍して実行ファイルと同じフォルダにコピーしておきます。

●ダウンロード画面

ダウンロードできるデータベースは次の通りです。

データベース説明
GeoLite2 ASNIPアドレスとASNの対応表(6MB程度)
(CVS 6MB程度、MMDB 3MB程度 )
GeoLite2 CityIPアドレスと都市名の対応表(145MB程度)
(CVS 150MB程度、MMDB 60MB程度 )
GeoLite2 CountryIPアドレスと国名の対応表(4MB程度)
(CVS 4MB程度、MMDB 3MB程度 )

 都市名と国名は英語だけでなく、日本語で書かれたものもあり、かなり親切設計でした。
 フォーマットの種類は次の2種類で提供されています。
   csv形式 他のソフトウェアでDBを利用したい場合、読込が遅い
   mmdb形式 mindmax社独自のDB形式、読込が早い
 データの中身を確認したり、独自で読込ソフトウェアを作成するのならcsvでダウンロードする必要があります。
 今回は、GeoLite2 Cityのcsvとmmdbの両方をダウンロードしました。

GeoLite2を使ってIPから地域情報を取得

取得したDB情報を使って地域情報を取得します。

①DBの保存形式を確認
 GeoLite2 City CSV ファイルをダウンロードしたところ、次のファイル等が含まれていました。

ファイル名
GeoLite2-City-Blocks-IPv4.csvipv4アドレスとgeoname_idの対応表 等
GeoLite2-City-Blocks-IPv6.csvipv6アドレスとgeoname_idの対応表 等
GeoLite2-City-Locations-ja.csvgeoname_idと、都市名、国名の対応表 等

指定されたIPアドレスを一度geoname_idに変換してから、geoname_idを使って都市名を取得するという流れですね。
 mmdbファイルはバイナリ化されており、内容を可読化することはできません。

②Composerを使ってGeoLite2をインストール

・GeoLite2のインストールが失敗
 次のコマンドでGeoLite2をインストールしたところ、警告が表示されて失敗しました。

c:\{インストールDIR} >composer require geoip2/geoip2

./composer.json has been created
Running composer update geoip2/geoip2
Loading composer repositories with package information
:
    - guzzle/guzzle[v3.0.0, ..., v3.9.3] require ext-curl * -> it is missing from your system. Install or enable PHP's curl extension.
    - maxmind/web-service-common[v0.0.2, ..., v0.9.0] require ext-curl * -> it is missing from your system. Install or enable PHP's curl extension.

To enable extensions, verify that they are enabled in your .ini files:
    - C:\{PHP_HOME}\php.ini
You can also run `php --ini` in a terminal to see which files are used by PHP in CLI mode.
Alternatively, you can run Composer with `--ignore-platform-req=ext-curl` to temporarily ignore these required extensions.
You can also try re-running composer require with an explicit version constraint, e.g. "composer require geoip2/geoip2:*" to figure out if any version is installable, or "composer require geoip2/geoip2:^2.1" if you know which you need.

Installation failed, deleting ./composer.json.

当環境では、php.ini 内でcurlが使用できなくなっているというエラーが発生しましたので、警告に従って、
  C:\{PHP_HOME}\php.ini
を開いて

次のコメントを解除
;extension=curl
↓
extension=curl

としたところ、同じコマンドでインストールが成功しました。

③GeoLite2を使ってIPアドレスから地域情報を取得
 composerでGeolite2をインストールしたフォルダで次のPHPプログラムを作成しました。

<?php 
require './vendor/autoload.php';
use GeoIp2\Database\Reader;
use GeoIp2\Exception\AddressNotFoundException;

try {
    # ダウンロードしたバイナリを指定
    $my_reader = new Reader('./GeoLite2-City.mmdb');

    # チェックしたいIPアドレスを指定
    $ip = "61.120.205.110"; # とりあえず東京都のHPのIPを入力
    $res = $my_reader->city($ip);

    # IPを表示
    var_dump("IPアドレス: $ip");
    
    # 国コード
    $isocode = $res->country->isoCode;
    var_dump("国コード(isoCode):$isocode");
    
    # 国名
    $name = $res->country->name;
    var_dump("国名(country name):$name");
    
    # 国名 names['ja']が存在するかをチェックして表示
    if (isset($res->country->names['ja'])) {
        $name_ja = $res->country->names['ja'];
        var_dump("国名(country name ja):$name_ja"); # 国名(日本語)
    } else {
        echo "日本語の国名情報がありません。";
    }

    # 都市名(日本語)が存在するかをチェックして表示
    if (isset($res->city->names['ja'])) {
        $city_ja = $res->city->names['ja'];
        var_dump("都市名(city name ja):$city_ja"); # 都市名(日本語)
    } else {
        echo "日本語の都市名情報がありません。";
    }

} catch (AddressNotFoundException $e) {
    # 結果が見つからない場合の処理
    echo "データベースに結果が見つかりませんでした。";
    
} catch (Exception $e) {
    # その他のエラーをキャッチ
    echo "エラーが発生しました: " . $e->getMessage();
}

?>

フォルダ構成は次の通り


c:\{実行フォルダ}
 ├― vendor (composerによって作成されたフォルダ)
 |   ├― autoload.php (Geoip ロードファイル)
 |   :  省略
 ├― composer.json(composerによって作成された定義ファイル)
 ├― composer.lock(composerによって作成された定義ファイル)
 ├― test.php (今回作成したphp実行ファイル)
 ├― GeoLite2-City.mmdb (ダウンロードしたDBファイル)
 ├― GeoLite2-City-Blocks-IPv4.csv (ダウンロードしたDBファイル なくても実行可)
 ├― GeoLite2-City-Locations-ja.csv (ダウンロードしたDBファイル なくても実行可)

 実行結果は次の通りです。

C:\{実行フォルダ}>php test.php
string(30) "IPアドレス: 61.120.205.110"
string(24) "国コード(isoCode):JP"
string(26) "国名(country name):Japan"
string(30) "国名(country name ja):日本"
string(30) "都市名(city name ja):東京"

 IPアドレスは東京都のHPのものを入力しました。
 IPによっては、「●●区」「●●町」まで表示されます。
 ちなみに、自身のIPアドレスを入力したところ、正確な都市名が表示されました。
 GeoLite2は、ja表記が無い都市名を表示しようとするとExceptionを吐いてくるので、try~catchで囲んでおく必要があります。

最後に

 Geolite2を使ってIPアドレスから地域情報を取得することができました。
 GoogleAnalize等で利用者の都市名が表示されるのは、こういう仕組みを利用しているのですね。
 Maxmind社がどのようにしてIPの地域情報を集めているのかはわかりませんが、これだけの情報を無料で使わせてくれることに感謝です。
 GeoIPのライセンスを読むと、「MaxMindはサービスを無料で提供していますが、将来的に有料にする権利を保持します。」等と書かれていたので、無料のうちに使って経験を積んだほうがいいと感じています。