Python 利用 IP 地理库生成随机天朝 IP
首先从 MaxMind 下载一份按照国家分类的 IP 数据库 (csv 格式)。
首先要做的是把里面所有的国内 IP 段提取出来;手动打开 Locations
和 Blocks-IPv4
文件看了下,得知在 Blocks-IPv4 文件内,第一列是 IP 段,第二列是地理 ID;而在 Locations 内,天朝的地理 ID 为 1814991
。
虽然说这些列的序号可能会根据以后更新而打乱,但是目前来看 MaxMind 不会经常调整这个列的序号。毕竟我们用的是个 Quick & Dirty
的解决方案,更新起来也是挺方便的。
提取出来后需要写出到一个可被随机访问的文件(数据库?)内。一个简单的格式就是将每个 IP 段打包为 5 个字节 (IP 占 4 个,掩码占 1 个),随机抽取的时候可以直接定位到需要的位置。最后在文件开始处标记共有多少项,连获取文件大小都可以省掉。
from struct import pack
# Define indexes
I_NETWORK = 0
I_GEONAME_ID = 1
# 从 GeoLite2-Country-Locations-*.csv 获取天朝的地理 ID
GEO_CHINA = 1814991
f_ipv4 = open('GeoLite2-Country-Blocks-IPv4.csv', 'r')
f_output = open('GeoChinaIp.dat', 'wb')
# 跳过 CSV 表头
f_ipv4.readline()
count = 0
f_output.write(pack('I', count))
while True:
address = f_ipv4.readline()
if address == '': break
I = address.split(',')
if not I[I_GEONAME_ID]: continue
network = I[I_NETWORK ]
geo_id = int(I[I_GEONAME_ID])
if geo_id == GEO_CHINA:
count = count + 1
ip, mask = network.split('/')
a, b, c, d = [int(x) for x in ip.split('.')]
f_output.write(pack('BBBBB', a, b, c, d, int(mask)))
f_output.seek(0, 0)
f_output.write(pack('I', count))
f_output.close()
f_ipv4.close()
获取到我们自己的国内 IP 段大全后,就可以写一个通用的 IP 地址生成器了。
因为 IP 其实讲白了就是一个 4 字节的数字,而掩码则可以视为这个段的大小。为了简化计算,直接将 IP 地址作为一个整数来运算即可;段开始地址加上 rand(0, size - 1)
就得到一个 IP 了。
在实际实验中,偶尔有时候会发生生成的 IP 不能通过网易的检测的情况。因此,需要加入一个主动连接网易服务器进行检测的函数。在这里是利用的网页版 API;为了不增加复杂度,我选择事先在浏览器对提交数据进行加密,然后在 Python 内直接传输。若是生成的 IP 不行,那就再生成一个。
在测试的时候能发现,虽然 MaxMind 提供的地理数据库与网易检测用的地理数据库兼容不是特别的完美,几乎每 3 次有一次失败。当然因为是随机抽样检测,这个数据并不能代表整个数据库,但这个成功率也是足够用来作为我们自动更新构造 IP 的小程序了。
最后为了方便一次性生成可用的文件,给程序加上两个参数:其一为输入文件路径,将其的所有 <ip>
都替换为随机生成的一个国内 IP;其二为输出文件路径,将替换后的文件内容写入。
from __future__ import print_function
from re import sub
from sys import argv, stdout
from struct import unpack
from random import randint
try:
# Python 2
from httplib import HTTPConnection
from urllib import urlencode
except:
# Python 3
from urllib.parse import urlencode
from http.client import HTTPConnection
# Run following in http://music.163.com/ to get payload; where 64634 is any id that does not work outside China.
# with(asrsea("{\"ids\":\"[64634]\",\"br\":128000,\"csrf_token\":\"\"}","010001","00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7","0CoJUm6Qyw8W8jud")) JSON.stringify({ params: encText, encSecKey });
payload = {"params":"1jGcS1W77hIKvP/5xvJN4DRJP4qWjWdVXio+Iy4ztTwUQpfehnmLdxKkz8y7tUO1kkQXqe0Cv3wCRrgTfqjoa+ripG5hqvZ1+YUYODcz7es=","encSecKey":"21bfcacc1ee1459e04b0a9970ea7b6775524af3309168549f82084266721a2f29dba89f9be2c84ca627f8369eb64f61aa2002e6bea90a29651445154e96cb0052e12077d6c5094dfcc72f22c07879abc6a5eb69e50ab6fb4293140036b65a465fcd6ca5a54d0672c8a1ec393bc9f22771d4a17762a8d8792e32c522e826ff46f"}
payload = urlencode(payload)
def check_ip(ip):
"""Connect to netease and check if the ip is availiable.
Args:
ip (str): IP Address to be checked
Returns:
bool: True if valid, False otherwise.
"""
conn = HTTPConnection('music.163.com', 80)
headers = {
"Accept":"*/*",
"Content-Type":"application/x-www-form-urlencoded",
"Content-Length":len(payload),
"Referer":"http//music.163.com/",
"User-Agent":"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)",
"X-Real-IP": ip
}
conn.request('POST', '/weapi/song/enhance/player/url?csrf_token=', payload, headers)
resp = conn.getresponse()
r = str(resp.read())
conn.close()
return '"url":"http' in r
def main():
"""IP Picker
Pick a random IP from `GeoChinaIp.dat`,
fill the template file and generate a new file.
Usage: pick_ip.py <tpl_file> <output_file>
All "<ip>" from <tpl_file> will be replaced with a random picked IP.
"""
if len(argv) != 3:
print('IP Picker')
print('Pick a random IP from GeoChinaIp.dat, ')
print('fill the template file and generate a new file.\n')
print('Usage: %s <tpl_file> <output_file>\n' % argv[0])
print('All "<ip>" from <tpl_file> will be replaced with a random picked IP.')
return
with open(argv[1], 'r') as f:
tpl = f.read()
f_input = open('GeoChinaIp.dat', 'rb')
count ,= unpack('I', f_input.read(4))
def pick_ip(unused = 0):
"""Pick a random Chinese IP.
Args:
unused (any): Not used
Returns:
A valid ip that have passed netease check.
"""
while True:
f_input.seek(4 + randint(0, count - 1) * 5, 0)
ip, mask = unpack('>IB', f_input.read(5))
size = pow(2, 32 - mask)
a = (ip >> 24) & 255
b = (ip >> 16) & 255
c = (ip >> 8) & 255
d = (ip >> 0) & 255
print('%d.%d.%d.%d/%d; size = 0x%x' % (a, b, c, d, mask, size))
ip += randint(0, size - 1)
a = (ip >> 24) & 255
b = (ip >> 16) & 255
c = (ip >> 8) & 255
d = (ip >> 0) & 255
str_ip = '%d.%d.%d.%d' % (a, b, c, d)
print('IP: %s, verifying...' % str_ip, end='')
if check_ip(str_ip):
print(' OK!');
break
print(' Failed!')
return str_ip
result = sub(r'<ip>', pick_ip, tpl)
f_input.close()
with open(argv[2], 'w') as f:
f.write(result)
if __name__ == "__main__":
main()
项目文件下载(v2 包含 MaxMind 2017.04.04 版 csv 数据库 CC-By-SA 4.0):