python訪問純真IP數(shù)據(jù)庫的代碼
核心代碼:
#!/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字節(jié)) +----------+ | 記錄區(qū) | (不定長) +----------+ | 索引區(qū) | (大小由文件頭決定) +----------+ 文件頭:4字節(jié)開始索引偏移值+4字節(jié)結(jié)尾索引偏移值 記錄區(qū): 每條IP記錄格式 ==> IP地址[國家信息][地區(qū)信息] 對于國家記錄,可以有三種表示方式: 字符串形式(IP記錄第5字節(jié)不等于0x01和0x02的情況), 重定向模式1(第5字節(jié)為0x01),則接下來3字節(jié)為國家信息存儲地的偏移值 重定向模式(第5字節(jié)為0x02), 對于地區(qū)記錄,可以有兩種表示方式: 字符串形式和重定向 最后一條規(guī)則:重定向模式1的國家記錄后不能跟地區(qū)記錄 索引區(qū): 每條索引記錄格式 ==> 4字節(jié)起始IP地址 + 3字節(jié)指向IP記錄的偏移值 索引區(qū)的IP和它指向的記錄區(qū)一條記錄中的IP構(gòu)成一個IP范圍。查詢信息是這個 范圍內(nèi)IP的信息 ''' import sys import socket from struct import pack, unpack class IPInfo(object): '''QQWry.Dat數(shù)據(jù)庫查詢功能集合 ''' def __init__(self, dbname): ''' 初始化類,讀取數(shù)據(jù)庫內(nèi)容為一個字符串, 通過開始8字節(jié)確定數(shù)據(jù)庫的索引信息''' self.dbname = dbname # f = file(dbname, 'r') # Demon注:在Windows下用'r'會有問題,會把\r\n轉(zhuǎn)換成\n # 詳見http://demon.tw/programming/python-open-mode.html # 還有Python文檔中不提倡用file函數(shù)來打開文件,推薦用open f = open(dbname, 'rb') self.img = f.read() f.close() # QQWry.Dat文件的開始8字節(jié)是索引信息,前4字節(jié)是開始索引的偏移值, # 后4字節(jié)是結(jié)束索引的偏移值。 # (self.firstIndex, self.lastIndex) = unpack('II', self.img[:8]) # Demon注:unpack默認(rèn)使用的endian是和機(jī)器有關(guān)的 # Intel x86和AMD64(x86-64)是little-endian # Motorola 68000和PowerPC G5是big-endian # 而純真數(shù)據(jù)庫全部采用了little-endian字節(jié)序 # 所以在某些big-endian的機(jī)器上原代碼會出錯 (self.firstIndex, self.lastIndex) = unpack('<II', self.img[:8]) # 每條索引長7字節(jié),這里得到索引總個數(shù) self.indexCount = (self.lastIndex - self.firstIndex) / 7 + 1 def getString(self, offset = 0): ''' 讀取字符串信息,包括"國家"信息和"地區(qū)"信息 QQWry.Dat的記錄區(qū)每條信息都是一個以'\0'結(jié)尾的字符串''' o2 = self.img.find('\0', offset) #return self.img[offset:o2] # 有可能只有國家信息沒有地區(qū)信息, 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字節(jié),本函數(shù)取得3字節(jié)的偏移量的常規(guī)表示 QQWry.Dat使用“字符串“存儲這些值''' s = self.img[offset: offset + 3] s += '\0' # unpack用一個'I'作為format,后面的字符串必須是4字節(jié) # return unpack('I', s)[0] # Demon注:和上面一樣,強(qiáng)制使用little-endian return unpack('<I', s)[0] def getAreaAddr(self, offset = 0): ''' 通過給出偏移值,取得區(qū)域信息字符串,''' byte = ord(self.img[offset]) if byte == 1 or byte == 2: # 第一個字節(jié)為1或者2時,取得2-4字節(jié)作為一個偏移量調(diào)用自己 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][國家和地區(qū)信息的絕對偏移地址] # 使用接下來的3字節(jié)作為偏移量調(diào)用字節(jié)取得信息 return self.getAddr(self.getLong3(o + 1)) if byte == 2: # 重定向模式2 # [IP][0x02][國家信息的絕對偏移][地區(qū)信息字符串] # 使用國家信息偏移量調(diào)用自己取得字符串信息 cArea = self.getAreaAddr(self.getLong3(o + 1)) o += 4 # 跳過前4字節(jié)取字符串作為地區(qū)信息 aArea = self.getAreaAddr(o) return (cArea, aArea) if byte != 1 and byte != 2: # 最簡單的IP記錄形式,[IP][國家信息][地區(qū)信息] # 重定向模式1有種情況就是偏移量指向包含國家和地區(qū)信息兩個字符串 # 即偏移量指向的第一個字節(jié)不是1或2,就使用這里的分支 # 簡單地說:取連續(xù)取兩個字符串! cArea = self.getString(o) #o += 2*len(cArea) + 1 # 我們已經(jīng)修改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 = "聯(lián)通" return (cArea, aArea) def find(self, ip, l, r): ''' 使用二分法查找網(wǎng)絡(luò)字節(jié)編碼的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注:和上面一樣,強(qiáng)制使用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): ''' 調(diào)用其他函數(shù),取得信息!''' # 使用網(wǎng)絡(luò)字節(jié)編碼IP地址 ip = unpack('!I', socket.inet_aton(ip))[0] # 使用 self.find 函數(shù)查找ip的索引偏移 i = self.find(ip, 0, self.indexCount - 1) # 得到索引記錄 o = self.firstIndex + i * 7 # 索引記錄格式是: 前4字節(jié)IP信息+3字節(jié)指向IP記錄信息的偏移量 # 這里就是使用后3字節(jié)作為偏移量得到其常規(guī)表示(QQWry.Dat用字符串表示值) o2 = self.getLong3(o + 4) # IP記錄偏移值+4可以丟棄前4字節(jié)的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命令行中運(yùn)行把編碼轉(zhuǎn)回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命令行中運(yùn)行把編碼轉(zhuǎn)回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數(shù)據(jù)庫 QQWry.dat 格式差不多的 IPv6 數(shù)據(jù)庫,所以在網(wǎng)上搜索了一下,在 LinuxTOY 看到了一個 Python 腳本,發(fā)現(xiàn)有一些小小的問題,于是修改了一下。
#!/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字節(jié)) +----------+ | 記錄區(qū) | (不定長) +----------+ | 索引區(qū) | (大小由文件頭決定) +----------+ 文件頭:4字節(jié)開始索引偏移值+4字節(jié)結(jié)尾索引偏移值 記錄區(qū): 每條IP記錄格式 ==> IP地址[國家信息][地區(qū)信息] 對于國家記錄,可以有三種表示方式: 字符串形式(IP記錄第5字節(jié)不等于0x01和0x02的情況), 重定向模式1(第5字節(jié)為0x01),則接下來3字節(jié)為國家信息存儲地的偏移值 重定向模式(第5字節(jié)為0x02), 對于地區(qū)記錄,可以有兩種表示方式: 字符串形式和重定向 最后一條規(guī)則:重定向模式1的國家記錄后不能跟地區(qū)記錄 索引區(qū): 每條索引記錄格式 ==> 4字節(jié)起始IP地址 + 3字節(jié)指向IP記錄的偏移值 索引區(qū)的IP和它指向的記錄區(qū)一條記錄中的IP構(gòu)成一個IP范圍。查詢信息是這個 范圍內(nèi)IP的信息 ''' import sys import socket from struct import pack, unpack class IPInfo(object): '''QQWry.Dat數(shù)據(jù)庫查詢功能集合 ''' def __init__(self, dbname): ''' 初始化類,讀取數(shù)據(jù)庫內(nèi)容為一個字符串, 通過開始8字節(jié)確定數(shù)據(jù)庫的索引信息''' self.dbname = dbname # f = file(dbname, 'r') # Demon注:在Windows下用'r'會有問題,會把\r\n轉(zhuǎn)換成\n # 詳見http://demon.tw/programming/python-open-mode.html # 還有Python文檔中不提倡用file函數(shù)來打開文件,推薦用open f = open(dbname, 'rb') self.img = f.read() f.close() # QQWry.Dat文件的開始8字節(jié)是索引信息,前4字節(jié)是開始索引的偏移值, # 后4字節(jié)是結(jié)束索引的偏移值。 # (self.firstIndex, self.lastIndex) = unpack('II', self.img[:8]) # Demon注:unpack默認(rèn)使用的endian是和機(jī)器有關(guān)的 # Intel x86和AMD64(x86-64)是little-endian # Motorola 68000和PowerPC G5是big-endian # 而純真數(shù)據(jù)庫全部采用了little-endian字節(jié)序 # 所以在某些big-endian的機(jī)器上原代碼會出錯 (self.firstIndex, self.lastIndex) = unpack('<II', self.img[:8]) # 每條索引長7字節(jié),這里得到索引總個數(shù) self.indexCount = (self.lastIndex - self.firstIndex) / 7 + 1 def getString(self, offset = 0): ''' 讀取字符串信息,包括"國家"信息和"地區(qū)"信息 QQWry.Dat的記錄區(qū)每條信息都是一個以'\0'結(jié)尾的字符串''' o2 = self.img.find('\0', offset) #return self.img[offset:o2] # 有可能只有國家信息沒有地區(qū)信息, 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字節(jié),本函數(shù)取得3字節(jié)的偏移量的常規(guī)表示 QQWry.Dat使用“字符串“存儲這些值''' s = self.img[offset: offset + 3] s += '\0' # unpack用一個'I'作為format,后面的字符串必須是4字節(jié) # return unpack('I', s)[0] # Demon注:和上面一樣,強(qiáng)制使用little-endian return unpack('<I', s)[0] def getAreaAddr(self, offset = 0): ''' 通過給出偏移值,取得區(qū)域信息字符串,''' byte = ord(self.img[offset]) if byte == 1 or byte == 2: # 第一個字節(jié)為1或者2時,取得2-4字節(jié)作為一個偏移量調(diào)用自己 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][國家和地區(qū)信息的絕對偏移地址] # 使用接下來的3字節(jié)作為偏移量調(diào)用字節(jié)取得信息 return self.getAddr(self.getLong3(o + 1)) if byte == 2: # 重定向模式2 # [IP][0x02][國家信息的絕對偏移][地區(qū)信息字符串] # 使用國家信息偏移量調(diào)用自己取得字符串信息 cArea = self.getAreaAddr(self.getLong3(o + 1)) o += 4 # 跳過前4字節(jié)取字符串作為地區(qū)信息 aArea = self.getAreaAddr(o) return (cArea, aArea) if byte != 1 and byte != 2: # 最簡單的IP記錄形式,[IP][國家信息][地區(qū)信息] # 重定向模式1有種情況就是偏移量指向包含國家和地區(qū)信息兩個字符串 # 即偏移量指向的第一個字節(jié)不是1或2,就使用這里的分支 # 簡單地說:取連續(xù)取兩個字符串! cArea = self.getString(o) #o += len(cArea) + 1 # 我們已經(jīng)修改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): ''' 使用二分法查找網(wǎng)絡(luò)字節(jié)編碼的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注:和上面一樣,強(qiáng)制使用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): ''' 調(diào)用其他函數(shù),取得信息!''' # 使用網(wǎng)絡(luò)字節(jié)編碼IP地址 ip = unpack('!I', socket.inet_aton(ip))[0] # 使用 self.find 函數(shù)查找ip的索引偏移 i = self.find(ip, 0, self.indexCount - 1) # 得到索引記錄 o = self.firstIndex + i * 7 # 索引記錄格式是: 前4字節(jié)IP信息+3字節(jié)指向IP記錄信息的偏移量 # 這里就是使用后3字節(jié)作為偏移量得到其常規(guī)表示(QQWry.Dat用字符串表示值) o2 = self.getLong3(o + 4) # IP記錄偏移值+4可以丟棄前4字節(jié)的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命令行中運(yùn)行把編碼轉(zhuǎn)回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. 工具下面網(wǎng)友的建議,修改"o += len(cArea) + 1" # http://linuxtoy.org/archives/python-ip.html#comment-113960 # 因?yàn)檫@個時候我已經(jīng)把得到的字符串變成utf-8編碼了,長度會有變化!
- Python3.7 pyodbc完美配置訪問access數(shù)據(jù)庫
- 詳解js文件通過python訪問數(shù)據(jù)庫方法
- 對Python通過pypyodbc訪問Access數(shù)據(jù)庫的方法詳解
- Python使用pyodbc訪問數(shù)據(jù)庫操作方法詳解
- Python輕量級ORM框架Peewee訪問sqlite數(shù)據(jù)庫的方法詳解
- Python的Tornado框架實(shí)現(xiàn)異步非阻塞訪問數(shù)據(jù)庫的示例
- Linux下通過python訪問MySQL、Oracle、SQL Server數(shù)據(jù)庫的方法
- python訪問mysql數(shù)據(jù)庫的實(shí)現(xiàn)方法(2則示例)
- python使用MySQLdb訪問mysql數(shù)據(jù)庫的方法
- Python訪問純真IP數(shù)據(jù)庫腳本分享
- 在Linux中通過Python腳本訪問mdb數(shù)據(jù)庫的方法
- Shell、Perl、Python、PHP訪問 MySQL 數(shù)據(jù)庫代碼實(shí)例
- 使用Python通過oBIX協(xié)議訪問Niagara數(shù)據(jù)的示例
相關(guān)文章
解決Python報錯:SyntaxError:?invalid?character?‘,‘?(U+FF0C)
Python中的 SyntaxError錯誤是Python語言中常見的異常錯誤類型之一,表示語法錯誤,下面這篇文章主要給大家介紹了關(guān)于解決Python報錯:SyntaxError:?invalid?character?‘,‘?(U+FF0C)的相關(guān)資料,需要的朋友可以參考下2022-12-12Python調(diào)用SQLPlus來操作和解析Oracle數(shù)據(jù)庫的方法
這篇文章主要介紹了Python調(diào)用SQLPlus來操作和解析Oracle數(shù)據(jù)庫的方法,這樣用SQL*Plus方式來分析Oracle中的數(shù)據(jù)就變得十分方便,需要的朋友可以參考下2016-04-04基于Python實(shí)現(xiàn)GeoServer矢量文件批量發(fā)布
由于矢量圖層文件較多,手動發(fā)布費(fèi)時費(fèi)力,python支持的關(guān)于geoserver包又由于年久失修,無法在較新的geoserver版本中正常使用。本文為大家準(zhǔn)備了Python自動化發(fā)布矢量文件的代碼,需要的可以參考一下2022-07-07