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

