List of IP addresses/hostnames from local network in Python

How can I get a list of the IP addresses or host names from a local network easily in Python?

It would be best if it was multi-platform, but it needs to work on Mac OS X first, then others follow.

Edit: By local I mean all active addresses within a local network, such as 192.168.xxx.xxx.

So, if the IP address of my computer (within the local network) is 192.168.1.1, and I have three other connected computers, I would want it to return the IP addresses 192.168.1.2, 192.168.1.3, 192.168.1.4, and possibly their hostnames.

If by “local” you mean on the same network segment, then you have to perform the following steps:

  1. Determine your own IP address
  2. Determine your own netmask
  3. Determine the network range
  4. Scan all the addresses (except the lowest, which is your network address and the highest, which is your broadcast address).
  5. Use your DNS’s reverse lookup to determine the hostname for IP addresses which respond to your scan.

Or you can just let Python execute nmap externally and pipe the results back into your program.

Update: The script is now located on github.

I wrote a small python script, that leverages scapy‘s arping().

If you know the names of your computers you can use:

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

Otherwise you will have to scan for all the IP addresses that follow the same mask as your local computer (IP1), as stated in another answer.

I have collected the following functionality from some other threads and it works for me in Ubuntu.

import os
import socket    
import multiprocessing
import subprocess
import os


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)

For OSX (and Linux), a simple solution is to use either os.popen or os.system and run the arp -a command.

For example:

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

This will give you a list of the devices on your local network.

Try:

import socket

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

I found this network scanner in python article and wrote this short code. It does what you want! You do however need to know accessible ports for your devices. Port 22 is ssh standard and what I am using. I suppose you could loop over all ports. Some defaults are:

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

EDIT by TheLizzard:

Using the code above and adding threading:

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? ")

I have done following code to get the IP of MAC known device. This can be modified accordingly to obtain all IPs with some string manipulation. Hope this will help you.

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

One of the answers in this question might help you. There seems to be a platform agnostic version for python, but I haven’t tried it yet.

Here is a small tool scanip that will help you to get all ip addresses and their corresponding mac addresses in the network (Works on Linux).

https://github.com/vivkv/scanip

Leave a Comment