美文网首页pythonPython
【干货】用Python教你写一个批量ping

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

作者: 点滴技术 | 来源:发表于2021-03-20 12:47 被阅读0次

    用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都熟悉一边,实操干起来,我相信大家能够胜任,并在此基础上更新迭代,写出更好、更优雅的代码,咋们下次见。

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


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

    相关文章

      网友评论

        本文标题:【干货】用Python教你写一个批量ping

        本文链接:https://www.haomeiwen.com/subject/duqwcltx.html