import os
import subprocess
import socket
from scapy.all import srp, Ether, ARP, conf
import threading

mac_name_mapping = {
    "00:e0:4c:29:e2:5b": "我的台式机711",
    "08:5c:1b:7c:5b:fe": "cmcc.wifi",
    "14:49:d4:af:93:3f": "小米13",
    "6e:3e:07:54:b5:b3": "红米note11",
    "48:2c:a0:49:86:29": "mi8 se",
    "90:78:b2:50:fa:46": "红米k20",
    "9c:9c:1f:40:d5:ed":"充电插座",
    "3c:61:05:d4:5c:15": "院子灯"
    # 继续添加其他设备的 MAC 地址和主机名
}

def ping_device(ip):
    """
    Ping 设备,检查是否在线
    """
    try:
        # print(ip)
        output = subprocess.check_output(["ping", "-c", "1", ip], universal_newlines=True)
        # print(output)
        if "TTL" in output:
            return True
        return False
    except subprocess.CalledProcessError:
        return False

def get_mac_address(ip):
    """
    使用 ARP 获取 MAC 地址
    """
    conf.verb = 0  # 禁用冗长的输出
    ans, _ = srp(Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst=ip), timeout=2, retry=2)
    for _, rcv in ans:
        return rcv[Ether].src
    return None

def get_device_name(ip):
    """
    通过 IP 地址获取设备名
    """
    try:
        device_name = socket.gethostbyaddr(ip)[0]
    except socket.herror:
        device_name = None
    return device_name

def scan_ip(ip, results):
    """
    扫描单个 IP,获取设备的 IP、MAC 地址和名称
    """
    if ping_device(ip):
        mac_address = get_mac_address(ip)
        device_name = get_device_name(ip)
        if not device_name:
            if mac_address in mac_name_mapping:
                device_name = mac_name_mapping[mac_address]

        results.append({"ip": ip, "mac": mac_address, "name": device_name})

def scan_network(network_range):
    """
    使用多线程扫描指定网段
    """
    threads = []
    results = []
    
    for i in range(1, 255):
        ip = f"{network_range}.{i}"
        thread = threading.Thread(target=scan_ip, args=(ip, results))
        threads.append(thread)
        thread.start()

    # 等待所有线程完成
    for thread in threads:
        thread.join()

    return results

if __name__ == "__main__":
    network_range = "192.168.31"  # 根据实际情况调整
    devices = scan_network(network_range)
    
    for device in devices:
        print(f"IP: {device['ip']}, MAC: {device['mac']}, Name: {device['name']}")

 

 

大纲

  1. 引言

    • 介绍局域网设备扫描的用途(例如,网络管理、设备监控)。
    • 提及 Python 的强大功能和可扩展性。
  2. 环境准备

    • 确保安装 Python 和相关库(如 scapy)。
    • 提及必要的系统权限(如管理员权限)以便执行网络扫描。
  3. 代码分析

    • 逐段分析代码,解释其功能和实现逻辑。

代码详细分析

import os
import subprocess
import socket
from scapy.all import srp, Ether, ARP, conf
import threading
  • 导入必要的库
    • os 和 subprocess 用于执行系统命令(如 ping)。
    • socket 用于进行网络编程,获取设备名称。
    • scapy 是一个强大的网络包处理库,用于发送和接收网络数据包。
    • threading 用于实现多线程扫描。

设备 MAC 地址到主机名的映射

mac_name_mapping = {
    "00:e0:4c:29:e2:5b": "我的台式机711",
    "08:5c:1b:7c:5b:fe": "cmcc.wifi",
    "14:49:d4:af:93:3f": "小米13",
    "6e:3e:07:54:b5:b3": "红米note11",
    "48:2c:a0:49:86:29": "mi8 se",
    "90:78:b2:50:fa:46": "红米k20",
    "9c:9c:1f:40:d5:ed":"充电插座"
}
  • 映射字典:维护一个 MAC 地址和主机名的映射字典。这样可以在 DNS 查询失败时提供更直观的设备名称。

设备在线检查

def ping_device(ip):
    """
    Ping 设备,检查是否在线
    """
    try:
        output = subprocess.check_output(["ping", "-c", "1", ip], universal_newlines=True)
        if "TTL" in output:
            return True
        return False
    except subprocess.CalledProcessError:
        return False
  • 功能:通过 ping 命令检查设备是否在线。如果设备响应,则返回 True,否则返回 False
  • 改进点:可以根据需求调整 ping 的参数,例如增加超时时间。

获取 MAC 地址

def get_mac_address(ip):
    """
    使用 ARP 获取 MAC 地址
    """
    conf.verb = 0  # 禁用冗长的输出
    ans, _ = srp(Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst=ip), timeout=2, retry=2)
    for _, rcv in ans:
        return rcv[Ether].src
    return None
  • 功能:使用 ARP 请求获取给定 IP 地址的 MAC 地址。该函数通过广播 ARP 请求到网络中,然后解析响应以提取 MAC 地址。

获取设备名称

def get_device_name(ip):
    """
    通过 IP 地址获取设备名
    """
    try:
        device_name = socket.gethostbyaddr(ip)[0]
    except socket.herror:
        device_name = None
    return device_name
  • 功能:通过反向 DNS 查找获取设备名称。如果查找失败,则返回 None

扫描单个 IP 地址

def scan_ip(ip, results):
    """
    扫描单个 IP,获取设备的 IP、MAC 地址和名称
    """
    if ping_device(ip):
        mac_address = get_mac_address(ip)
        device_name = get_device_name(ip)
        if not device_name:
            if mac_address in mac_name_mapping:
                device_name = mac_name_mapping[mac_address]

        results.append({"ip": ip, "mac": mac_address, "name": device_name})
  • 功能:对于每个 IP 地址,首先检查其是否在线。如果在线,则获取其 MAC 地址和设备名称。如果设备名称为 None,则从映射字典中查找。

多线程扫描网络

def scan_network(network_range):
    """
    使用多线程扫描指定网段
    """
    threads = []
    results = []
    
    for i in range(1, 255):
        ip = f"{network_range}.{i}"
        thread = threading.Thread(target=scan_ip, args=(ip, results))
        threads.append(thread)
        thread.start()

    # 等待所有线程完成
    for thread in threads:
        thread.join()

    return results
  • 功能:使用多线程扫描整个子网,从 192.168.31.1 到 192.168.31.254。这显著提高了扫描速度。

主程序

if __name__ == "__main__":
    network_range = "192.168.31"  # 根据实际情况调整
    devices = scan_network(network_range)
    
    for device in devices:
        print(f"IP: {device['ip']}, MAC: {device['mac']}, Name: {device['name']}")
  • 功能:定义要扫描的网络范围,调用扫描函数并输出结果。

结论

  • 该 Python 脚本可以有效地扫描局域网内的设备,获取其 IP、MAC 地址及设备名称。
  • 通过维护 MAC 地址与设备名称的映射,可以更方便地识别设备。

文章结尾

  • 讨论脚本的可扩展性和潜在的改进(如添加更多的设备识别功能、使用更高级的库等)。
  • 鼓励读者尝试自己修改和扩展代码,适应不同的网络环境。

通过这种结构和内容,你可以撰写一篇详尽的文章,帮助他人理解和使用这个 Python 网络扫描脚本。