python訪問純真IP資料庫的代碼_python

來源:互聯網
上載者:User

核心代碼:

#!/usr/bin/env python # -*- coding: utf-8 -*- from bisect import bisect _LIST1, _LIST2 = [], [] _INIT = False ip2int = lambda ip_str: reduce(lambda a, b: (a << 8) + b, [int(i) for i in ip_str.split('.')]) def _init(): global _LIST, _INIT if not _INIT: for l in open('ipdata.txt', 'rb'): ip1, ip2 = l.split()[:2] addr = ' '.join(l.split()[2:]) ip1, ip2 = ip2int(ip1), ip2int(ip2) _LIST1.append(ip1) _LIST2.append((ip1, ip2, addr)) _INIT = True def ip_from(ip): _init() i = ip2int(ip) idx = bisect(_LIST1, i) assert(idx > 0) if len(_LIST1) <= idx: return u'unknown ip address %s' % ip else: frm, to ,addr = _LIST2[idx - 1] if frm <= i <= to: return addr else: return u'unknown ip address %s' % ip if __name__ == '__main__': print ip_from('115.238.54.106') print ip_from('220.181.29.160') print ip_from('115.238.54.107') print ip_from('8.8.8.8')

代碼打包下載 http://xiazai.jb51.net/201105/yuanma/ipaddress.7z

接下來為大家分享更完美的代碼:

#!/usr/bin/env python# coding: utf-8 '''用Python指令碼查詢純真IP庫 QQWry.Dat的格式如下: +----------+| 檔案頭 | (8位元組)+----------+| 記錄區 | (不定長)+----------+| 索引區 | (大小由檔案頭決定)+----------+ 檔案頭:4位元組開始索引位移值+4位元組結尾索引位移值 記錄區: 每條IP記錄格式 ==> IP地址[國家資訊][地區資訊]   對於國家記錄,可以有三種表示方式:     字串形式(IP記錄第5位元組不等於0x01和0x02的情況),    重新導向模式1(第5位元組為0x01),則接下來3位元組為國家資訊儲存地的位移值    重新導向模式(第5位元組為0x02),   對於地區記錄,可以有兩種表示方式: 字串形式和重新導向   最後一條規則:重新導向模式1的國家記錄後不能跟地區記錄 索引區: 每條索引記錄格式 ==> 4位元組起始IP地址 + 3位元組指向IP記錄的位移值   索引區的IP和它指向的記錄區一條記錄中的IP構成一個IP範圍。查詢資訊是這個  範圍內IP的資訊 ''' import sysimport socketfrom struct import pack, unpack class IPInfo(object):  '''QQWry.Dat資料庫查詢功能集合  '''  def __init__(self, dbname):    ''' 初始化類,讀取資料庫內容為一個字串,    通過開始8位元組確定資料庫的索引資訊'''     self.dbname = dbname    # f = file(dbname, 'r')     # Demon註:在Windows下用'r'會有問題,會把\r\n轉換成\n    # 詳見http://demon.tw/programming/python-open-mode.html    # 還有Python文檔中不提倡用file函數來開啟檔案,推薦用open    f = open(dbname, 'rb')     self.img = f.read()    f.close()     # QQWry.Dat檔案的開始8位元組是索引資訊,前4位元組是開始索引的位移值,    # 後4位元組是結束索引的位移值。    # (self.firstIndex, self.lastIndex) = unpack('II', self.img[:8])     # Demon註:unpack預設使用的endian是和機器有關的    # Intel x86和AMD64(x86-64)是little-endian    # Motorola 68000和PowerPC G5是big-endian    # 而純真資料庫全部採用了little-endian位元組序    # 所以在某些big-endian的機器上原代碼會出錯    (self.firstIndex, self.lastIndex) = unpack('<II', self.img[:8])     # 每條索引長7位元組,這裡得到索引總個數    self.indexCount = (self.lastIndex - self.firstIndex) / 7 + 1   def getString(self, offset = 0):    ''' 讀取字串資訊,包括"國家"資訊和"地區"資訊     QQWry.Dat的記錄區每條資訊都是一個以'\0'結尾的字串'''     o2 = self.img.find('\0', offset)    #return self.img[offset:o2]    # 有可能只有國家資訊沒有地區資訊,    gb2312_str = self.img[offset:o2]    try:      utf8_str = unicode(gb2312_str,'gb2312').encode('utf-8')    except:      return '未知'    return utf8_str   def getLong3(self, offset = 0):    '''QQWry.Dat中的位移記錄都是3位元組,本函數取得3位元組的位移量的常規表示    QQWry.Dat使用“字串“儲存這些值'''    s = self.img[offset: offset + 3]    s += '\0'    # unpack用一個'I'作為format,後面的字串必須是4位元組    # return unpack('I', s)[0]     # Demon註:和上面一樣,強制使用little-endian    return unpack('<I', s)[0]   def getAreaAddr(self, offset = 0):    ''' 通過給出位移值,取得地區資訊字串,'''     byte = ord(self.img[offset])    if byte == 1 or byte == 2:      # 第一個位元組為1或者2時,取得2-4位元組作為一個位移量調用自己      p = self.getLong3(offset + 1)      return self.getAreaAddr(p)    else:      return self.getString(offset)   def getAddr(self, offset, ip = 0):    img = self.img    o = offset    byte = ord(img[o])     if byte == 1:      # 重新導向模式1      # [IP][0x01][國家和地區資訊的絕對位移地址]      # 使用接下來的3位元組作為位移量調用位元組取得資訊      return self.getAddr(self.getLong3(o + 1))     if byte == 2:      # 重新導向模式2      # [IP][0x02][國家資訊的絕對位移][地區資訊字串]      # 使用國家資訊位移量調用自己取得字串資訊      cArea = self.getAreaAddr(self.getLong3(o + 1))      o += 4      # 跳過前4位元組取字串作為地區資訊      aArea = self.getAreaAddr(o)      return (cArea, aArea)     if byte != 1 and byte != 2:      # 最簡單的IP記錄形式,[IP][國家資訊][地區資訊]      # 重新導向模式1有種情況就是位移量指向包含國家和地區資訊兩個字串      # 即位移量指向的第一個位元組不是1或2,就使用這裡的分支      # 簡單地說:取連續取兩個字串!       cArea = self.getString(o)      #o += 2*len(cArea) + 1      # 我們已經修改cArea為utf-8字元編碼了,len取得的長度會有變,      # 用下面方法得到offset       o = self.img.find('\0',o) + 1      aArea = self.getString(o)      if aArea == "?":        aArea = "電信"      if aArea == "信":        aArea = ""      if aArea == "[":        aArea = "聯通"      return (cArea, aArea)   def find(self, ip, l, r):    ''' 使用二分法尋找網路位元組編碼的IP地址的索引記錄'''    if r - l <= 1:      return l     m = (l + r) / 2    o = self.firstIndex + m * 7    #new_ip = unpack('I', self.img[o: o+4])[0]     # Demon註:和上面一樣,強制使用little-endian    new_ip = unpack('<I', self.img[o: o+4])[0]     if ip <= new_ip:      return self.find(ip, l, m)    else:      return self.find(ip, m, r)   def getIPAddr(self, ip):    ''' 調用其他函數,取得資訊!'''    # 使用網路位元組編碼IP地址    ip = unpack('!I', socket.inet_aton(ip))[0]    # 使用 self.find 函數尋找ip的索引位移    i = self.find(ip, 0, self.indexCount - 1)    # 得到索引記錄    o = self.firstIndex + i * 7    # 索引記錄格式是: 前4位元組IP資訊+3位元組指向IP記錄資訊的位移量    # 這裡就是使用後3位元組作為位移量得到其常規表示(QQWry.Dat用字串表示值)    o2 = self.getLong3(o + 4)    # IP記錄位移值+4可以丟棄前4位元組的IP地址資訊。    (c, a) = self.getAddr(o2 + 4)    return (c, a)   def output(self, first, last):    for i in range(first, last):      o = self.firstIndex + i * 7      ip = socket.inet_ntoa(pack('!I', unpack('I', self.img[o:o+4])[0]))      offset = self.getLong3(o + 4)      (c, a) = self.getAddr(offset + 4)      print "%s %d %s/%s" % (ip, offset, c, a)def getIP(ip):  import os  _localDir=os.path.dirname(__file__)  _curpath=os.path.normpath(os.path.join(os.getcwd(),_localDir))  curpath=_curpath  i = IPInfo(curpath+'/qqwry.dat')  (c, a) = i.getIPAddr(ip)  return c+adef main():  import os  _localDir=os.path.dirname(__file__)  _curpath=os.path.normpath(os.path.join(os.getcwd(),_localDir))  curpath=_curpath  i = IPInfo(curpath+'/qqwry.dat')  if os.path.exists(sys.argv[1]):    for line in open(sys.argv[1],"r").readlines():      line = line.replace("\r","").replace("\n","")      (c, a) = i.getIPAddr(line)      # Demon註:如果是在Windows命令列中運行把編碼轉回gb2312以避免亂碼      if sys.platform == 'win32':        c = unicode(c, 'utf-8').encode('gb2312')        a = unicode(a, 'utf-8').encode('gb2312')      print '%s %s/%s' % (line, c, a)  else:    (c, a) = i.getIPAddr(sys.argv[1])    # Demon註:如果是在Windows命令列中運行把編碼轉回gb2312以避免亂碼    if sys.platform == 'win32':      c = unicode(c, 'utf-8').encode('gb2312')      a = unicode(a, 'utf-8').encode('gb2312')    print '%s %s/%s' % (sys.argv[1], c, a) if __name__ == '__main__':  main()

用Python指令碼查詢純真IP庫QQWry.dat(Demon修改版)

由於要用 Python 讀取一個和純真IP資料庫 QQWry.dat 格式差不多的 IPv6 資料庫,所以在網上搜尋了一下,在 LinuxTOY 看到了一個 Python 指令碼,發現有一些小小的問題,於是修改了一下。

#!/usr/bin/env python# coding: utf-8# from: http://linuxtoy.org/files/pyip.py# Blog: http://linuxtoy.org/archives/python-ip.html# Modified by Demon# Blog: http://demon.tw/programming/python-qqwry-dat.html'''用Python指令碼查詢純真IP庫QQWry.Dat的格式如下:+----------+| 檔案頭 | (8位元組)+----------+| 記錄區 | (不定長)+----------+| 索引區 | (大小由檔案頭決定)+----------+檔案頭:4位元組開始索引位移值+4位元組結尾索引位移值記錄區: 每條IP記錄格式 ==> IP地址[國家資訊][地區資訊]  對於國家記錄,可以有三種表示方式:    字串形式(IP記錄第5位元組不等於0x01和0x02的情況),    重新導向模式1(第5位元組為0x01),則接下來3位元組為國家資訊儲存地的位移值    重新導向模式(第5位元組為0x02),    對於地區記錄,可以有兩種表示方式: 字串形式和重新導向  最後一條規則:重新導向模式1的國家記錄後不能跟地區記錄索引區: 每條索引記錄格式 ==> 4位元組起始IP地址 + 3位元組指向IP記錄的位移值  索引區的IP和它指向的記錄區一條記錄中的IP構成一個IP範圍。查詢資訊是這個  範圍內IP的資訊'''import sysimport socketfrom struct import pack, unpackclass IPInfo(object):  '''QQWry.Dat資料庫查詢功能集合  '''  def __init__(self, dbname):    ''' 初始化類,讀取資料庫內容為一個字串,    通過開始8位元組確定資料庫的索引資訊'''        self.dbname = dbname    # f = file(dbname, 'r')    # Demon註:在Windows下用'r'會有問題,會把\r\n轉換成\n    # 詳見http://demon.tw/programming/python-open-mode.html    # 還有Python文檔中不提倡用file函數來開啟檔案,推薦用open    f = open(dbname, 'rb')    self.img = f.read()    f.close()    # QQWry.Dat檔案的開始8位元組是索引資訊,前4位元組是開始索引的位移值,    # 後4位元組是結束索引的位移值。    # (self.firstIndex, self.lastIndex) = unpack('II', self.img[:8])    # Demon註:unpack預設使用的endian是和機器有關的    # Intel x86和AMD64(x86-64)是little-endian    # Motorola 68000和PowerPC G5是big-endian    # 而純真資料庫全部採用了little-endian位元組序    # 所以在某些big-endian的機器上原代碼會出錯    (self.firstIndex, self.lastIndex) = unpack('<II', self.img[:8])    # 每條索引長7位元組,這裡得到索引總個數    self.indexCount = (self.lastIndex - self.firstIndex) / 7 + 1    def getString(self, offset = 0):    ''' 讀取字串資訊,包括"國家"資訊和"地區"資訊    QQWry.Dat的記錄區每條資訊都是一個以'\0'結尾的字串'''        o2 = self.img.find('\0', offset)    #return self.img[offset:o2]    # 有可能只有國家資訊沒有地區資訊,    gb2312_str = self.img[offset:o2]    try:      utf8_str = unicode(gb2312_str,'gb2312').encode('utf-8')    except:      return '未知'    return utf8_str  def getLong3(self, offset = 0):    '''QQWry.Dat中的位移記錄都是3位元組,本函數取得3位元組的位移量的常規表示    QQWry.Dat使用“字串“儲存這些值'''    s = self.img[offset: offset + 3]    s += '\0'    # unpack用一個'I'作為format,後面的字串必須是4位元組    # return unpack('I', s)[0]    # Demon註:和上面一樣,強制使用little-endian    return unpack('<I', s)[0]  def getAreaAddr(self, offset = 0):    ''' 通過給出位移值,取得地區資訊字串,'''        byte = ord(self.img[offset])    if byte == 1 or byte == 2:      # 第一個位元組為1或者2時,取得2-4位元組作為一個位移量調用自己      p = self.getLong3(offset + 1)      return self.getAreaAddr(p)    else:      return self.getString(offset)  def getAddr(self, offset, ip = 0):    img = self.img    o = offset    byte = ord(img[o])    if byte == 1:      # 重新導向模式1      # [IP][0x01][國家和地區資訊的絕對位移地址]      # 使用接下來的3位元組作為位移量調用位元組取得資訊      return self.getAddr(self.getLong3(o + 1))        if byte == 2:      # 重新導向模式2      # [IP][0x02][國家資訊的絕對位移][地區資訊字串]      # 使用國家資訊位移量調用自己取得字串資訊      cArea = self.getAreaAddr(self.getLong3(o + 1))      o += 4      # 跳過前4位元組取字串作為地區資訊      aArea = self.getAreaAddr(o)      return (cArea, aArea)          if byte != 1 and byte != 2:      # 最簡單的IP記錄形式,[IP][國家資訊][地區資訊]      # 重新導向模式1有種情況就是位移量指向包含國家和地區資訊兩個字串      # 即位移量指向的第一個位元組不是1或2,就使用這裡的分支      # 簡單地說:取連續取兩個字串!      cArea = self.getString(o)      #o += len(cArea) + 1      # 我們已經修改cArea為utf-8字元編碼了,len取得的長度會有變,      # 用下面方法得到offset      o = self.img.find('\0',o) + 1      aArea = self.getString(o)      return (cArea, aArea)  def find(self, ip, l, r):    ''' 使用二分法尋找網路位元組編碼的IP地址的索引記錄'''    if r - l <= 1:      return l    m = (l + r) / 2    o = self.firstIndex + m * 7    #new_ip = unpack('I', self.img[o: o+4])[0]    # Demon註:和上面一樣,強制使用little-endian    new_ip = unpack('<I', self.img[o: o+4])[0]    if ip <= new_ip:      return self.find(ip, l, m)    else:      return self.find(ip, m, r)      def getIPAddr(self, ip):    ''' 調用其他函數,取得資訊!'''    # 使用網路位元組編碼IP地址    ip = unpack('!I', socket.inet_aton(ip))[0]    # 使用 self.find 函數尋找ip的索引位移    i = self.find(ip, 0, self.indexCount - 1)    # 得到索引記錄    o = self.firstIndex + i * 7    # 索引記錄格式是: 前4位元組IP資訊+3位元組指向IP記錄資訊的位移量    # 這裡就是使用後3位元組作為位移量得到其常規表示(QQWry.Dat用字串表示值)    o2 = self.getLong3(o + 4)    # IP記錄位移值+4可以丟棄前4位元組的IP地址資訊。    (c, a) = self.getAddr(o2 + 4)    return (c, a)      def output(self, first, last):    for i in range(first, last):      o = self.firstIndex + i * 7      ip = socket.inet_ntoa(pack('!I', unpack('I', self.img[o:o+4])[0]))      offset = self.getLong3(o + 4)      (c, a) = self.getAddr(offset + 4)      print "%s %d %s/%s" % (ip, offset, c, a)def main():  i = IPInfo('QQWry.Dat')  (c, a) = i.getIPAddr(sys.argv[1])  # Demon註:如果是在Windows命令列中運行把編碼轉回gb2312以避免亂碼  if sys.platform == 'win32':    c = unicode(c, 'utf-8').encode('gb2312')    a = unicode(a, 'utf-8').encode('gb2312')  print '%s %s/%s' % (sys.argv[1], c, a)if __name__ == '__main__':  main()# changelog# 時間:2009年5月29日# 1. 工具下面網友的建議,修改"o += len(cArea) + 1"#  http://linuxtoy.org/archives/python-ip.html#comment-113960#  因為這個時候我已經把得到的字串變成utf-8編碼了,長度會有變化!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.