python访问纯真IP数据库的代码
程序员文章站
2023-11-03 21:17:04
核心代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from bisect import bise...
核心代码:
#!/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')
代码打包下载
接下来为大家分享更完美的代码:
#!/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 sys import socket from 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+a def 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 sys import socket from 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 += 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编码了,长度会有变化!
上一篇: Python之列表实现栈的工作功能