核心代码:
#!/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字节)
+----------+
| 记录区 | (不定长)
+----------+
| 索引区 | (大小由文件头决定)
+----------+
文件头:4字节开始索引偏移值+4字节结尾索引偏移值
记录区: 每条IP记录格式 ==> IP地址[国家信息][地区信息]
对于国家记录,可以有三种表示方式:
字符串形式(IP记录第5字节不等于0x01和0x02的情况),
重定向模式1(第5字节为0x01),则接下来3字节为国家信息存储地的偏移值
重定向模式(第5字节为0x02),
对于地区记录,可以有两种表示方式: 字符串形式和重定向
最后一条规则:重定向模式1的国家记录后不能跟地区记录
索引区: 每条索引记录格式 ==> 4字节起始IP地址 + 3字节指向IP记录的偏移值
索引区的IP和它指向的记录区一条记录中的IP构成一个IP范围。查询信息是这个
范围内IP的信息
'''
import sys
import socket
from struct import pack, unpack
class IPInfo(object):
'''QQWry.Dat数据库查询功能集合
'''
def __init__(self, dbname):
''' 初始化类,读取数据库内容为一个字符串,
通过开始8字节确定数据库的索引信息'''
self.dbname = dbname
# f = file(dbname, 'r')
# Demon注:在Windows下用'r'会有问题,会把\r\n转换成\n
# 详见http://demon.tw/programming/python-open-mode.html
# 还有Python文档中不提倡用file函数来打开文件,推荐用open
f = open(dbname, 'rb')
self.img = f.read()
f.close()
# QQWry.Dat文件的开始8字节是索引信息,前4字节是开始索引的偏移值,
# 后4字节是结束索引的偏移值。
# (self.firstIndex, self.lastIndex) = unpack('II', self.img[:8])
# Demon注:unpack默认使用的endian是和机器有关的
# Intel x86和AMD64(x86-64)是little-endian
# Motorola 68000和PowerPC G5是big-endian
# 而纯真数据库全部采用了little-endian字节序
# 所以在某些big-endian的机器上原代码会出错
(self.firstIndex, self.lastIndex) = unpack('<II', self.img[:8])
# 每条索引长7字节,这里得到索引总个数
self.indexCount = (self.lastIndex - self.firstIndex) / 7 + 1
def getString(self, offset = 0):
''' 读取字符串信息,包括"国家"信息和"地区"信息
QQWry.Dat的记录区每条信息都是一个以'\0'结尾的字符串'''
o2 = self.img.find('\0', offset)
#return self.img[offset:o2]
# 有可能只有国家信息没有地区信息,
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字节,本函数取得3字节的偏移量的常规表示
QQWry.Dat使用"字符串"存储这些值'''
s = self.img[offset: offset + 3]
s += '\0'
# unpack用一个'I'作为format,后面的字符串必须是4字节
# return unpack('I', s)[0]
# Demon注:和上面一样,强制使用little-endian
return unpack('<I', s)[0]
def getAreaAddr(self, offset = 0):
''' 通过给出偏移值,取得区域信息字符串,'''
byte = ord(self.img[offset])
if byte == 1 or byte == 2:
# 第一个字节为1或者2时,取得2-4字节作为一个偏移量调用自己
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][国家和地区信息的绝对偏移地址]
# 使用接下来的3字节作为偏移量调用字节取得信息
return self.getAddr(self.getLong3(o + 1))
if byte == 2:
# 重定向模式2
# [IP][0x02][国家信息的绝对偏移][地区信息字符串]
# 使用国家信息偏移量调用自己取得字符串信息
cArea = self.getAreaAddr(self.getLong3(o + 1))
o += 4
# 跳过前4字节取字符串作为地区信息
aArea = self.getAreaAddr(o)
return (cArea, aArea)
if byte != 1 and byte != 2:
# 最简单的IP记录形式,[IP][国家信息][地区信息]
# 重定向模式1有种情况就是偏移量指向包含国家和地区信息两个字符串
# 即偏移量指向的第一个字节不是1或2,就使用这里的分支
# 简单地说:取连续取两个字符串!
cArea = self.getString(o)
#o += 2*len(cArea) + 1
# 我们已经修改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 = "联通"
return (cArea, aArea)
def find(self, ip, l, r):
''' 使用二分法查找网络字节编码的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注:和上面一样,强制使用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):
''' 调用其他函数,取得信息!'''
# 使用网络字节编码IP地址
ip = unpack('!I', socket.inet_aton(ip))[0]
# 使用 self.find 函数查找ip的索引偏移
i = self.find(ip, 0, self.indexCount - 1)
# 得到索引记录
o = self.firstIndex + i * 7
# 索引记录格式是: 前4字节IP信息+3字节指向IP记录信息的偏移量
# 这里就是使用后3字节作为偏移量得到其常规表示(QQWry.Dat用字符串表示值)
o2 = self.getLong3(o + 4)
# IP记录偏移值+4可以丢弃前4字节的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命令行中运行把编码转回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命令行中运行把编码转回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数据库 QQWry.dat 格式差不多的 IPv6 数据库,所以在网上搜索了一下,在 LinuxTOY 看到了一个 Python 脚本,发现有一些小小的问题,于是修改了一下。
#!/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字节)
+----------+
| 记录区 | (不定长)
+----------+
| 索引区 | (大小由文件头决定)
+----------+
文件头:4字节开始索引偏移值+4字节结尾索引偏移值
记录区: 每条IP记录格式 ==> IP地址[国家信息][地区信息]
对于国家记录,可以有三种表示方式:
字符串形式(IP记录第5字节不等于0x01和0x02的情况),
重定向模式1(第5字节为0x01),则接下来3字节为国家信息存储地的偏移值
重定向模式(第5字节为0x02),
对于地区记录,可以有两种表示方式: 字符串形式和重定向
最后一条规则:重定向模式1的国家记录后不能跟地区记录
索引区: 每条索引记录格式 ==> 4字节起始IP地址 + 3字节指向IP记录的偏移值
索引区的IP和它指向的记录区一条记录中的IP构成一个IP范围。查询信息是这个
范围内IP的信息
'''
import sys
import socket
from struct import pack, unpack
class IPInfo(object):
'''QQWry.Dat数据库查询功能集合
'''
def __init__(self, dbname):
''' 初始化类,读取数据库内容为一个字符串,
通过开始8字节确定数据库的索引信息'''
self.dbname = dbname
# f = file(dbname, 'r')
# Demon注:在Windows下用'r'会有问题,会把\r\n转换成\n
# 详见http://demon.tw/programming/python-open-mode.html
# 还有Python文档中不提倡用file函数来打开文件,推荐用open
f = open(dbname, 'rb')
self.img = f.read()
f.close()
# QQWry.Dat文件的开始8字节是索引信息,前4字节是开始索引的偏移值,
# 后4字节是结束索引的偏移值。
# (self.firstIndex, self.lastIndex) = unpack('II', self.img[:8])
# Demon注:unpack默认使用的endian是和机器有关的
# Intel x86和AMD64(x86-64)是little-endian
# Motorola 68000和PowerPC G5是big-endian
# 而纯真数据库全部采用了little-endian字节序
# 所以在某些big-endian的机器上原代码会出错
(self.firstIndex, self.lastIndex) = unpack('<II', self.img[:8])
# 每条索引长7字节,这里得到索引总个数
self.indexCount = (self.lastIndex - self.firstIndex) / 7 + 1
def getString(self, offset = 0):
''' 读取字符串信息,包括"国家"信息和"地区"信息
QQWry.Dat的记录区每条信息都是一个以'\0'结尾的字符串'''
o2 = self.img.find('\0', offset)
#return self.img[offset:o2]
# 有可能只有国家信息没有地区信息,
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字节,本函数取得3字节的偏移量的常规表示
QQWry.Dat使用"字符串"存储这些值'''
s = self.img[offset: offset + 3]
s += '\0'
# unpack用一个'I'作为format,后面的字符串必须是4字节
# return unpack('I', s)[0]
# Demon注:和上面一样,强制使用little-endian
return unpack('<I', s)[0]
def getAreaAddr(self, offset = 0):
''' 通过给出偏移值,取得区域信息字符串,'''
byte = ord(self.img[offset])
if byte == 1 or byte == 2:
# 第一个字节为1或者2时,取得2-4字节作为一个偏移量调用自己
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][国家和地区信息的绝对偏移地址]
# 使用接下来的3字节作为偏移量调用字节取得信息
return self.getAddr(self.getLong3(o + 1))
if byte == 2:
# 重定向模式2
# [IP][0x02][国家信息的绝对偏移][地区信息字符串]
# 使用国家信息偏移量调用自己取得字符串信息
cArea = self.getAreaAddr(self.getLong3(o + 1))
o += 4
# 跳过前4字节取字符串作为地区信息
aArea = self.getAreaAddr(o)
return (cArea, aArea)
if byte != 1 and byte != 2:
# 最简单的IP记录形式,[IP][国家信息][地区信息]
# 重定向模式1有种情况就是偏移量指向包含国家和地区信息两个字符串
# 即偏移量指向的第一个字节不是1或2,就使用这里的分支
# 简单地说:取连续取两个字符串!
cArea = self.getString(o)
#o += len(cArea) + 1
# 我们已经修改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):
''' 使用二分法查找网络字节编码的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注:和上面一样,强制使用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):
''' 调用其他函数,取得信息!'''
# 使用网络字节编码IP地址
ip = unpack('!I', socket.inet_aton(ip))[0]
# 使用 self.find 函数查找ip的索引偏移
i = self.find(ip, 0, self.indexCount - 1)
# 得到索引记录
o = self.firstIndex + i * 7
# 索引记录格式是: 前4字节IP信息+3字节指向IP记录信息的偏移量
# 这里就是使用后3字节作为偏移量得到其常规表示(QQWry.Dat用字符串表示值)
o2 = self.getLong3(o + 4)
# IP记录偏移值+4可以丢弃前4字节的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命令行中运行把编码转回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. 工具下面网友的建议,修改"o += len(cArea) + 1"
# http://linuxtoy.org/archives/python-ip.html#comment-113960
# 因为这个时候我已经把得到的字符串变成utf-8编码了,长度会有变化!
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。