Python使用纯真年代数据库qqwry.dat转换物理位置

PS:网上直接找的,贴出来,方便以后随时用,感谢分享的人。

#!/usr/bin/python
#encoding: utf-8

import socket
import codecs
import mmap
from struct import pack, unpack

def decode_str(old):
    ‘‘‘专门对纯真的gbk编码字符串解压
    返回 utf8 字符串
    ‘‘‘
    try:
        return unicode(old,‘gbk‘).encode(‘utf-8‘)
    except:
        # 当字符串解码失败,并且最一个字节值为‘\x96‘,则去掉它,再解析
        if old[-1] == ‘\x96‘:
            try:
                return unicode(old[:-1],‘gbk‘).encode(‘utf-8‘) + ‘?‘
            except:
                pass

        return ‘Invalid‘

class QQWry(object):
    def __init__(self, path):
        self.path = path
        self.db = None
        self.open_db()
        self.idx_start, self.idx_end = self._read_idx()
        # IP索引总数
        self.total = (self.idx_end - self.idx_start) / 7 + 1

    def open_db(self):
        if not self.db:
            self.db = open(self.path, ‘rb‘)
            self.db = mmap.mmap(self.db.fileno(), 0, access = 1)
        return self.db

    def _read_idx(self):
        ‘‘‘
        读取数据库中IP索引起始和结束偏移值
        ‘‘‘
        self.db.seek(0)
        start = unpack(‘I‘, self.db.read(4))[0]
        end = unpack(‘I‘, self.db.read(4))[0]
        return start, end

    def version(self):
        ‘‘‘
        返回纯真IP库的版本信息
        格式如 "纯真网络2014年8月5日IP数据"
        ‘‘‘
        ip_end_offset = self.read_offset(self.idx_end + 4)
        a_raw, b_raw = self.read_record(ip_end_offset+4)
        return decode_str(a_raw + b_raw)

    def read_ip(self, off, seek=True):
        ‘‘‘
        读取ip值(4字节整数值)
        返回IP值
        ‘‘‘
        if seek:
            self.db.seek(off)
        buf = self.db.read(4)
        return unpack(‘I‘, buf)[0]

    def read_offset(self, off, seek=True):
        ‘‘‘
        读取3字节的偏移量值
        返回偏移量的整数值
        ‘‘‘
        if seek:
            self.db.seek(off)
        buf = self.db.read(3)
        return unpack(‘I‘, buf+‘\0‘)[0]

    def read_string(self, offset):
        ‘‘‘
        读取原始字符串(以"\0"结束)
        返回元组:字符串
        ‘‘‘
        if offset == 0:
            return ‘N/A1‘
        flag = self.get_flag(offset)
        if flag == 0:
            return ‘N/A2‘
        elif flag == 2:
            # 0x02 表示该处信息还是需要重定向
            offset = self.read_offset(offset+1)
            return self.read_string(offset)
        self.db.seek(offset)
        raw_string  = ‘‘
        while True:
            x = self.db.read(1)
            if x == ‘\0‘:
                break
            raw_string += x
        return raw_string

    def get_flag(self, offset):
        ‘‘‘
        读取偏移处的1字节整数值
        QQWry地址信息字符串的第一个字节值可能会是一个标志位,
        这是一个通用的函数.
        ‘‘‘
        self.db.seek(offset)
        c = self.db.read(1)
        if not c:
            return 0
        return ord(c)

    def read_record(self, offset):
        self.db.seek(offset)
        # 读取 flag
        flag = ord(self.db.read(1))
        if flag == 1:
            # 0x01 表示记录区记录(国家,地区)信息都重定向
            # 注意:一次重定向后记录还有可能是一个重定向(其flag=0x02)
            buf = self.db.read(3)
            a_offset = unpack(‘I‘, buf+‘\0‘)[0]
            a_raw = self.read_string(a_offset)
            # 判断新记录的flag是否为0x02,如果是,则表明:
            # - 国家信息重定向另外地址
            # - 地区信息为新记录起始地址偏移4字节
            a_flag = self.get_flag(a_offset)
            if a_flag == 2:
                b_raw = self.read_string(a_offset+4)
            else:
                b_raw = self.read_string(a_offset+len(a_raw)+1)
        elif flag == 2:
            # 0x02 表示仅国家记录重定向
            # 地区信息偏移4字节
            buf = self.db.read(3)
            a_offset = unpack(‘I‘, buf+‘\0‘)[0]
            a_raw = self.read_string(a_offset)
            b_raw = self.read_string(offset+4)
        else:
            # 正常的信息记录
            a_raw = self.read_string(offset)
            b_raw = self.read_string(offset+len(a_raw)+1)
        return a_raw, b_raw

    def output(self, output_file=‘ip.txt‘):
        ‘‘‘
        输出所有IP信息到文件
        ‘‘‘
        fp = codecs.open(output_file, ‘w‘, ‘utf8‘)
        idx = self.idx_start
        while idx <= self.idx_end:
            ip_int = self.read_ip(idx)
            ip_start = socket.inet_ntoa(pack(‘!I‘, ip_int))
            ip_end_offset = self.read_offset(idx + 4)
            ip_int = self.read_ip(ip_end_offset)
            ip_end = socket.inet_ntoa(pack(‘!I‘, ip_int))
            a_raw, b_raw = self.read_record(ip_end_offset+4)
            a_info = decode_str(a_raw)
            b_info = decode_str(b_raw)
            fp.write(u‘%15s\t%15s\t%s,%s\n‘ %(
                ip_start, ip_end,
                a_info.decode(‘utf8‘), b_info.decode(‘utf8‘)))
            # 步进7字节:4字节的起始IP值 + 3字节的结束IP偏移值
            idx += 7
        fp.close()

    def find(self, ip, l, r):
        ‘‘‘
        使用二分法查找网络字节编码的IP地址的索引记录
        ‘‘‘
        if r - l <= 1:
            return l
        m = (l + r) / 2
        offset = self.idx_start + m * 7
        new_ip = self.read_ip(offset)
        if ip < new_ip:
            return self.find(ip, l, m)
        else:
            return self.find(ip, m, r)

    def query(self, ip):
        ‘‘‘
        查询IP信息
        ‘‘‘
        # 使用网络字节编码IP地址
        ip = unpack(‘!I‘, socket.inet_aton(ip))[0]
        # 使用 self.find 函数查找ip的索引偏移
        i = self.find(ip, 0, self.total - 1)
        # 得到索引记录
        o = self.idx_start + i * 7
        # 索引记录格式是: 前4字节IP信息+3字节指向IP记录信息的偏移量
        # 这里就是使用后3字节作为偏移量得到其常规表示(QQWry.Dat用字符串表示值)
        o2 = self.read_offset(o + 4)
        # IP记录偏移值+4可以丢弃前4字节的IP地址信息。
        (c, a) = self.read_record(o2 + 4)
        return (decode_str(c), decode_str(a))

    def __del__(self):
        if self.db:
            self.db.close()

def main():
    dbpath = "C:\ipdata\qqwry.dat"
    ip = "183.61.60.23"
    qqwry = QQWry(dbpath)
    c, a = qqwry.query(ip)
    print ‘%s %s--%s‘ % (ip, c.decode(‘utf-8‘), a.decode(‘utf-8‘))

if __name__ == ‘__main__‘:
    main()

  

Result:

时间: 2024-08-12 05:26:09

Python使用纯真年代数据库qqwry.dat转换物理位置的相关文章

java读取真IP数据库QQwry.dat的源代码(转载)

原帖:http://blog.csdn.net/swazn_yj/article/details/1611020 一.IPEntry.java /** ** 一条IP范围记录,不仅包括国家和区域,也包括起始IP和结束IP *  *  * @author swallow */public class IPEntry {    public String beginIp;    public String endIp;    public String country;    public Stri

python绝技 — 使用PyGeoIP关联IP地址和物理位置

准备工作 要关联IP与物理位置,我们需要有一个包含这样对应关系的数据库. 我们可以使用开源数据库GeoLiteCity,它能够较为准确地把IP地址与所在城市关联起来 下载地址:http://dev.maxmind.com/geoip/legacy/geolite/ 下载之后我们解压:xz -d GeoLiteCity.dat.xz,如:/My/lib/ip/GeoLiteCity.dat 安装pygeoip库.这个库用于对GeoLiteCity数据库的查询 代码: #!/usr/bin/pyth

python 全栈 数据库 (三) python操作数据库

python 操作MYSQL数据库主要有两种方式: 使用原生模块:pymysql ORM框架:SQLAchemy 一.pymysql 1.1下载安装模块 第一种:cmd下:执行命令下载安装:pip3 install pymysql 第二种:IDE下pycharm python环境路径下添加模块 1.2使用操作 #导入模块 import pymysql #建立连接通道,建立连接填入(连接数据库的IP地址,端口号,用户名,密码,要操作的数据库,字符编码) conn = pymysql.connect

MySQL---连接器(python如何操作数据库媒介,基于python语言)

MySQL — 连接器 连接器的概念 它们往往是一类Python包,或者是一类已经写好的Python库.这些库提供了我们Python去连接数据库服务器的基本功能. ? 既然它是一个包,那么我们首先学会导入这个包 #! /usr/bin/env python3 # coding: utf-8 ? from mysql import connector print('导入成功') 执行以上代码 $ python3 test_connector.py 导入成功 表示我们成功导入了相关的包或者模块 连接

QQWry.dat 数据写入

纯真IP库 数据多,更新及时,很多同学在用,网上关于其读取的帖子也有不少(当然其中有一些是有BUG的),但却很少有关于其写入的帖子.OK,下面分享下写QQWry.dat. QQWry.dat 分三个部分 :文件头,记录区,索引区. 一:首先写文件头,文件头的内容只有8个字节,首四个字节是第一条索引的绝对偏移,后四个字节是最后一条索引的绝对偏移.但是一开始我们还不知道这两个偏移量,那么就先随便写点啥,占个位吧,等后面索引写完了再回来修改. string path = HttpContext.Cur

数据库的行列转换问题

在平常的工作中或者面试中,我们可能有遇到过数据库的行列转换问题.今天我们就来讨论下. 1.创建表 首先我们来创建一张表. sql语句: --1. 创建数据表 if OBJECT_ID('Score') is not null drop table Score create table Score ( 姓名 nvarchar(128), 课程 nvarchar(128), 分数 int ) insert into Score values('张三','语文',98) insert into Sco

在qqwry.dat里查询IP地址归属地,Python3版

for Python 3.0+ 类似代码网上也有,这个的特点就是: 1.有两套实现供选择.有一个查找速度更快,但占用更多内存. 2.看着干净,仅import array和bisect这两个Python自带的模块. 用法:q = QQwry()q.load_file('qqwry.dat')q.lookup('8.8.8.8')详细用法见qqwry.py文件里面的说明 github上的项目:https://github.com/animalize/qqwry-python3 源码: # codin

python操作mysql数据库

连接数据库 输入值 存入数据库 关闭 import string import mysql.connector conn=mysql.connector.connect(user='root',password='test',database='dalian',use_unicode=True) cursor=conn.cursor() a=raw_input('enter an id: ') b=raw_input('enter a name: ') while(a!='quit' or b!

Python使用MySQL数据库(新)

之前写过一篇 Python使用MySQL数据库的博客,主要使用的是Python2和MySQLdb驱动. python使用mysql数据库 然而,2016年开始,我从Python2切换到了Python3,Python2已经基本不再使用,MySQLdb驱动从2014年1月停止了维护.所以,打算重新再来写这篇博客. Python2 ---> Python3 MySQLdb --> PyMySQL 一,安装PyMySQL Python是编程语言,MySQL是数据库,它们是两种不同的技术:要想使Pyth