1、QQWry IP纯真数据库
纯真版IP地址数据库是当前网络上最权威、地址最精确、IP记录以及网吧数据最多的IP地址数据库。收集了包括中国电信、中国移动、中国联通、铁通、长城宽带等各ISP的最新准确IP地址数据。由于IP地址数据是民间收集的,而IP地址段会经常更改,所以有点遗漏、错误是难免的,也因此纯真IP数据库每5天更新一次。
数据库和代码下载地址:QQWry_IPLook.zip,源码如下:
#!/usr/bin/env python2
# -*- coding: UTF-8 -*-
from struct import unpack, pack
import sys, _socket, mmap
import os
DataFileName = "qq_ip_database.Dat"
def _ip2ulong(ip):
'''ip(0.0.0.0) -> unsigned long'''
return unpack('>L', _socket.inet_aton(ip))[0]
def _ulong2ip(ip):
'''unsigned long -> ip(0.0.0.0)'''
return _socket.inet_ntoa(pack('>L', ip))
class QQIpQueryBase:
'''
QQIpQueryBase, 提供基本查找功能.
注意返回的国家和地区信息都是未解码的字符串, 对于简体版数据库应为GB编码, 对于繁体版则应为BIG5编码.
'''
class ipInfo(tuple):
'''
方便输出 ip 信息的类.
ipInfo((sip, eip, country, area)) -> ipInfo object
'''
def __str__(self):
return str(self[0]).ljust(16) + ' - ' + str(self[1]).rjust(16) + ' ' + self[2] + self[3]
def normalize(self):
'''
转化ip地址成点分十进制.
'''
return QQIpQueryBase.ipInfo((_ulong2ip(self[0]), _ulong2ip(self[1]), self[2], self[3]))
def __init__(self, dbfile):
'''
QQIpQueryBase(dbfile) -> QQIpQueryBase object
dbfile 是数据库文件的 file 对象.
'''
self.f = dbfile
self.f.seek(0)
self.indexBaseOffset = unpack('<L', self.f.read(4))[0] # 索引区基址
self.Count = (unpack('<L', self.f.read(4))[0] - self.indexBaseOffset) / 7 # 索引数-1
def Lookup(self, ip):
'''
x.Lookup(ip) -> (sip, eip, country, area) 查找 ip 所对应的位置.
ip, sip, eip 是点分十进制记录的 ip 字符串.
sip, eip 分别是 ip 所在 ip 段的起始 ip 与结束 ip.
'''
return self.nLookup(_ip2ulong(ip))
def nLookup(self, ip):
'''
x.nLookup(ip) -> (sip, eip, country, area) 查找 ip 所对应的位置.
ip 是 unsigned long 型 ip 地址.
其它同 x.Lookup(ip).
'''
si = 0
ei = self.Count
if ip < self._readIndex(si)[0]:
raise StandardError('IP NOT Found.')
elif ip >= self._readIndex(ei)[0]:
si = ei
else: # keep si <= ip < ei
while (si + 1) < ei:
mi = (si + ei) // 2
if self._readIndex(mi)[0] <= ip:
si = mi
else:
ei = mi
ipinfo = self[si]
if ip > ipinfo[1]:
raise StandardError('IP NOT Found.')
else:
return ipinfo
def __str__(self):
tmp = []
tmp.append('RecCount:')
tmp.append(str(len(self)))
tmp.append('\nVersion:')
tmp.extend(self[self.Count].normalize()[2:])
return ''.join(tmp)
def __len__(self):
return self.Count + 1
def __getitem__(self, key):
'''
x[key]
若 key 为整数, 则返回第key条记录(从0算起, 注意与 x.nLookup(ip) 不一样).
若 key 为点分十进制的 ip 描述串, 同 x.Lookup(key).
'''
if type(key) == type(0):
if (key >= 0) and (key <= self.Count):
index = self._readIndex(key)
sip = index[0]
self.f.seek(index[1])
eip = unpack('<L', self.f.read(4))[0]
(country, area) = self._readRec()
return QQIpQueryBase.ipInfo((sip, eip, country, area))
else:
raise KeyError('INDEX OUT OF RANGE.')
elif type(key) == type(''):
try:
return self.Lookup(key).normalize()
except StandardError, e:
if e.message == 'IP NOT Found.':
raise KeyError('IP NOT Found.')
else:
raise e
else:
raise TypeError('WRONG KEY TYPE.')
def __iter__(self):
'''返回迭代器(生成器).'''
for i in range(0, len(self)):
yield self[i]
def _read3ByteOffset(self):
'''_read3ByteOffset() -> unsigned long 从文件 f 读入长度为3字节的偏移.'''
return unpack('<L', self.f.read(3) + '\x00')[0]
def _readCStr(self):
'''x._readCStr() -> string 读 '\0' 结尾的字符串.'''
if self.f.tell() == 0:
return 'Unknown'
tmp = []
ch = self.f.read(1)
while ch != '\x00':
tmp.append(ch)
ch = self.f.read(1)
return ''.join(tmp)
def _readIndex(self, n):
'''x._readIndex(n) -> (ip ,offset) 读取第n条索引.'''
self.f.seek(self.indexBaseOffset + 7 * n)
return unpack('<LL', self.f.read(7) + '\x00')
def _readRec(self, onlyOne=False):
'''x._readRec() -> (country, area) 读取记录的信息.'''
mode = unpack('B', self.f.read(1))[0]
if mode == 0x01:
rp = self._read3ByteOffset()
bp = self.f.tell()
self.f.seek(rp)
result = self._readRec(onlyOne)
self.f.seek(bp)
return result
elif mode == 0x02:
rp = self._read3ByteOffset()
bp = self.f.tell()
self.f.seek(rp)
result = self._readRec(True)
self.f.seek(bp)
if not onlyOne:
result.append(self._readRec(True)[0])
return result
else: # string
self.f.seek(-1, 1)
result = [self._readCStr()]
if not onlyOne:
result.append(self._readRec(True)[0])
return result
pass # End of class QQIpQueryBase
class QQIpQuery(QQIpQueryBase):
'''QQIpQuery 类.'''
def __init__(self, filename='qq_ip_database.Dat'):
'''QQIpQuery(filename) -> QQIpQuery object
filename 是数据库文件名.
'''
f = open(filename, 'rb')
QQIpQueryBase.__init__(self, f)
class MQQIpQuery(QQIpQueryBase):
'''MQQIpQuery 类.
将数据库放到内存的 QQIpQuery 类.查询速度大约快两倍.
'''
def __init__(self, filename=DataFileName, dbfile=None):
'''MQQIpQuery(filename[,dbfile]) -> MQQIpQuery object
filename 是数据库文件名.
也可以直接提供 dbfile 文件对象. 此时 filename 被忽略.
'''
if dbfile == None:
try:
UPLOAD_DIR = os.path.dirname(os.path.realpath(__file__))
filename = '%s/%s' % (UPLOAD_DIR, filename)
dbf = open(filename, 'rb')
except IOError:
print "ERROR:", filename, "is not exist!"
sys.exit(1)
else:
dbf = dbfile
bp = dbf.tell()
dbf.seek(0)
QQIpQueryBase.__init__(self, mmap.mmap(dbf.fileno(), 0, access=1))
dbf.seek(bp)
def _readCStr(self):
'''x._readCStr() -> string 读 '\0' 结尾的字符串.'''
pstart = self.f.tell()
if pstart == 0:
return 'unknown'
else:
pend = self.f.find('\x00', pstart)
if pend < 0:
raise StandardError('Fail To Read CStr.')
else:
self.f.seek(pend + 1)
return self.f[pstart:pend].decode('GBK').encode('UTF-8')
def _readIndex(self, n):
'''x._readIndex(n) -> (ip ,offset) 读取第n条索引.'''
startp = self.indexBaseOffset + 7 * n
return unpack('<LL', self.f[startp:startp + 7] + '\x00')
#def get_ip_list(ip_list):
# try:
# Q = MQQIpQuery()
# results = []
#
# print ip_list
# for item in ip_list:
# address = str(item['address'])
# count = item['count']
# result = {}
# result['address'] = address
# result['region'] = ''.join(Q[address][2:])
# result['type'] = ''.join(Q[address][3:])
# result['count'] = count
# print "result:", result
# results.append(result)
# print results
# return results
# except Exception, err:
# print err
# return None
if __name__ == '__main__':
try:
Q = MQQIpQuery() # 数据库文件名为 ./qq_ip_database.Dat
if len(sys.argv) == 1:
print Q
if len(sys.argv) == 2:
if sys.argv[1] == '-': # 参数只有一个“-”时, 从标准输入读取IP
print ''.join(Q[raw_input()][2:])
elif sys.argv[1] in ('all', '-a', '-all'): # 遍历示例代码
for i in Q:
print i.normalize()
else: # 参数只有一个IP时, 只输出简要的信息
print ''.join(Q[sys.argv[1]][2:])
else:
for i in sys.argv[1:]:
print Q[i]
except StandardError, e:
if e.message != '':
print e.message
else:
raise e
finally:
pass
ZIP文件中主要的文件是qq_ip_database.Dat和qq_ip_query.py,前者是数据文件,后者是代码文件,使用方法如下:
python qq_ip_query.py 115.28.143.213
对于纯真IP数据库的其他详细介绍,大家可以参考:纯真IP数据库格式详解, 使用Python查询纯真IP
2、淘宝IP查询服务
淘宝IP服务目前支持根据用户提供的IP地址,快速查询出该IP地址所在的地理信息和地理相关的信息,包括国家、省、市和运营商。优势是信息维度广,格式规范,,省准确度超过99.8%,市准确度超过96.8%,另外提供完善的统计分析报表;唯一的缺憾是有次数限制:每个用户的访问频率需小于10qps(每秒10次查询),对于访问量较大的网站和服务来说肯定是不行的(不知道能不能付费或者合作解除这个限制)。
虽然说有每秒10次的限制,但是淘宝的IP数据库还是比较全面的,提供的信息很多,官网地址是:淘宝IP地址库(具体的介绍和统计数据可以到这里查看),使用示例如下:
http://ip.taobao.com/service/getIpInfo.php?ip=115.28.143.213
返回的JSON数据格式如下:
{
code: 0,
data: {
country: "中国",
country_id: "CN",
area: "华东",
area_id: "300000",
region: "山东省",
region_id: "370000",
city: "青岛市",
city_id: "370200",
county: "",
county_id: "-1",
isp: "阿里巴巴",
isp_id: "100098",
ip: "115.28.143.213"
}
}
3、IPInfoDB.com
IPInfoDB是国外的一个免费的IP地理定位工具,它提供了XML和JSON两种方式的API,并给出了多种语言的调用方式,API返回的信息也比较多,而且还包括了经纬度信息(这一点儿挺不错哦~),具体示例可以参考:IPInfoDB API。
使用IPInfoDB的API来获取地理位置信息,需要首先在IPInfoDB注册一个账号,然后它会给你一个唯一的API KEY,调用API的时候需要将这个key作为参数传过去。以JSON API为例,请求示例为:http://api.ipinfodb.com/v3/ip-city/?key=API_KEY&ip=115.28.143.213&format=json,返回的JSON数据格式如下:
{
statusCode: "OK",
statusMessage: "",
ipAddress: "115.28.143.213",
countryCode: "CN",
countryName: "CHINA",
regionName: "BEIJING",
cityName: "BEIJING",
zipCode: "-",
latitude: "39.9075",
longitude: "116.397",
timeZone: "+08:00"
}
PS:我没有在具体的项目中使用IPInfoDB,所以不太清楚有什么限制没有,据网站上说是免费的;但是对于部署在国内的网站或者服务来说,IPInfoDB并不是很好的选择方案,毕竟它是国外的服务,访问速度没办法保证,而且又被墙的可能,所以个人也不建议使用这种方法。。。。
4、IPaddressAPI.com
同样是国外的服务,一个比较专业、准确的IP定位服务(网站上的口号。。),其实它和IPInfoDB差不多,同样提供了API的访问方式,同样需要注册申请API KEY,当然也同样有一样的问题(访问速度 & 被墙的可能)。。。
官网是:IPaddressAPI,API的访问方式:http://www.ipaddressapi.com/l/your_api_code?h=HOST,其他的信息大家可以参考官网说明,这里就不在赘述(好吧,我承认我是懒的翻译了)。。。。
5、Mysql IP数据库
这种方案是将IP和IP的信息存放在MySQL数据库中,在需要的时候通过查询数据库得到地理位置信息。它的优点是方便简单,缺点是地理信息较少,IP不太完整,同时需要自己实现查询数据库的代码,如果数据库优化不好,查询速度很慢。。这里提供一个IP数据库:ip2nation.sql,大家可以试试。
Over!