对于开发数据,我从http://dev.maxmind.com/geoip/geolite下载了一个免费的IP地址对应城市数据库。这个数据库包含了两个重要的文件:包含了IP地址范围和城市ID范围信息的Geo- LiteCity-Blocks.csv,以及包含了城市ID对应的名称、地区、国家以及一些我们不需要的信息的GeoLiteCity-Location.csv
我们首先构造一个允许我们使用一个IP地址并将它转换为城市ID的查找表,然后构造第二个允许我们使用一个城市ID并将它转换成它实际的城市信息(也包含了地区和国家信息)的查找表。
这个使用一个IP地址并将它转换为城市ID的查找表将被构造成单个ZSET,这个ZSET使用城市ID作为成员,IP地址作为得分。为了允许我们从IP地址映射到城市ID,我们将点分形式的IP地址转换成整形得分,采用32位无符号整数中每八位为一个字节,第一个字节为作为最高位。执行这些操作的代码看这里
#代码https://github.com/huangz1990/riacn-code/blob/master/ch05_listing_source.py#L318def ip_to_score(ip_address): score = 0 for v in ip_address.split(‘.‘): score = score * 256 + int(v, 10) return score
当我们有了得分,我们首先添加将IP地址映射到城市ID,为了从一般城市ID构造一个唯一城市ID(因为多个IP地址可以映射到同一个城市ID),我们将添加一个_字符随后接着一件添加到ZSET中的数量,我们可以看如下代码:
#https://github.com/huangz1990/riacn-code/blob/master/ch05_listing_source.py#L316def import_ips_to_redis(conn, filename): csv_file = csv.reader(open(filename, ‘rb‘)) for count, row in enumerate(csv_file): # 按需将IP地址转换为分值。 start_ip = row[0] if row else ‘‘ if ‘i‘ in start_ip.lower(): continue if ‘.‘ in start_ip: start_ip = ip_to_score(start_ip) elif start_ip.isdigit(): start_ip = int(start_ip, 10) else: # 略过文件的第一行以及格式不正确的条目。 continue # 构建唯一城市ID。 city_id = row[2] + ‘_‘ + str(count) # 将城市ID及其对应的IP地址分值添加到有序集合里面。 conn.zadd(‘ip2cityid:‘, city_id, start_ip)
执行完该函数,效果如下图所示:
当IP地址通过import_ips_to_redis()全部加载,我们将创建一个ZSET用来映射城市ID到城市信息,如下代码所示。我们将使用json格式来存储城市信息,因为我们的数据是不会随着时间而改变的固定格式。
#https://github.com/huangz1990/riacn-code/blob/master/ch05_listing_source.py#L354def import_cities_to_redis(conn, filename): for row in csv.reader(open(filename, ‘rb‘)): if len(row) < 4 or not row[0].isdigit(): continue row = [i.decode(‘latin-1‘) for i in row] # 准备好需要添加到散列里面的信息。 city_id = row[0] country = row[1] region = row[2] city = row[3] # 将城市信息添加到Redis里面。 conn.hset(‘cityid2city:‘, city_id, json.dumps([city, region, country]))
执行完该代码后,效果如图所示:
现在我们已经在Redis中有了全部的信息,可以开始查找IP地址了。
http://dev.maxmind.com/geoip/geolite#sthash.Y0BjekhR.dpuf
http://dev.maxmind.com/geoip/geolite#sthash.Y0BjekhR.dpuf