- 帖子
- 2
- 精华
- 0
- 积分
- 18
- 阅读权限
- 10
- 注册时间
- 2017-7-28
- 最后登录
- 2017-8-24
|
#!/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的机器上原代码会出错
# struct.unpack(fmt, buffer)
# Unpack from the buffer buffer (presumably packed by pack(fmt, ...)) according to the format string fmt. The result is # a tuple even if it contains exactly one item. The buffer’s size in bytes must match the size required by the format, # as reflected by calcsize().
(self.firstIndex, self.lastIndex) = unpack('<II', self.img[:8])
# 每条索引长7字节,这里得到索引总个数
indexCount = (self.lastIndex - self.firstIndex) / 7 + 1
self.indexCount = int(indexCount) # 将indexCount转换成整数,应该本来是整数,只是后面小数点后多了一些0
# print(self.indexCount)
def getString(self, offset = 0):
''' 读取字符串信息,包括"国家"信息和"地区"信息
QQWry.Dat的记录区每条信息都是一个以'\0'结尾的字符串'''
o2 = self.img.find(b'\0', offset) # 做的修改,在'\0'前面加上b
print('getString--o2:',o2)
#return self.img[offset2]
# 有可能只有国家信息没有地区信息,
gb2312_str = self.img[offset2]
print('gb2312_str:',gb2312_str)
try:
#utf8_str = unicode(gb2312_str,'gb2312').encode('utf-8') # 意思是gb2312-->unicode-->utf-8
utf8_str = gb2312_str.decode('gb2312').encode('utf-8') # 此处做了修改
print('utf8_str:',utf8_str) # b'\x02<' 不知是什么
except:
return '未知!'
return utf8_str
def getLong3(self, offset = 0):
'''QQWry.Dat中的偏移记录都是3字节,本函数取得3字节的偏移量的常规表示
QQWry.Dat使用“字符串“存储这些值'''
s = self.img[offset: offset + 3]
# s += '\0'
# s += '\0'.encode('ascii') # 此处做了修改,bytes & str 有问题;原来是s+='0'
s += b'\0' # 两种修改结果相同:b'\x00'
# 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]) # 此处做了修改,将ord()去掉了
byte = 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): # 这里丢弃ip了么,貌似是
img = self.img
o = offset
# byte = ord(img[o])
byte = img[o] # 发现直接打印出来本身就是数字, 此处做了修改
# ord(c)
# Given a string representing one Unicode character, return an integer representing the Unicode code point of that character. For example, ord('a') returns the integer 97 and ord('€') (Euro sign) returns 8364. This is the inverse of chr().
print('getAddr--byte:',byte)
if byte == 1:
# 重定向模式1
# [IP][0x01][国家和地区信息的绝对偏移地址]
# 使用接下来的3字节作为偏移量调用字节取得信息
print('byte==1:',self.getLong3(o + 1))
return self.getAddr(self.getLong3(o + 1))
if byte == 2:
# 重定向模式2
# [IP][0x02][国家信息的绝对偏移][地区信息字符串]
# 使用国家信息偏移量调用自己取得字符串信息
cArea = self.getAreaAddr(self.getLong3(o + 1)) # 最终都是getString()函数得到地区信息
o += 4
# 跳过前4字节取字符串作为地区信息
aArea = self.getAreaAddr(o) # 最终都是getString()函数得到地区信息
return (cArea, aArea)
if byte != 1 and byte != 2:
# 最简单的IP记录形式,[IP][国家信息][地区信息]
# 重定向模式1有种情况就是偏移量指向包含国家和地区信息两个字符串
# 即偏移量指向的第一个字节不是1或2,就使用这里的分支
# 简单地说:取连续取两个字符串!
print('byte != 1 and byte != 2')
cArea = self.getString(o) # 最终都是getString()函数得到地区信息
print('cArea:',cArea) # 此处是得到国家信息
#o += len(cArea) + 1
# 我们已经修改cArea为utf-8字符编码了,len取得的长度会有变,
# 用下面方法得到offset
o = self.img.find(b'\0',o) + 1 # 此处加了b
print('offset:',o)
aArea = self.getString(o) # 最终都是getString()函数得到地区信息
print('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
o = int(o) # 转换成整数,此处做了修改
#new_ip = unpack('I', self.img[o: o+4])[0]
# Demon注:和上面一样,强制使用little-endian
new_ip = unpack('<I', self.img[o+4])[0]
if ip <= new_ip:
return self.find(ip, l, m)
else:
return self.find(ip, m, r) # 还没有明白find()函数最后的返回结果是什么
def getIPAddr(self, ip):
''' 调用其他函数,取得信息!'''
# 使用网络字节编码IP地址
ip = unpack('!I', socket.inet_aton(ip))[0]
print('getIPAddr--ip:',ip)
# 使用 self.find 函数查找ip的索引偏移
i = self.find(ip, 0, self.indexCount - 1)
i = int(i) # 此处做了修改,变成整数
# print('i:',i)
# 得到索引记录
o = self.firstIndex + i * 7
# print('o:',o)
# 索引记录格式是: 前4字节IP信息+3字节指向IP记录信息的偏移量
# 这里就是使用后3字节作为偏移量得到其常规表示(QQWry.Dat用字符串表示值)
o = int(o) # 此处做了修改,变成了整数
o2 = self.getLong3(o + 4)
print('getIPAddr--o2:',o2)
# 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+4])[0]))
offset = self.getLong3(o + 4)
(c, a) = self.getAddr(offset + 4)
print("%s %d %s/%s" % (ip, offset, c, a))
# *******************上面定义了IPInfo()类,下面生成实例调用***********************
i = IPInfo(r'C:\Users\Administrator\Downloads\ip\qqwry.dat')
# ip ='61.128.128.68' # 61.128.128.68--湖南省益阳市--休闲(大渡口茶厂对面)
# ip = '61.128.192.4' # 61.128.192.4--湖南省益阳市--休闲(大渡口茶厂对面)
# ip = '112.5.175.6' # 112.5.175.6--爱尔兰--' ????????
# ip = '127.0.0.1' # 127.0.0.1--爱尔兰--' ??????????
ip = '110.127.255.255' # 本应该是北京地区的!
(c, a) = i.getIPAddr(ip) # 中间会打印一些关键属性信息
print('*******result********')
print('未改变编码:',c,a)
c = c.decode('utf-8') # 此处可以出现汉字,但是尝试了好多ip,有的一直显示”爱尔兰“ ??????????
a = a.decode('utf-8') # 此处应该是编码的问题,但是我还没有找到 ??????????? print('%s--%s--%s' % (ip,c,a))
问题:getString()是终极的函数,即被其他函数调用,我在try:...部分做了修改(上文红色部分标注),可能不对;现在最大的问题是有的结果国家地区很奇怪。麻烦你了。
print('%s--%s--%s' % (ip,c,a))
|
|