Python:从IP地址列表生成IP范围

5

我有一个.csv文件,其中包含几个数据中心的IP地址列表。目前的列表看起来类似于下表:

  Data_Center_Name      IP
              DC_1      52.102.182.2
              DC_1      52.102.182.4
              DC_1      52.102.182.1
              DC_1      52.102.182.5
              DC_1      52.102.182.3
              DC_1      27.101.178.17
              DC_1      27.101.178.16
              DC_1      27.101.178.15
              DC_1      23.201.165.7
              DC_2      55.200.162.10
              DC_2      55.200.162.12
              DC_2      55.200.162.13
              DC_2      55.200.162.11
              DC_3      30.101.102.4

我想将列表转换为单个列表,例如:
     DC_1 = [52.102.182.1-52.102.182.5,
             27.101.178.15-27.101.178.17,
             23.201.165.7]
     DC_2 = [55.200.162.10-55.200.162.13]
     DC_3 = [30.101.102.4]

有人能帮我用Python吗?


编程中几乎总是不建议以编程方式生成变量名(即使用locals)。您是否考虑使用dict代替? - Snakes and Coffee
我认为你的结果不正确,52.102.182.2并不存在,但是如果你想说的是将 s/52/50/,那么确实存在 50.102.182.2 - salparadise
@salparadise:我的错,已经编辑并修正了问题。 - Preetesh Gaitonde
3个回答

3
我的解决方案是:
  1. 将每个IP地址转换为十进制数

  2. 排序并从数字列表中获取范围(区间)

  3. 将它们转换为IP格式。

输入:
ips = [ "52.102.182.2", "52.102.182.4", "52.102.182.1", "52.102.182.5", "52.102.182.3", 
        "27.101.178.17", "27.101.178.16", "27.101.178.15",
        "23.201.165.7", ]

步骤1:

IP => 二进制 => 十进制

# Convert ips to binary strings
bins = [''.join([bin(int(i))[2:].zfill(8) for i in ip.split('.')]) for ip in ips]

# Convert binary strings to decimal numbers
numbers = [int(b, 2) for b in bins]

或者 IP => 十进制

# Convert ips to decimal numbers
numbers = [sum((256 ** (3 - k)) * int(n) for k, n in enumerate(ip.split('.'))) for ip in ips]

步骤二:

# Sort decimal numbers
numbers.sort()

# Get ranges from decimal numbers
ranges = []
tmp = []
for i in range(len(numbers)):
    tmp.append(numbers[i])
    if (i == len(numbers) - 1) or (numbers[i + 1] > numbers[i] + 1):
        if len(tmp) == 1:
            ranges.append(tmp[0])
        else:
            ranges.append((tmp[0], tmp[-1]))
        tmp = []

第三步:

# Convert dec ranges to ip ranges
def dec_to_ip(n):
    return '.'.join([str(int(n % 256 ** (4 - k) / 256 ** (3 - k))) for k in range(4)])

# Final result
ip_ranges = [(dec_to_ip(r[0]), dec_to_ip(r[1])) if type(r) == tuple else dec_to_ip(r) for r in ranges]

输出:

['23.201.165.7', ('27.101.178.15', '27.101.178.17'), ('52.102.182.1', '52.102.182.5')]

我执行了 print(ip_ranges),输出结果如下:['23.787674367427826.201.64463806152344.165.02734375.7.0', ('27.397248208522797.101.69554138183594.178.05859375.15.0', '27.397248327732086.101.69557189941406.178.06640625.17.0'), ('52.40121465921402.102.71095275878906.182.00390625.1.0', '52.4012148976326.102.71101379394531.182.01953125.5.0')] - Preetesh Gaitonde
@Rooney:我使用的是Python 2,dec_to_ip(n)函数中的%存在问题,我已经编辑了答案以使其与Python 3兼容。 - Huu-Danh Pham

2

*由于粗心阅读问题,此答案已进行编辑*

对于单个list范围:

df[['P1','P2']]=df.IP.str.rsplit('.',1).apply(pd.Series)
d=df.sort_values(['Data_Center_Name','P1','P2']).\
    groupby(['Data_Center_Name','P1']).\
       IP.apply(lambda x : x.iloc[0]+'-'+x.iloc[-1] if len(x)>1 else x.iloc[0] )
d
Out[388]: 
Data_Center_Name  P1        
DC_1              23.201.165                   23.201.165.7
                  27.101.178    27.101.178.15-27.101.178.17
                  50.102.182                   50.102.182.2
                  52.102.182      52.102.182.1-52.102.182.5
DC_2              55.200.162    55.200.162.10-55.200.162.13
DC_3              30.101.102                   30.101.102.4
Name: IP, dtype: object

为了得到您的结果

d.groupby(level=0).apply(list)
Out[392]: 
Data_Center_Name
DC_1    [23.201.165.7, 27.101.178.15-27.101.178.17, 50...
DC_2                        [55.200.162.10-55.200.162.13]
DC_3                                       [30.101.102.4]
Name: IP, dtype: object

3
谢谢 Wen!我已经得到了这个结果。我正在尝试将其以 IP 范围的形式呈现,而不是在字典或列表中列出单独的 IP 地址。 - Preetesh Gaitonde
@Rooney 抱歉给你带来了误导 :-) - BENY

2

使用Python3(如果需要可以使用Python2)

利用内置库ipaddressgroupby以及其他内置好用的工具:

创建一个将IP地址对象列表转换为范围的函数:

def create_range(ip_addresses):
    groups=[]
    for _, g in itertools.groupby(enumerate(sorted(ip_addresses)), lambda (i,x):i-int(x)):
       group = map(operator.itemgetter(1), g)
       if len(group) > 1:
           groups.append("{}-{}".format(group[0], str(group[-1]).split('.')[-1]))
       else:
           groups.append(str(group[0]))
    return groups

导入所需的库,从csv中解析出值(使用StringIO模拟读取文件):

import csv ## for reading csv file
import ipaddress ## for creating ip address objects
import io ## for mimicking reading csv file
import operator ## for grouping operation
import itertools ## for grouping operation
import collections ## for creating a defaultdict

ips = defaultdict(list)
csv_file = u"""Data_Center_Name,      IP
              DC_1,      50.102.182.2
              DC_1,      52.102.182.4
              DC_1,      52.102.182.1
              DC_1,      52.102.182.5
              DC_1,      52.102.182.3
              DC_1,      27.101.178.17
              DC_1,      27.101.178.16
              DC_1,      27.101.178.15
              DC_1,      23.201.165.7
              DC_2,      55.200.162.10
              DC_2,      55.200.162.12
              DC_2,      55.200.162.13
              DC_2,      55.200.162.11
              DC_3,      30.101.102.4
"""

with io.StringIO(csv_file) as f:
    reader = list(csv.reader(f))
    for (dc, ip) in reader[1:]:
        ip = ipaddress.IPv4Address(unicode(ip.strip()))
        ips[dc.strip()].append(ip)
    result = {dc: create_range(ip_range) for dc, ip_range in ips.items()}

结果:

In [92]: result
Out[92]:
{'DC_1': ['23.201.165.7',
  '27.101.178.15-17',
  '50.102.182.2',
  '52.102.182.1',
  '52.102.182.3-5'],
 'DC_2': ['55.200.162.10-13'],
 'DC_3': ['30.101.102.4']}

Python2

import csv ## for reading csv file
import ipaddress ## for creating ip address objects
from StringIO import StringIO  ## for mimicking reading csv file
import operator ## for grouping operation
import itertools ## for grouping operation
import collections ## for creating a defaultdict

def create_range(ip_addresses):
    groups=[]
    for _, g in itertools.groupby(enumerate(sorted(ip_addresses)), lambda (i,x):i-int(x)):
       group = map(operator.itemgetter(1), g)
       if len(group) > 1:
           groups.append("{}-{}".format(group[0], str(group[-1]).split('.')[-1]))
       else:
           groups.append(str(group[0]))
    return groups

ips = collections.defaultdict(list)

csv_file = """Data_Center_Name,      IP
              DC_1,      50.102.182.2
              DC_1,      52.102.182.4
              DC_1,      52.102.182.1
              DC_1,      52.102.182.5
              DC_1,      52.102.182.3
              DC_1,      27.101.178.17
              DC_1,      27.101.178.16
              DC_1,      27.101.178.15
              DC_1,      23.201.165.7
              DC_2,      55.200.162.10
              DC_2,      55.200.162.12
              DC_2,      55.200.162.13
              DC_2,      55.200.162.11
              DC_3,      30.101.102.4
"""

reader = csv.reader(StringIO(csv_file))
next(reader)
for (dc, ip) in reader:
    ip = ipaddress.IPv4Address(unicode(ip.strip()))
    ips[dc.strip()].append(ip)
result = {dc: create_range(ip_range) for dc, ip_range in ips.items()}

使用 Py2 代码的结果

print result
{'DC_2': ['55.200.162.10-13'], 'DC_3': ['30.101.102.4'], 'DC_1': ['23.201.165.7', '27.101.178.15-17', '50.102.182.2', '52.102.182.1', '52.102.182.3-5']}

做得好!谢谢。是否可能获得输出:{'DC_2': ['55.200.162.10-55.200.162.13'], 'DC_3': ['30.101.102.4'], 'DC_1': ['23.201.165.7', '27.101.178.15-27.101.178.17', '50.102.182.2', '52.102.182.1', '52.102.182.3-52.102.182.5']} –

是的,请更改这一行:

groups.append("{}-{}".format(group[0], str(group[-1]).split('.')[-1]))

到这个:
groups.append("{}-{}".format(group[0], group[-1]))

谢谢!您能提供一个Python 2的解决方案吗? 在lambda (i,x):i-int(x))处我得到了一个错误,因为“tuple parameter unpacking is not supported in python 3”(元组参数解包在Python 3中不受支持)。 - Preetesh Gaitonde
我还遇到了以下错误:对于 len(group) - TypeError: object of type 'map' has no len()。对于 groups.append(str(group[0])) - map object is not subscriptable - Preetesh Gaitonde
@Rooney 关于你的错误,看起来你没有将正确的对象发送到函数中,请确保它是一个 IPv4Network 对象列表。 - salparadise
对于Python2:ImportError: No module named ipaddress 我正在使用Python 2.7,它为我抛出了一个错误。 - Preetesh Gaitonde
@rooney,这个不是Python2标准库的一部分,而是被移植过来的,所以需要安装它。 - salparadise
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接