script/Ping多多/MyPing网段.py

192 lines
6.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import subprocess
import platform
import ipaddress
from concurrent.futures import ThreadPoolExecutor
from tabulate import tabulate
import colorama
from colorama import Fore, Style
# 初始化颜色设置
colorama.init(autoreset=True)
def ping_ip(ip):
"""Ping单个IP地址返回是否可达"""
param = '-n' if platform.system().lower() == 'windows' else '-c'
timeout = '-w' if platform.system().lower() == 'windows' else '-W'
command = ['ping', param, '1', timeout, '500', str(ip)]
try:
response = subprocess.run(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=1)
return ip, response.returncode == 0
except subprocess.TimeoutExpired:
return ip, False
def parse_ip_range(ip_range):
"""解析IP范围字符串如10.10.0.1 或 10.10.0.1-10.10.0.255"""
# 如果只输入了一个IP
if '-' not in ip_range:
# 提取网络前缀
ip_parts = ip_range.split('.')
if len(ip_parts) != 4:
raise ValueError("请输入有效的IPv4地址")
# 构建C类网段范围
start_ip = ip_range
end_ip = f"{ip_parts[0]}.{ip_parts[1]}.{ip_parts[2]}.255"
print(f"自动扩展扫描范围: {start_ip}{end_ip}")
else:
start_ip, end_ip = ip_range.split('-')
start = ipaddress.ip_address(start_ip)
end = ipaddress.ip_address(end_ip)
# 提取网络前缀
prefix = '.'.join(str(start).split('.')[:-1]) + '.'
ip_list = []
current = start
while current <= end:
ip_list.append(current)
current += 1
return ip_list, prefix
def ip_to_int(ip):
"""将IP地址转换为整数用于排序"""
return int(ipaddress.ip_address(ip))
def scan_network(ip_range):
"""扫描IP范围并返回结果"""
try:
ip_list, prefix = parse_ip_range(ip_range)
except Exception as e:
print(f"{Fore.RED}错误: {e}{Style.RESET_ALL}")
return None, None
results = []
print(f"扫描中: {len(ip_list)} 个IP地址...")
with ThreadPoolExecutor(max_workers=100) as executor:
futures = [executor.submit(ping_ip, ip) for ip in ip_list]
for i, future in enumerate(futures):
ip, status = future.result()
results.append((str(ip), status))
# 显示进度
if (i + 1) % 10 == 0 or (i + 1) == len(ip_list):
online_count = sum(1 for _, status in results if status)
offline_count = sum(1 for _, status in results if not status)
# 使用多行字符串避免语法错误
progress_msg = (
f"\r进度: {i + 1}/{len(ip_list)} | "
f"{Fore.GREEN}在线: {online_count}{Style.RESET_ALL} | "
f"{Fore.RED}离线: {offline_count}{Style.RESET_ALL}"
)
print(progress_msg, end='')
print("\n扫描完成!")
return results, prefix
def display_results(results, prefix):
"""以紧凑格式显示结果每行20个IP"""
# 按IP地址排序
sorted_results = sorted(results, key=lambda x: ip_to_int(x[0]))
# 提取IP最后一段和状态
compact_results = []
for ip, status in sorted_results:
host_part = ip.split('.')[-1]
compact_results.append((host_part, status))
# 计算统计信息
online_count = sum(1 for _, status in results if status)
offline_count = sum(1 for _, status in results if not status)
total_count = len(results)
# 显示结果
print("\n" + "=" * 80)
summary = (
f"扫描结果摘要: {prefix}0/24 | 总计 {total_count} 个IP | "
f"{Fore.GREEN}在线: {online_count}{Style.RESET_ALL} | "
f"{Fore.RED}离线: {offline_count}{Style.RESET_ALL}"
)
print(summary)
print("=" * 80 + "\n")
# 准备表格数据 - 每行20个IP
table_data = []
# 将数据分组每组20个IP
for i in range(0, len(compact_results), 20):
row = compact_results[i:i + 20]
table_row = []
for host, status in row:
# 使用ASCII字符替代Unicode图标
if status:
display_text = f"{Fore.GREEN}{host}*{Style.RESET_ALL}" # 使用星号*表示在线
else:
display_text = f"{Fore.RED}{host}x{Style.RESET_ALL}" # 使用x表示离线
table_row.append(display_text)
# 填充不足20个的行
while len(table_row) < 20:
table_row.append("")
table_data.append(table_row)
# 输出表格(不显示列号标题)
print(tabulate(table_data, tablefmt="simple_grid", stralign="center"))
# 添加图例说明
print(f"\n图例: {Fore.GREEN}* 在线{Style.RESET_ALL} | {Fore.RED}x 离线{Style.RESET_ALL}")
print(f"注: 数字表示IP地址最后一段例如 '1*' 表示 {prefix}1 在线")
def show_menu():
"""显示菜单选项"""
print("\n" + "=" * 60)
print(f"{Fore.CYAN}IP网段扫描工具{Style.RESET_ALL}")
print("=" * 60)
print("功能说明:")
print(f" - 输入单个IP (如 {Fore.YELLOW}10.10.0.1{Style.RESET_ALL}) 将自动扫描整个网段")
print(f" - 输入IP范围 (如 {Fore.YELLOW}10.10.0.1-10.10.0.100{Style.RESET_ALL}) 扫描指定范围")
print(f" - 输入 {Fore.RED}0{Style.RESET_ALL} 退出程序")
print("=" * 60)
def main():
while True:
show_menu()
ip_range = input("\n请输入要扫描的IP地址或范围: ").strip()
# 退出条件
if ip_range == '0':
print(f"{Fore.YELLOW}感谢使用,程序已退出!{Style.RESET_ALL}")
break
# 验证输入是否为空
if not ip_range:
print(f"{Fore.RED}错误: 输入不能为空{Style.RESET_ALL}")
continue
# 执行扫描
results, prefix = scan_network(ip_range)
# 显示结果(如果扫描成功)
if results and prefix:
display_results(results, prefix)
if __name__ == "__main__":
main()