ip库查询

2010-06-09 09:51

[python]

现在网上流传的ip库,稍微处理了一下,一共 373691条记录,压缩后是 3.5M
记录格式如

63.211.200.72   63.211.200.79   美国 加利福尼亚州山景市谷歌公司
64.41.221.192   64.41.221.207   美国 加利福尼亚州山景市谷歌公司
64.68.64.64     64.68.64.127    美国 加利福尼亚州山景市谷歌公司
64.68.80.0      64.68.95.255    美国 加利福尼亚州山景市谷歌公司
64.233.160.0    64.233.191.255  美国 加利福尼亚州山景市谷歌公司
66.102.0.0      66.102.15.255   美国 加利福尼亚州山景市谷歌公司
66.249.64.0     66.249.95.255   美国 加利福尼亚州山景市谷歌公司
72.14.192.0     72.14.255.255   美国 加利福尼亚州山景市谷歌公司
74.125.0.0      74.125.255.255  美国 加利福尼亚州山景市谷歌公司
194.221.68.0    194.221.68.255  英国 谷歌(北爱尔兰)有限公司
195.249.20.0    195.249.20.255  丹麦 谷歌公司
IP范围,然后是名称。名称为utf8编码。使用掩码表示可能更专业一些。

写了个简单Python程序,使用二分法检索这个IP库。实际使用时启动为daemon,不用每次都加载30万条记录
import socket, struct, string
# import datetime

class SubNetwork:
  def __init__(self, p, m, n):
    self.portion = p
    self.end = m
    self.name = n
  def __str__(self):
    return '%d %s %s %s' % (self.portion,
      socket.inet_ntoa(struct.pack('>I', self.portion)),
      socket.inet_ntoa(struct.pack('>I', self.end)),
      self.name)

class IpLibrary:
  def __init__(self):
    self.list = []

  def Find(self, ips):
    ip = struct.unpack('>I', socket.inet_pton(socket.AF_INET, ips))[0]
    low = 0
    high = len(self.list)

    while (low <= high):
      m = (low+high)/2
      if m >= len(self.list):
        return None #self.list[-1]
      mv = self.list[m]
      if ip > mv.portion and ip < mv.end:
#        print '%d got (%d < %d < %d)' % (m, mv.portion, ip, mv.end)
        return mv
      else:
        if ip > mv.end:
          low = m + 1
        else:
          high = m -1
#        print '%d (%d  %d)' % (m, ip, mv.end)
    return mv

  def Split(self, line, sep):
    arr = string.split(line, sep)
    ret = []
    for i in arr:
      if i and i != '\n':
        ret.append(i)
    return ret

  def Load(self, filename):
    c = 0
    f = open(filename, 'r')
    for i in f:
      c = c + 1
      arr = self.Split(i, ' ')
      if len(arr) < 3:
        continue
      # >I mean big-endian unsigned int
      p = struct.unpack('>I', socket.inet_pton(socket.AF_INET, arr[0]))
      m = struct.unpack('>I', socket.inet_pton(socket.AF_INET, arr[1]))
      name = string.join(arr[2:])

      sn = SubNetwork(p[0], m[0], string.replace(name, '\n', ''))
      self.list.append(sn)

#      if c > 200:
#        break

def Load(filename):
  global _lib
  _lib.Load(filename)

def Query(ips):
  global _lib
#  t = datetime.datetime.now()
  r = _lib.Find(ips)
  return r
#  print 'query %s, result: %s, used: %s' % (
#    ips, r, str(datetime.datetime.now() - t))

if __name__ == "__main__":
  _lib.Load('ipsu.txt')

  Query('1.155.3.3')
  Query('0.0.3.3')
  Query('255.255.3.3')
  Query('0.0.0.0')
  Query('218.0.4.137')
  Query('218.0.5.0')

需要注意的是,代码中的 >

struct.unpack('>I', ...)

>I 的意思是 big-endian unsigned int

实际使用时,基本都在30毫秒左右返回结果