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()

数据库表结构:

posted @ 2020-07-12 16:49  jasonminghao  阅读(2061)  评论(0编辑  收藏  举报