2019-07-16に更新

【PHP】IPアドレスから地域・郵便番号・経緯度座標などを調べる

PHP

地域に応じてWebサイトの表示が変わると面白いと思ったので、PHPを使ってIPアドレスから地域を推定します。

IPアドレスから住所を調べられるサービスはいくつかありますが、今回はwww.ip-adress.comを使います。

スクレイピング

もちろんスクレイピングでやります。スクレイピングとは、Webサイトのソースコードから欲しい情報だけを抜き出して利用することです。APIのようなものです。()

スクレイピングで注意すべき点は、絶対に過剰なアクセスをかけないことです。あなたのサーバーがDoS攻撃を受けると、間接的にスクレイピング先のサーバーにもDoS攻撃がかかります。

私はかつてこれをやらかし、とあるAPIからサーバーごとBANされたことがあります。向こうからすれば私のサーバーがDoS攻撃をしたように見えるわけです。

キャッシュを取るか、アクセスに制限を設けましょう。

今回は、0.6秒以上間隔を空けないとスクレイピング処理が行われないようにします。これは、IPinfoDBの無料APIの上限が秒間2アクセスであることを参考にしています。

コード

動作デモはこちらです。
https://reiwa.cf/get_ip.php

<?php

define('WAIT', 0.6);

$last_updated = @file_get_contents('get_ip_last_updated');

if($last_updated !== false && $_SERVER["REQUEST_TIME_FLOAT"] - $last_updated < WAIT)
{
    echo '<meta http-equiv="refresh" content="'.WAIT.';URL=">';
    return;
}

$remote_addr = $_SERVER['REMOTE_ADDR'];

$ipv4 = '^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$';

if (!preg_match('/'.$ipv4.'/', $remote_addr))
{
    echo 'IPv4しか扱えません';
    return;
}

$ip_html = file_get_contents('https://www.ip-adress.com/ip-address/ipv4/'.$remote_addr);

$regex = <<<EOT
|<tr><th>IP Address</th><td>(.*)</td></tr>
<tr><th>Decimal Representation</th><td>(?:.*)</td></tr>
<tr><th><abbr title="Autonomous System Number">ASN</abbr></th><td>(?:.*)</td></tr>
<tr><th>City</th><td>(.*)</td></tr>
<tr><th>Country</th><td>(.*)</td></tr>
<tr><th>Country Code</th><td>(?:.*)</td></tr>
<tr><th><abbr title="Internet Service Provider">ISP</abbr></th><td>(?:.*)</td></tr>
<tr><th>Latitude</th><td>(.*)</td></tr>
<tr><th>Longitude</th><td>(.*)</td></tr>
<tr><th>Organization</th><td>(.*)</td></tr>
<tr><th>Postal Code</th><td>(.*)</td></tr>
<tr><th>Is Private IP Address</th><td>(?:.*)</td></tr>
<tr><th>PTR Resource Record</th><td>(?:.*)</td></tr>
<tr><th>Is Reserved IP Address</th><td>(?:.*)</td></tr>
<tr><th>State</th><td>(.*)</td></tr>
<tr><th>State Code</th><td>(?:.*)</td></tr>
<tr><th>Timezone</th><td>(.*)</td></tr>|U
EOT;

$is_match = preg_match($regex, $ip_html, $matches);

if(!$is_match)
{
    echo 'わかりませんでした';
    return;
}

$ip_addr = $matches[1];
$postal = $matches[7];
$country = $matches[3];
$state = $matches[8];
$city = $matches[2];
$ido = strtok($matches[4], '&');
$keido = strtok($matches[5], '&');
$org = $matches[6];
$timezone = $matches[9];

echo <<<EOT
IPアドレス: {$ip_addr}<br>
郵便番号: {$postal}<br>
地域: {$city}, {$state}, {$country}<br>
緯度: {$ido}°<br>
経度: {$keido}°<br>
組織: {$org}<br>
タイムゾーン: {$timezone}<br>
EOT;

file_put_contents('get_ip_last_updated', $_SERVER["REQUEST_TIME_FLOAT"]);

返り値はこんな感じになります。

IPアドレス: 111.101.7.185
郵便番号: 220-0023
地域: Yokohama, Kanagawa, Japan
緯度: 35.4500°
経度: 139.6333°
組織: au one net
タイムゾーン: Asia/Tokyo

最後の更新時間(Unixtime)を「get_ip_last_updated」に保存していて、もしも0.6秒経たないうちにアクセスしてしまった場合は、0.6秒後にリフレッシュするHTMLを送り返しています。
IPv4しか扱えないので、その他の書式のものは正規表現で除外しています。

使い道

僕は、近所の天気予報を表示したかったので作りました。座標情報が出ているので、最寄りの何かを検索したりもできるだろうし、「北海道から新しいアクセスがありました。」みたいな通知メールを送ったりにも使えそうですね。

ツイッターでシェア
みんなに共有、忘れないようにメモ

ウラル

Splatoonの二次創作サイト「スプランプ」の管理人です。サーモンラン研究所やオクトチャット、フェス速報などを作りました。

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント