IP與地理資訊對應資料庫,在很多場合都會用到, 例如網路廣告業、線上遊戲產業、各種影片、直播鎖區的需求等等. 以前知道有一些免費的這類資料可供下載, 我想試試看將資料灌到 NoSQL 也就是 AWS DynamoDB裡,並測試一下效能如何.

下載資料庫

根據 stackoverflow 鄉民的討論串: Best IP to Country Database , 免費資料庫有下列兩種, 大部分人都推薦 前者Maxmind 的產品. 這類資料庫的付費模式大都相同, 免費的資料庫涵蓋了各個國家的ip對照表, 而更詳細的城市、經緯度對照則要付費(年費超過1,000美金).

  • Maxmind 公司的 GeoIP2 資料庫
  • ip2nation

下載

http://dev.maxmind.com/geoip/geoip2/geolite2/ --

解壓縮

--

裡面的資料有三類:

  • IPv4 資料庫
  • IPv6 資料庫
  • 國家名稱與國家代碼的對應表

整理資料

目標

先說明一下此整理資料的目標是: 把上面的 1. ipv4 資料庫 2. 國家名稱代碼對照表 , 兩張表格合併成一張CSV(如下圖), 以利後面匯入到DynamoDB. --

整合後的表格欄位如下:

  1. 任意相同的字串, 這會是DynamoDB Table的 主鍵之一(Partition key)
  2. ip 區段的第一個 ip 轉換成數字, 例如: 1.0.0.0 => 16777216 (另一個主鍵)
  3. ip 區段的最後一個 ip 轉換成數字
  4. 國家全名
  5. 國家縮寫代碼
  6. CIDR

處理過程後面會再詳細敘述.

1. ipv4 資料庫

GeoLite2-Country-Blocks-IPv4-csv 記載著 CIDR ,以及國家代碼數字。

--

提示:

  • 其中第2,3,4個欄位都是國家代碼數字,合併處理一下
  • 少數資料是完全沒有國家代碼的

2. 國家名稱代碼對照表

GeoLite2-Country-Locations-en.csv 會用到的欄位有國家代碼數字、國家代碼縮寫、國家名稱

--

提示:

  • 請用UTF8 讀檔進來, 有些國家名稱是 UTF8字元.
  • 有些國家名稱有逗號, 例如這個國家 "Bonaire, Sint Eustatius, and Saba", 處理字串時須注意這稍微難纏的狀況.

解析

IPv4資料庫是這樣子的:

1.0.0.0/24,2077456,2077456,,0,0
1.0.1.0/24,1814991,1814991,,0,0
1.0.2.0/23,1814991,1814991,,0,0

解析工作主要有二:

  1. 把 CIDR 轉換成IP區間, 例如: 1.0.0.0/24 -> 1.0.0.0 ~ 1.0.0.255
  2. 把 IP 轉換成數字, 例如: 1.0.0.0 -> 16777216

所有的IP都可以轉換數字, 這樣轉換是為了匯入到資料庫後, 我們可以用一些大於小於的條件來做查詢.

1. CIDR 轉換成 IP 區間

CIDR用很容易的方式, 在IP後面加個斜線數字, 就能輕鬆說明子網路遮罩.例如:

a.b.c.d/32 -> 子網路遮罩255.255.255.255 -> 1個 IP
a.b.c.d/24 -> 子網路遮罩255.255.255.000 -> 256個 IP
a.b.c.d/16 -> 子網路遮罩255.255.000.000 -> 65536個 IP

使用 Python的朋友,可以安裝 netaddr 這個 lib, 可以讓你的工作簡單一些. 一次完成兩樣工作. 使用其他程式語言的玩家, 可以用 " convert cidr to ip range stackoverflow " 當關鍵字搜尋.

from netaddr import IPNetwork

ip = IPNetwork('1.0.0.0/24')

print('ip.network   :%s' % ip.network)
print('ip.broadcast :%s' % ip.broadcast)
print('ip.size      :%s' % ip.size)
print('ip.first     :%s' % ip.first)
print('ip.last      :%s' % ip.last)

執行結果:
ip.network   :1.0.0.0
ip.broadcast :1.0.0.255
ip.size      :256
ip.first     :16777216
ip.last      :16777471
2. 把 IP 轉換成數字

假設IP是 w.x.y.z , 轉換成數字的公式是:

IP Number = 16777216*w + 65536*x + 256*y + z

假如你是Python玩家,只想用內建 lib的話,以下是範例:

import socket
import struct

def ip2int(addr):
    '''ip to int
    '''
    return struct.unpack("!I", socket.inet_aton(addr))[0]

print(ip2int('168.95.1.1'))

執行結果:
2824798465
使用 MySQL 也可以

對於 SQL 比較熟悉的朋友, 不一定要用本篇寫程式轉換的方式. 有另外的路,就是使用MySQL Import CSV ,然後再把兩張Table Join Select出來. 剛才查了一下 StackOverFlow ,看了其他人的方法,發覺自己好像繞了一條比較遠的路 XD

--

下一篇談談如何匯入 DynamoDB.

參考資料