【干货】用Python教你写一个批量ping

用Python教你写一个批量ping

[TOC]

前言

最近几天,刚好需要配合防火墙替换的割接方案,需要去批量ping测试20+个C类网段,约5000+个地址,我同事在网上找的工具也不能很好的一次性ping完所有网段的IP地址,心想,我来帮你搞定,就花些时间劈里啪啦的调试下代码,其中还是遇到一些疑难杂症的,比如下所列:

  • 使用的模块,必须得有回显代码01让你判断是通还是不通,另外ping结果也要保存?
  • 使用的模块,ping回显是通的,但实际用电脑ping是不通的,你说妖不妖?
  • 综合测试了scrapypythonpingsubprocess模块,最终还是选择subprocess内置模块符合我心意。

想要写一个批量的ping脚本,首先需要厘清下编写的思路,要怎么去实现?

这是我整理的思路,我会一步一步教大家怎么去实现,咋们接着往下看...

实验环境

  • 系统:windows 10 和 Centos7.6
  • python工具:Pycharm Pro
  • 模块:内置模块

备注:没啥特别的要求。

代码分解

如何读取文本中的所有网段?

说明:通过上下文管理with读取文本中的所有IP网段信息

假如你把收集的所有IP网段都集中放到了一个文本当中,那么我们在执行脚本的时候,怎么把它提取出来呢?

# 存放ip网段的文本路径(当前目录)
IP_INFO_PATH = 'IPnet.cfg'
# 以只读权限打开文本
with open(IP_INFO_PATH, 'r') as f:
    # for循环遍历每一行
    for line in f.readlines():
        #去掉前后空白字符
        line = line.strip()
        # 忽略空白行或注释行
        if ( len(line) == 1 or line.startswith('#') ):
            continue
        print(line)

执行结果如下所示:

192.168.100.0/24
192.168.101.0/24
192.168.102.0/24
192.168.103.0/24
192.168.104.0/24

代码解释:

  • 先定义好存放ip网段的文本路径:IP_INFO_PATH = 'IPnet.cfg'
  • 打开文本通过上下文管理with,格式:with open('文本路径','只读方式') as '别名'
  • readlines()方法可以读取信息,并通过for循环就遍历所有行信息;
  • len(line) == 1, 表示空白行长度为1,记住不是0。

如何获取网段的主机地址?

说明:该模块我以前有详细写过,请在公众号搜索<用Python帮你实现IP子网计算>

  • 如给你一个网段,你怎么去ping呢?
    对于我们专业来说,心算就可以得出来了,其他同学就拿起子网计算工具吧,那么,我就通过ipaddress模块给大家演示下:

    假如有一个网段:192.168.100.0/24, 快速计算所有的主机地址。

    # 导入内置模块
    import ipaddress
    
    IPnet = '192.168.100.0/24'
    # 创建一个空的列表,用于存放主机地址信息
    ip_list = []
    # 通过ip_network方法,赋值给temp,这是一个生成器
    # 已经计算出所有主机了,需要迭代出来
    temp = ipaddress.ip_network(IPnet, strict=False).hosts()
    # for循环出主机地址,添加到ip_list这个列表
    for ip in temp:
        # print(type(ip), ip)
        ip_list.append(str(ip))
    print(ip_list)
    

    这一步,我们已经掌握如何计算一个网段的所有主机地址了。

如何执行ping程序?

subprocess是一个内置模块,它可以让你创建一个新的子程序来运行。通常使用如下3个方法:

  • subprocess.run()
  • subprocess.call()
  • subprocess.Popen()

这里,我只介绍简单好用的run()方法就够满足我们的需求了。

  • 一个简单的示例:

    import subprocess
    
    ip = '114.114.114.114'
    # windows上cmd的ping操作格式
    res = subprocess.run(['ping', '-n', '2', '-w', '500', ip])
    print('-'*50)
    print('类对象:', res)
    print('什么类型:', type(res))
    print('返回代码值:', res.returncode)
    

    执行结果如下所示:

    如果不想再终端上看到ping的执行结果, 增加参数stdout=subprocess.PIPE

    说明:简单理解就是把执行结果隐藏起来。

    # 修改代码如下
    res = subprocess.run(['ping', '-n', '2', '-w', '500', ip], stdout=subprocess.PIPE)
    
  • 如何把执行的结果写入到文本当中?

    说明:既然隐藏了ping结果,那就要把结果赋值给一个变量,就可以拿到结果了.

    import subprocess
    
    ip_list  = ['8.8.8.8', '114.114.114.114', '114.114.114.115']
    for ip in ip_list:
        res = subprocess.run(['ping', '-n', '2', '-w', '500', ip], stdout=subprocess.PIPE)
        # 将输出标准流解码成中文
        output = res.stdout.decode('gbk')
        # ping通代码为0
        if res.returncode == 0:
            print('%-20s%-20s' % (ip, 'success'))
            # ping通结果写入文本
            with open('ping_success.txt', 'a+') as f:
                f.write('%-20s%-20s' % (ip, 'success'))
            # ping执行结果写入文本
            with open('ping_result.txt', 'a+') as f:
                f.write(output)
                f.write('-'*50)
        # ping不通代码为1
        else:
            print('%-20s%-20s' % (ip, 'failure'))
            # ping不通结果写入文本
            with open('ping_failure.txt', 'a+') as f:
                f.write('%-20s%-20s' % (ip, 'success'))
            # ping执行结果写入文本
            with open('ping_result.txt', 'a+') as f:
                f.write(output)
                f.write('-' * 50)
    

    执行结果如下所示:

代码执行完成后自动创建了文件,这里就不把贴出来了,自行打开文本进行验证。

  • 如何并发执行ping呢?

    使用多进程multiprocessing的异步方式来看看并发的效果是怎么样的(多线程threading就不用了)。

    说明:multiprocessing,简单理解就是通过CPU多核进行并发处理,本示例只演示其功能。

    from multiprocessing.pool import ThreadPool
    from datetime import datetime
    import subprocess
    
    THREADING_NUM = 10
    pool = ThreadPool(THREADING_NUM)
    
    def do_ping(ip):
        res = subprocess.run(['ping', '-n', '2', '-w', '500', ip], stdout=subprocess.PIPE)
        output = res.stdout.decode('gbk')
        if res.returncode == 0:
            print('%-20s%-20s' % (ip, 'success'))
            with open('ping_success.txt', 'a+') as f:
                f.write('%-20s%-20s' % (ip, 'success'))
    
            with open('ping_result.txt', 'a+') as f:
                f.write(output)
                f.write('-' * 50)
        else:
            print('%-20s%-20s' % (ip, 'failure'))
            with open('ping_failure.txt', 'a+') as f:
                f.write('%-20s%-20s' % (ip, 'success'))
    
            with open('ping_result.txt', 'a+') as f:
                f.write(output)
                f.write('-' * 50)
                
    if __name__ == '__main__':
        start_time = datetime.now()
        ip_list = ['1.1.1.1', '1.1.1.2','1.1.1.3', '8.8.8.8', '114.114.114.114', '114.114.114.115']
        for ip in ip_list:
            pool.apply_async(do_ping, args=(ip,))
        pool.close()
        pool.join()
    
        end_time = datetime.now()
        print('All done.总花费时间{:0.2f}s.'.format((end_time - start_time).total_seconds()))
    

    执行结果如下所示:

    说明:具体效果,大家可通过大规模网段进行测试,方可看出其效果明显。

完整代码

  • 以下为本章更新的完整代码,大家可在此基础上进行拓展和优化:

    #!/usr/bin/env python3
    # -*- coding:UTF-8 -*-
    
    from multiprocessing import freeze_support
    from multiprocessing.pool import ThreadPool
    from datetime import datetime
    
    import subprocess
    import ipaddress
    import threading
    import logging
    import sys
    
    # ip网段文本路径(当前目录下)
    IP_INFO_PATH = 'ip_list.txt'
    
    # 线程数()
    THREADING_NUM = 10
    # 进程池
    pool = ThreadPool(THREADING_NUM)
    # 线程锁
    queueLock = threading.Lock()
    
    # 打印消息
    def show_info(msg):
        queueLock.acquire()
        print(msg)
        queueLock.release()
    
    # 读取文本,获取ip网段信息
    def get_ips_info():
        try:
            with open(IP_INFO_PATH, 'r') as f:
                for line in f.readlines():
                    # 去掉前后空白
                    line = line.strip()
                    # 忽略空格行,len=1
                    if (
                            len(line) == 1 or
                            line.startswith('#')
                    ):
                        continue
    
                    yield line
    
        except FileNotFoundError as e:
            show_info('Can not find "{}"'.format(IP_INFO_PATH))
        except IndexError as e:
            show_info('"{}" format error'.format(IP_INFO_PATH))
    
    def do_one_ping(target_ip):
        """
        单机ping测试
        """
        res = ''
        if sys.platform == 'linux':
            res = subprocess.run(['ping', '-c', '2', '-W', '1000', target_ip], stdout=subprocess.PIPE)
            res_out = str(res.stdout.decode('gbk'))
        if sys.platform == 'win32':
            res = subprocess.call(['ping', '-n', '2', '-w', '1000', target_ip ], stdout = subprocess.PIPE)
        else:
            show_info('不支持该平台系统,非常抱歉!')
        # print(f'res状态是: {res}')
        if res.returncode == 0:
            show_info('%-20s%-20s' % (target_ip, 'success'))
    
        else:
            show_info('%-20s%-20s' % (target_ip, 'failure'))
    
    def do_ping(target_ip):
        """
        批量ping测试
        """
        try:
            res , res_out  = '', ''
            # 判断系统平台,执行对应命令
            if sys.platform == 'linux':
                res = subprocess.run(['ping', '-c', '2', '-W', '1000', target_ip], stdout=subprocess.PIPE)
                res_out = str(res.stdout.decode('gbk'))
            # 判断系统平台,执行对应命令
            if sys.platform == 'win32':
                res = subprocess.run(['ping', '-n', '2', '-w', '1000', target_ip ], stdout = subprocess.PIPE)
                res_out = str(res.stdout.decode('gbk'))
            else:
                show_info('不支持该平台系统,非常抱歉!')
    
            # print(f'res状态是: {res.returncode}')
            if res.returncode == 0:
                show_info('%-20s%-20s' % (target_ip, 'success'))
                # ping成功
                with open('LOG' + '/' + 'success_ping_result_' + LogTime + '.txt', 'a+') as f:
                    f.write('%-20s%-20s' % (target_ip, 'success') + '\n')
                # ping成功结果记录
                with open('LOG' + '/' + 'record_ping_' + LogTime + '.txt', 'a+') as f:
                    f.write(res_out)
                    f.write('-' * 50)
            else:
                show_info('%-20s%-20s' % (target_ip, 'failure'))
                # ping失败
                with open('LOG' + '/' + 'failure_ping_result_' + LogTime + '.txt', 'a+') as f:
                    f.write('%-20s%-20s' % (target_ip, 'failure') + '\n')
                # ping失败结果记录
                with open('LOG' + '/' + 'record_ping_' + LogTime + '.txt', 'a+') as f:
                    f.write(res_out)
                    f.write('-' * 50)
    
        except Exception as e:
            show_info(e)
    
    def get_ip_list(ip):
        """
        获取ip列表
        """
        temp = ipaddress.ip_network(ip, False).hosts()
        ip_list = []
        for item in temp:
            ip_list.append(str(item))
        # print(ip_list)
        return ip_list
    
    
    if __name__ == '__main__':
        freeze_support()
        LogTime = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
        start_time = datetime.now()
    
        # 单主机ping
        # do_one_ping('114.114.114.114')
    
        # 批量执行ping
        ips_list = get_ips_info()
        for ips in ips_list:
            ip_list = get_ip_list(ips)
            for ip in ip_list:
                pool.apply_async(do_ping, args=(ip,))
        pool.close()
        pool.join()
    
        end_time = datetime.now()
        print('All done.总花费时间{:0.2f}s.'.format((end_time - start_time).total_seconds()))
    

okay, 本章就先介绍这么多了,相信大家有了一个清晰的思路了,再把各个模块ipaddresssubprocessmultiprocessing都熟悉一边,实操干起来,我相信大家能够胜任,并在此基础上更新迭代,写出更好、更优雅的代码,咋们下次见。

码字不易,如觉得本文章有用,请动动手指给个爱心、分享给有需要的朋友拉,多谢各位,晚安。


如果喜欢的我的文章,欢迎关注我的公号:点滴技术,扫码关注,不定期分享

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,914评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,935评论 2 383
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,531评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,309评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,381评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,730评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,882评论 3 404
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,643评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,095评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,448评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,566评论 1 339
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,253评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,829评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,715评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,945评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,248评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,440评论 2 348

推荐阅读更多精彩内容