IPアドレス所在地検索機能の実装
一番最初にIPに紐付く地域情報DBを調べる
「ip address location database」でググるといくつかのサイトが確認できる。
調べた結果
調べてみると、IPから国~市情報までは無料提供もあるけど、細かい情報は大体有料な場合が多い。でも、機能の実装ならIP2Locationで提供しているLITE11で十分なのでこれをダウンロードしてみる。
IP2LocationはダウンロードページからDB構築手順も案内していて、色んな開発ケースに対してチュートリアルも提供しているので便利。
IP2LocationでIPアドレス検索用DB構築
IPv4
CREATE TABLE `mt_ip2location_db11`( `id` int(11) NOT NULL AUTO_INCREMENT, `ip_from` INT(10) UNSIGNED, `ip_to` INT(10) UNSIGNED, `country_code` CHAR(2), `country_name` VARCHAR(64), `region_name` VARCHAR(128), `city_name` VARCHAR(128), `latitude` DOUBLE, `longitude` DOUBLE, `zip_code` VARCHAR(30), `time_zone` VARCHAR(8), PRIMARY KEY (`id`), INDEX `idx_ip_from` (`ip_from`), INDEX `idx_ip_to` (`ip_to`), INDEX `idx_ip_from_to` (`ip_from`, `ip_to`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
IPv6
CREATE TABLE `mt_ip2location_db11_ipv6`( `id` int(11) NOT NULL AUTO_INCREMENT, `ip_from` DECIMAL(39,0) UNSIGNED NULL DEFAULT NULL, `ip_to` DECIMAL(39,0) UNSIGNED NOT NULL, `country_code` CHAR(2), `country_name` VARCHAR(64), `region_name` VARCHAR(128), `city_name` VARCHAR(128), `latitude` DOUBLE, `longitude` DOUBLE, `zip_code` VARCHAR(30), `time_zone` VARCHAR(8), PRIMARY KEY (`id`), INDEX `idx_ip_from` (`ip_from`), INDEX `idx_ip_to` (`ip_to`), INDEX `idx_ip_from_to` (`ip_from`, `ip_to`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
IPv4
mysql> LOAD DATA LOCAL INFILE 'C:\\util\\ip-address\\IP2LOCATION-LITE-DB11.CSV\\IP2LOCATION-LITE-DB11.CSV' -> INTO TABLE mt_ip2location_db11 -> FIELDS TERMINATED BY ',' -> ENCLOSED BY '"' -> LINES TERMINATED BY '\r\n' -> (ip_from,ip_to,country_code,country_name,region_name,city_name,latitude,longitude,zip_code,time_zone); Query OK, 4044940 rows affected (56.48 sec) Records: 4044940 Deleted: 0 Skipped: 0 Warnings: 0
IPv6
mysql> LOAD DATA LOCAL INFILE 'C:\\util\\ip-address\\IP2LOCATION-LITE-DB11.IPV6.CSV\\IP2LOCATION-LITE-DB11.IPV6.CSV' -> INTO TABLE mt_ip2location_db11_ipv6 -> FIELDS TERMINATED BY ',' -> ENCLOSED BY '"' -> LINES TERMINATED BY '\r\n' -> (ip_from,ip_to,country_code,country_name,region_name,city_name,latitude,longitude,zip_code,time_zone); Query OK, 4366556 rows affected, 3 warnings (1 min 9.98 sec) Records: 4366556 Deleted: 0 Skipped: 0 Warnings: 3
DBが準備できれば後はSELECTするだけなのでここまで。
※DBからIPv6の値を元に検索する方法が現在チュートリアルには存在してない、且つPythonのライブラリが存在するため、Pythonで開発する場合はライブラリを利用して実装することをお勧めする。
django rest frameworkでの実装例
IP情報からIPv4かIPv6か判別し、地域情報を返す
(IPv6はDB格納、IPv6はライブラリ(BIN)をプロジェクト内に格納)
Python
class Ip2locationViewSet(viewsets.ViewSet): queryset = mt_ip2location_db11.objects.all() def list(self, request): ip_val = request.query_params.get('ip') if ip_val is not None: try: ipnet = ipaddress.ip_network(ip_val) if ipnet.version == 4: ipv4_val = int(ipaddress.IPv4Address(ip_val)) ip_infos = mt_ip2location_db11.objects.filter(ip_to__gte=ipv4_val).order_by('ip_to')[:1] elif ipnet.version == 6: IP2LocObj = IP2Location.IP2Location(); IP2LocObj.open(os.path.join("data","IP2LOCATION-LITE-DB11.IPV6.BIN")); ip_infos = IP2LocObj.get_all(ip_val); ipv6_result = OrderedDict() ipv6_result['country_code'] = ip_infos.country_short ipv6_result['country_long'] = ip_infos.country_long ipv6_result['region_name'] = ip_infos.region ipv6_result['city_name'] = ip_infos.city ipv6_result['latitude'] = str(ip_infos.latitude) ipv6_result['longitude'] = str(ip_infos.longitude) ipv6_result['zip_code'] = ip_infos.zipcode ipv6_result['time_zone'] = ip_infos.timezone ipv6_result['ip'] = ip_val return Response([ipv6_result]) else: ip_infos = mt_ip2location_db11.objects.all()[:1] return Response(ip_infos, status=status.HTTP_404_NOT_FOUND) except ValueError: return Response({'message': 'IP形式が正しくありません。', 'ip': ip_val}, status=status.HTTP_400_BAD_REQUEST) else: return Response({'message': 'IPアドレスの情報がありません。', 'ip': ip_val}, status=status.HTTP_400_BAD_REQUEST) serializer = Ip2locationSerializer(ip_infos, many=True) serializer.data[0]['ip'] = ip_val return Response(serializer.data)