【问题标题】:Ping list of computers FAST with Python使用 Python 快速 Ping 计算机列表
【发布时间】:2019-09-23 18:31:53
【问题描述】:

我已经多次 ping 了一个计算机列表,但是我第一次尝试使用 Python,其中包含大约 4,000 个计算机名称的列表,而且我的脚本非常慢。我将如何加快速度并将输出写入逗号分隔的文本文件?

import pandas as pd
import os
import sys
import subprocess
import datetime

#Get current date and time
now = datetime.datetime.now()
dt = now.strftime("%Y-%m-%d")
dtnow = now.strftime("%Y-%m-%d %H:%M")

#Open the file and read into memory
fh = pd.read_csv('list.csv')

#Fix column headers by replacing the spaces with a underscore
fh.columns = fh.columns.str.strip().str.replace(' ', '_')

#Read the computer names into a variable called "computers"
computers = fh.Machine_Name
#Debug - Uncomment line below to see a list of computer names from csv file
#print(computers)

def ping(comp):
    args = ["ping", "-n", "2", comp]
    p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    output, error = p.communicate()
    if 'bytes=32' in output:
        writetofile(comp, ',online')
    else:
        writetofile(comp, ',offline')
    #endIf
#endDef

def writetofile(compname, data):
    with open('DLPProv_' + dt + '.txt', 'a') as f:
        f.write(compname + data + '\n')
    #endWith
#endDef

for i in computers:
    ping(i)
#endFor

f.write('END: ' + dtnow)
f.close()

我尝试使用@rolandsmith 发布的代码,但出现错误:


import concurrent.futures as cf
import os
import pandas as pd
from pythonping import ping

#Open the file and read into memory
fh = pd.read_csv('list.csv')

#Fix column headers by replacing the spaces with a underscore
fh.columns = fh.columns.str.strip().str.replace(' ', '_')

#Read the computer names into a variable called "computers"
computers = fh.Machine_Name

def pingworker(address):
    rv = ping(address, count=4)
    if rv.success():
        return address, True
    return address,False

with cf.ThreadPoolExecutor() as tp:
    res = tp.map(pingworker, computers)

【问题讨论】:

  • 我认为ping 函数需要更多时间。是否可以在打印前后添加调试语句来计算所需时间?
  • 使用concurrent.futures.ThreadPoolExecutormapmultiprocessing.dummy.Poolimap/imap_unordered来获得基于线程的并行性?这是此类延迟受限问题的常用解决方案。

标签: python python-3.x ping


【解决方案1】:

当您认为脚本运行缓慢时,您应该测量导致它运行缓慢的原因。使用例如line-profiler.

我的猜测会是在这种情况下它是占用大部分时间的子流程。理想情况下,您希望消除为每个 ping 启动进程的开销。所以不要调用ping 程序,而是安装pythonping 模块。这允许您从 Python 执行 ICMP 回显请求。请注意,这使用原始套接字,因此根据操作系统,您可能需要以 root 身份运行脚本或使其能够使用原始套接字。 使用这个模块消除了使用子进程的开销。

接下来,当您的脚本执行此操作时,它主要是在等待来自网络的回复。所以我们使用concurrent.futures.ThreadPoolExecutor 来并行启动多个ping

import concurrent.futures as cf
import os
from pythonping import ping

def pingworker(address):
    rv = ping(address)
    if rv.success():
        return address, True
    return address,False

with cf.ThreadPoolExecutor() as tp:
    res = tp.map(pingworker, list_of_addresses)

在此之后,res 是一个 2 元组列表,每个元组都包含地址和布尔值(如果失败或成功)。

请注意,从 Python 3.5 开始,ThreadPoolExecutor 会启动 5*N 个线程,其中 N 是您机器上的内核数。所以对于四核机器,一次会运行 20 个 ping 调用。您可以在创建 ThreadPoolExecutor 时尝试使用 max_workers 参数,但在某个时候,您的网络连接会因 ping 调用而饱和。

编辑

pythonping.ping 函数需要IP 地址,而不是名称。因此,您必须先进行名称查找。幸运的是,这是内置在 socket 模块中的。您可以使用例如socket.gethostbyname_ex 进行 IPv4 地址查找。或socket.getaddrinfo 获取 IPv4 和 IPv6 地址。

如果您有一个名称列表,假设您使用的是 IPv4,您可以像这样更改工作人员:

import concurrent.futures as cf
import socket
import os
from pythonping import ping

def pingworker(name):
    """
    Ping a hostname.

    Arguments:
        name (str): hostname:

    Returns:
        a 3-tuple (hostname, IP-address, ping-result)
        where hostname and IP-address are strings and
        ping-result is a bool.
    """
    try:
        _, _, IPs = socket.gethostbyname_ex(name)
        address = IPs[0]
    except socket.gaierror:
        return name, None, False  # Name lookup failed.
    rv = ping(address)
    if rv.success():
        return name, address, True
    return name, address, False   # Host doesn't respond.

with cf.ThreadPoolExecutor() as tp:
    res = tp.map(pingworker, list_of_names)

我还修改了工作函数以返回 IP 地址。这样您就可以将不返回 ping 的主机与名称无法解析的主机区分开来。

【讨论】:

  • 需要原始套接字?那是……次优的。需要 admin/root 才能运行的第三方软件包是巨大的安全漏洞;如果所有者的信誉受到损害(或所有者决定自己是恶意的),则可以悄悄地更新软件包以劫持机器。显然ping 本身在没有管理员/root 的情况下运行,所以我有点怀疑包中是否需要它。
  • 它实际上不需要root权限,只需要root可以在文件上设置的cap_net_raw能力。这就是 ping 在我的盒子上的作用。
  • @ShadowRanger 如果这是一个 server 应用程序,我会同意。既然不是,危险就小了很多。其次,您的ping 二进制 运行setuid root。
  • @randomusername 我怀疑 cap_net_raw 是特定于 linux 的。并且 OP 没有提到他使用的是哪个操作系统。
  • @RolandSmith 感谢您的详细回复。我不确定我添加的计算机名称是否正确,因为我在运行脚本时遇到了很多错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-06-14
  • 2011-11-15
  • 1970-01-01
  • 2021-11-17
  • 2013-08-02
  • 1970-01-01
  • 2016-05-19
相关资源
最近更新 更多