Python中从本地网络获取IP地址/主机名列表

49

如何在Python中轻松获取本地网络中IP地址或主机名的列表?

最好是跨平台的,但首先需要在Mac OS X上工作,然后再考虑其他平台。

编辑:我所指的本地网络是指所有活动的地址,例如192.168.xxx.xxx

因此,如果我的计算机在本地网络中的IP地址是192.168.1.1,并且我还连接了另外三台计算机,我希望它返回IP地址192.168.1.2192.168.1.3192.168.1.4,以及它们可能的主机名。

11个回答

25
如果你所说的“本地”是指在同一个网络段内,那么你需要执行以下步骤:
  1. 确定你自己的 IP 地址。
  2. 确定你自己的子网掩码。
  3. 确定网络范围。
  4. 扫描所有地址(除了最低的网络地址和最高的广播地址)。
  5. 使用你的 DNS 反向解析来确定响应你扫描的 IP 地址的主机名。
或者你可以让 Python 在外部执行 nmap 并将结果传回你的程序。

arp -a??? # 额外的空格以便识别注释 - Vishnoo Rath

24

13

如果您知道您的计算机名称,您可以使用:

import socket
IP1 = socket.gethostbyname(socket.gethostname()) # local IP adress of your computer
IP2 = socket.gethostbyname('name_of_your_computer') # IP adress of remote computer
否则,您将不得不扫描所有遵循与本地计算机(IP1)相同掩码的IP地址,如另一个回答所述。

如何获取我的计算机名称 - Irfan Ghaffar7
socket.gethostname() 返回计算机的名称。 - Mapad

9

我从其他帖子中收集了以下功能,并且在Ubuntu中对我有效。

import os
import socket    
import multiprocessing
import subprocess


def pinger(job_q, results_q):
    """
    Do Ping
    :param job_q:
    :param results_q:
    :return:
    """
    DEVNULL = open(os.devnull, 'w')
    while True:

        ip = job_q.get()

        if ip is None:
            break

        try:
            subprocess.check_call(['ping', '-c1', ip],
                                  stdout=DEVNULL)
            results_q.put(ip)
        except:
            pass


def get_my_ip():
    """
    Find my IP address
    :return:
    """
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect(("8.8.8.8", 80))
    ip = s.getsockname()[0]
    s.close()
    return ip


def map_network(pool_size=255):
    """
    Maps the network
    :param pool_size: amount of parallel ping processes
    :return: list of valid ip addresses
    """
    
    ip_list = list()
    
    # get my IP and compose a base like 192.168.1.xxx
    ip_parts = get_my_ip().split('.')
    base_ip = ip_parts[0] + '.' + ip_parts[1] + '.' + ip_parts[2] + '.'
    
    # prepare the jobs queue
    jobs = multiprocessing.Queue()
    results = multiprocessing.Queue()
    
    pool = [multiprocessing.Process(target=pinger, args=(jobs, results)) for i in range(pool_size)]
    
    for p in pool:
        p.start()
    
    # cue hte ping processes
    for i in range(1, 255):
        jobs.put(base_ip + '{0}'.format(i))
    
    for p in pool:
        jobs.put(None)
    
    for p in pool:
        p.join()
    
    # collect he results
    while not results.empty():
        ip = results.get()
        ip_list.append(ip)

    return ip_list


if __name__ == '__main__':

    print('Mapping...')
    lst = map_network()
    print(lst)

pinger() 函数中,DEVNULL = open(os.devnull, 'w') 这一行的作用是什么呢?并且,难道你不应该在函数结束时关闭 os.devnull 以防止内存泄漏吗? - ds_secret
1
devnull是控制台,用于重定向标准输出并像文件一样打印到它。 - Santi Peñate-Vera
映射... [] #这是我得到的输出。我只得到了一个方框。有什么原因吗? - Redgar Tech
1
你在用Linux吗?上次我检查时,在Windows上这个功能没有按预期工作。 - Santi Peñate-Vera
这假设网络是一个/24子网掩码。 - joeyagreco

9

对于OSX(和Linux),一个简单的解决方案是使用os.popen或os.system并运行arp -a命令。

例如:

import os
devices = []
for device in os.popen('arp -a'): devices.append(device)

这将为您提供本地网络上设备的列表。

这个脚本只能捕获有线设备,无法捕获平板电脑、笔记本电脑和手机等无线设备。是否有其他解决方案可以同时捕获这些设备? - user12217470
2
这似乎正在检测我网络中的有线和无线设备 @tdelozie - joeyagreco

5
我发现了这篇用 Python 实现的网络扫描器文章,并写了这个简短的代码。它可以实现你想要的功能!但你需要知道你设备的可访问端口。22 端口是 ssh 的标准端口,我正在使用它。我想你可以遍历所有端口。一些默认的端口是:
linux: [20, 21, 22, 23, 25, 80, 111, 443, 445, 631, 993, 995]
windows: [135, 137, 138, 139, 445]
mac: [22, 445, 548, 631]

import socket

def connect(hostname, port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    socket.setdefaulttimeout(1)
    result = sock.connect_ex((hostname, port))
    sock.close()
    return result == 0

for i in range(0,255):
    res = connect("192.168.1."+str(i), 22)
    if res:
        print("Device found at: ", "192.168.1."+str(i) + ":"+str(22))

编辑 by TheLizzard:

使用上述代码,并添加线程:

from threading import Thread, Lock
from time import perf_counter
from sys import stderr
from time import sleep
import socket


# I changed this from "192.168.1.%i" to "192.168.0.%i"
BASE_IP = "192.168.0.%i"
PORT = 80


class Threader:
    """
    This is a class that calls a list of functions in a limited number of
    threads. It uses locks to make sure the data is thread safe.
    Usage:
        from time import sleep

        def function(i):
            sleep(2)
            with threader.print_lock:
                print(i)

        threader = Threader(10) # The maximum number of threads = 10
        for i in range(20):
            threader.append(function, i)
        threader.start()
        threader.join()

    This class also provides a lock called: `<Threader>.print_lock`
    """
    def __init__(self, threads=30):
        self.thread_lock = Lock()
        self.functions_lock = Lock()
        self.functions = []
        self.threads = []
        self.nthreads = threads
        self.running = True
        self.print_lock = Lock()

    def stop(self) -> None:
        # Signal all worker threads to stop
        self.running = False

    def append(self, function, *args) -> None:
        # Add the function to a list of functions to be run
        self.functions.append((function, args))

    def start(self) -> None:
        # Create a limited number of threads
        for i in range(self.nthreads):
            thread = Thread(target=self.worker, daemon=True)
            # We need to pass in `thread` as a parameter so we
            # have to use `<threading.Thread>._args` like this:
            thread._args = (thread, )
            self.threads.append(thread)
            thread.start()

    def join(self) -> None:
        # Joins the threads one by one until all of them are done.
        for thread in self.threads:
            thread.join()

    def worker(self, thread:Thread) -> None:
        # While we are running and there are functions to call:
        while self.running and (len(self.functions) > 0):
            # Get a function
            with self.functions_lock:
                function, args = self.functions.pop(0)
            # Call that function
            function(*args)

        # Remove the thread from the list of threads.
        # This may cause issues if the user calls `<Threader>.join()`
        # But I haven't seen this problem while testing/using it.
        with self.thread_lock:
            self.threads.remove(thread)


start = perf_counter()
# I didn't need a timeout of 1 so I used 0.1
socket.setdefaulttimeout(0.1)

def connect(hostname, port):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        result = sock.connect_ex((hostname, port))
    with threader.print_lock:
        if result == 0:
            stderr.write(f"[{perf_counter() - start:.5f}] Found {hostname}\n")

threader = Threader(10)
for i in range(255):
    threader.append(connect, BASE_IP%i, PORT)
threader.start()
threader.join()
print(f"[{perf_counter() - start:.5f}] Done searching")
input("Press enter to exit.\n? ")

1
超级慢,我认为这不是一个可行的解决方案。 - Jamal Alkelani
2
@dlammy 我知道我有点晚了,但我已经添加了线程。现在它可以在不到3秒的时间内扫描所有256个可能的IP地址。 - TheLizzard

3

尝试:

import socket

print ([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")][:1])

1
你能解释一下 socket.gethostbyname_ex(socket.gethostname()) 中索引为1的数组吗?我无法弄清楚为什么它是空的。 - Param Siddharth

0

我刚刚遇到了这个问题。我是这样解决的:

import kthread #pip install kthread
from time import sleep
import subprocess

def getips():
    ipadressen = {}
    def ping(ipadresse):
        try:
            outputcap = subprocess.run([f'ping', ipadresse, '-n', '1'], capture_output=True) #sends only one package, faster
            ipadressen[ipadresse] = outputcap
        except Exception as Fehler:
            print(Fehler)
    t = [kthread.KThread(target = ping, name = f"ipgetter{ipend}", args=(f'192.168.0.{ipend}',)) for ipend in range(255)] #prepares threads
    [kk.start() for kk in t] #starts 255 threads
    while len(ipadressen) < 255:
        print('Searching network')
        sleep(.3)
    alldevices = []
    for key, item in ipadressen.items():
        if not 'unreachable' in item.stdout.decode('utf-8') and 'failure' not in item.stdout.decode('utf-8'): #checks if there wasn't neither general failure nor 'unrechable host'
            alldevices.append(key)
    return alldevices

allips = getips() #takes 1.5 seconds on my pc

0
我已经编写了以下代码来获取已知MAC设备的IP地址。可以根据需要进行修改以通过一些字符串操作获得所有IP地址。希望这能对您有所帮助。
#running windows cmd line  statement and put output into a string
cmd_out = os.popen("arp -a").read()
line_arr = cmd_out.split('\n')
line_count = len(line_arr)


#search in all lines for ip
for i in range(0, line_count):
    y = line_arr[i]
    z = y.find(mac_address)

    #if mac address is found then get the ip using regex matching
    if z > 0:
        ip_out= re.search('[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+', y, re.M | re.I)

-1

这个问题的答案之一可能会对你有所帮助。似乎有一个适用于Python的跨平台版本,但我还没有尝试过。


不,我不想要我的 IP 地址,我想要其他人的。就像 Steve Moyer 所说的那样,但是加上代码 :) - Josh Hunt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接