QQwry是国内很流行的一个中文IP数据库,它使用特别的格式存储IP记录压缩率很高,正因如此,每次升级数据库时得重新下载整库,我希望让它更简单一点,升级只需要下载批量更新数据,我试着把它转换成sqlite3,
先贴我的代码
Python代码:
'''
Created on 2010-4-17
@author: Ben
@mail: ben.pang.china@gmail.com
'''
import
os
import
sys
import
sqlite3
def binToInt(data):
"""将传入的bytes转换为integer
传入的bytes序列将按编号从低位到高位的排序组转换为integer,如:
data[0]~data[3]分别是:0x00 0x01 0x02 0x03
那么它对应的数值的binary是
00000011 00000010 00000001 00000000
等于十进制数值50462976
"""
x = 0
for
i,d in
enumerate(data):
x |= d << ( i * 8 )
return
x
def readRecord(index):
"""根据传入的索引地址读取该记录的全部信息
根据传入的索引地址读取该IP记录,如其实IP,结束IP,country字段,area字段
"""
def readStr():
"""从指定索引读取出文本内容
从指定索引开始向后检索0,提取出字节并转换为字符信息;索引index引用readRecord的变量index,并且在读取字符后将index指向0
"""
nonlocal
index
old,index = index, data.find( b'x00'
, index ) # 从index位置开始查找0所在位置
return
data[ old : index ].decode('gbk'
)
start_ip = '{}.{}.{}.{}'
.format( *reversed(data[ index : index + 4 ]) ) # 将IP字节转换为人可读的IP地址,如: 192.168.1.1
index = binToInt(data[ index + 4 : index + 7 ]) # 获取结束IP的索引地址
end_ip = '{}.{}.{}.{}'
.format( *reversed(data[ index : index + 4 ]) ) # 结束IP地址
# 设置索引变量指向指向end ip后的索引处
index += 4
# 判断IP信息的索引模式
if
data[ index ] == 0x01:
index = binToInt(data[ index + 1 : index + 4 ]) # 重设索引到新位置
if
data[ index ] == 0x02: # 判断country信息是指针还是字符串
old, index = index, binToInt( data[ index + 1 : index + 4 ] ) # 缓存当前索引地址,并将索引变量指向country字符串所在地址
country = readStr() # 读取country信息
index = old + 4 # 将索引指area地址
else
:
country = readStr()
index += 1
if
data[ index ] == 0x01 or
data[ index ] == 0x02:
index = binToInt( data[ index + 1 : index + 4 ] )
area = readStr() # 获取area信息
return
[ start_ip, end_ip, country, area ]
# 全局变量,用于存储一些数据库的常量
data = bytes() # 用于缓存数据库内容
cur = None
c_record = {}
a_record = { "" : None
}
def convert(db):
"""将IP记录从QQway中检索出来并存入sqlite3数据库中"""
if not
os.path.exists(db):
print("Not found db:"
,db)
return
global
data, firstIndex, lastIndex, cur
with
open(db,'rb'
) as
f:
data = f.read() # 从数据库中读取字节并进行缓存
firstIndex = binToInt(data[:4])
lastIndex = binToInt(data[4:8])
conn = sqlite3.connect( "ipdb.sdb3"
)
cur = conn.cursor()
for
i in
range(firstIndex, lastIndex, 7):
saveIpRecord( readRecord(i) )
else
:
conn.commit()
conn.close()
print("converted and saving to ipdb.sql3"
)
def saveIpRecord(record):
"""保存IP记录到数据库"""
global
cur, c_record, a_record
start_ip, end_ip, country, area = record
if
country not in
c_record:
cur.execute("INSERT INTO country VALUES(null, ?)"
, (country, ))
c_record[ country ] = cur.execute("SELECT max(id) FROM country"
).fetchone()[0]
if
area not in
a_record:
cur.execute("INSERT INTO area VALUES(null, ?)"
, (area, ))
a_record[ area ] = cur.execute("SELECT max(id) FROM area"
).fetchone()[0]
cur.execute("INSERT INTO ipaddr VALUES(null, ?, ?, ?, ?)"
, ( start_ip, end_ip, c_record[ country ], a_record[ area ] ))
if
__name__ == '__main__'
:
if
len(sys.argv) == 2:
convert(sys.argv[1])
else
:
print("usage: converter.py database"
)
你需要先了解QQwry.dat的格式,那么应该看的懂我们代码.生成的sqlite3很大是原来的6倍.
不过这只是测试代码,我还能改进优化存储,应该能至少压缩一半,并加快检索速度.