Python 实现IP段扫描
1. 简介
很多童鞋都会有这样一个需求,我想要扫描特定网段并需要知道未使用和已使用的IP有哪些,甚至需要将其做统计,那这时候用Python去实现IP段扫描就会比较的轻松,当前文中我是将数据保存到mongo中,这里的代码只做参考,需要根据实际的场景进行修改!!
2. 代码实现
import time
import IPy
from concurrent.futures import ThreadPoolExecutor
import subprocess
from pymongo import MongoClient
class ip_check(object):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 数据库相关
self.client = MongoClient('127.0.0.1', 27017)
self.db = self.client['devops']
# self.save_dbname = "snmp_ping_result" # 存储历史记录
self.store_db = 'snmp_ping'
# 存放IP地址
self.ip_allocated = [] # 已分配/已使用的IP
self.ip_not_allocated = [] # 未分配/未使用的IP
self.ip_error = [] # 访问失败或异常的IP
# icmp相关(ping底层用的就是icmp协议)
self.icmp_timeout = 5 # 超时时间
self.icmp_count = 5 # 发送次数
# 并发相关
self.is_thread = True # 是否多线程(建议开启,否则检测速度异常的慢)
self.max_thread_workers = 100 # 最大线程池数
def run(self):
self.get_result()
def get_result(self):
# 存放最终返回的结果
# result = {}
# 获取网段地址
segment = '172.16.58.0/24'
db = self.db[self.store_db]
# todo 从数据库中查询对应segment的数据,但是这里注意,如果不需要指定 segment 来查询的话,可以直接查询到所有IP数据,然后通过for循环的方式一个一个网段的ping
data = db.find_one({'segment': segment})
if data is None:
raise Exception('网段不存在')
_ips = IPy.IP(data.get('segment')) # IPy 获取所有IP地址
# todo 获取该网段已使用的IP 一般这里是从数据库中查询
exist_ip = data.get('used_ip')
# 过滤已存在的IP
ips = [str(ip) for ip in _ips if str(ip) not in exist_ip]
"""并发执行ping"""
max_workers = self.max_thread_workers if self.max_thread_workers else 20
if self.is_thread:
with ThreadPoolExecutor(max_workers) as executor:
for ip in ips:
executor.submit(self.send_ping, ip)
else:
for ip in ips:
self.send_ping(ip)
"""统计IP数量"""
# 只有当 总ip数量 = 已使用IP + 剩余IP + 异常IP 才能确保所有IP都被扫描到了
if len(ips) == (len(self.ip_allocated) + len(self.ip_not_allocated) + len(self.ip_error)):
total_ip, used_ip, available_ip, err_ip = len(ips), len(self.ip_allocated), len(self.ip_not_allocated), len(
self.ip_error)
print("""
总IP:%s
已使用IP: %s
剩余IP:%s
故障的IP:%s
""" % (total_ip, used_ip, available_ip, err_ip))
else:
# todo 之后会增加日志记录或者是报异常
...
# todo 最后需要将结果存储到数据库中
save_result = {
'$set': {
'total_ip': len(ips),
'used_ip': self.ip_allocated,
'available_ip': self.ip_not_allocated,
'err_ip': self.ip_error,
}
}
db.update_one(data, save_result)
"""处理异常的IP"""
# todo 保留,可以对异常的IP再次发起二次ping或者是其他处理
def send_ping(self, ip):
"""
发送ping命令,执行IP扫描
:param ip: 字符串形式的IP地址,例如:'172.16.2.12'
:return:
"""
if ip:
try:
pipe = subprocess.run(f"ping -c {self.icmp_count} -t {self.icmp_timeout} {ip} > /dev/null 2>&1",
shell=True)
code = pipe.returncode # 获取执行状态码 ping通:返回0,ping不通:返回 2(非0)
if code == 0:
self.ip_allocated.append(ip) # 已使用的IP
elif code != 0 or code == 2:
self.ip_not_allocated.append(ip) # 未使用的IP
except Exception as e:
# icmp不可达的记录为异常IP
print(f'ip:{ip} 访问异常: {e}')
self.ip_error.append(ip) # 异常IP
else:
# todo 之后会增加日志记录或者是报异常
...
if __name__ == "__main__":
x = ip_check()
x.run()
数据库表结构: