Python 轻松扫描,快速检测:高效IP网段扫描工具全解析
相关资源文件已经打包成EXE文件,可双击直接运行程序,且文章末尾已附上相关源码,以供大家学习交流,博主主页还有更多Python相关程序案例,秉着开源精神的想法,望大家喜欢,点个关注不迷路!!!
1. 简介:
这款"IP网段扫描工具"是网络管理员和普通用户的必备神器,轻松帮助你检查每个网段下的IP是否在线,避免了手动逐一检查的繁琐。只需要输入网段,它便会自动生成所有IP,快速对每个IP进行Ping检测,实时展示结果并提供详细的在线/掉线统计。工具的并发扫描方式,确保了高效性,让扫描过程变得既快速又流畅。
不仅如此,它还为用户提供了丰富的功能,像是清晰的进度条、动态更新的表格、日志输出以及导出功能,都让整个扫描过程充满了互动感与可视化,数据一目了然。用户还可以随时中止扫描,避免浪费时间。
无论是日常网络维护、设备故障排查,还是批量IP监测,这款工具都能帮助你轻松搞定。简洁、实用、快速,让网络管理变得更智能,更高效。
功能模块介绍:
输入网段与IP扫描:
- 用户可以通过输入网段(如 192.168.1,192.168.2)来启动扫描。工具会自动生成每个网段下的所有IP,并开始对每个IP进行Ping操作,快速检测其在线状态。
并发Ping操作:
- 为了提高效率,工具在扫描时采用并发技术,最多同时检测20个IP,通过异步执行的方式提高扫描速度。
进度条:
- 用户可以清晰看到当前扫描的进度,实时更新的进度条让等待变得更加轻松,不再觉得枯燥。
表格展示:
- 扫描结果会自动填充到表格中,最多每行显示10个IP地址。在线IP用绿色标记,掉线IP用灰色标记,帮助用户一目了然地看到每个IP的状态。
日志输出与导出:
- 所有扫描的日志和结果会被记录到文本框中,并且可以选择将"在线IP"或"掉线IP"导出为文本文件,或者导出完整的日志记录,方便后续查看或备份。
停止扫描:
- 如果扫描过程中需要中断,用户可以随时点击"停止扫描"按钮,立即停止当前的Ping任务,并恢复按钮状态。
导出功能:
用户可以选择导出不同类型的数据(在线IP、掉线IP或日志记录),并将数据保存为文本文件,方便后期分析和记录。
2. 运行效果:
3.总 结:
这款"IP网段扫描工具"通过图形界面和异步技术,简化了IP扫描的操作流程,提升了扫描效率。它不仅能高效检测每个网段的IP状态,还能实时反馈扫描进度,并提供导出功能,方便用户进一步分析。无论是在家庭网络管理,还是公司网络安全维护中,都能作为得力助手。简单易用的界面和丰富的功能,让网络管理不再是繁琐的工作,反而带来了一些轻松的乐趣!
4. 相关源码:
python
import sys
import asyncio
import subprocess
from queue import Queue
from PyQt5.QtCore import QThread, pyqtSignal, QTimer
from PyQt5.QtGui import QColor, QFont
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QLineEdit, QTextEdit, QProgressBar, QTableWidget, QTableWidgetItem, QMessageBox, QFileDialog, QInputDialog
class PingThread(QThread):
# 定义信号
result_signal = pyqtSignal(str, str) # 传递IP和状态
progress_signal = pyqtSignal(int) # 用于更新进度条的信号
stop_signal = pyqtSignal() # 用于停止线程的信号
def __init__(self, ip_queue, total_ips, parent=None):
super().__init__(parent)
self.ip_queue = ip_queue
self.total_ips = total_ips
self.pinged_ips = 0 # 已ping的IP数量
self.stop_requested = False # 停止请求标志位
self.online_ips = [] # 在线IP列表
self.offline_ips = [] # 掉线IP列表
def run(self):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(self.ping_ips())
async def ping_ips(self):
tasks = []
while not self.ip_queue.empty() and not self.stop_requested:
ip = self.ip_queue.get()
task = asyncio.create_task(self.ping(ip))
tasks.append(task)
if len(tasks) >= 20: # 每次最多并发20个请求
await asyncio.gather(*tasks)
tasks.clear()
if tasks:
await asyncio.gather(*tasks)
async def ping(self, ip):
res = await asyncio.to_thread(self.ping_single, ip)
status = "在线" if res == 0 else "掉线"
self.result_signal.emit(ip, status) # 发送IP和状态信号
# 根据状态将IP添加到对应的列表
if status == "在线":
self.online_ips.append(ip)
else:
self.offline_ips.append(ip)
self.pinged_ips += 1
progress = int((self.pinged_ips / self.total_ips) * 100) # 计算当前进度
self.progress_signal.emit(progress) # 发送进度信号
def ping_single(self, ip):
"""使用subprocess调用ping命令"""
return subprocess.call(f'ping -n 1 -w 5 {ip}', stdout=subprocess.PIPE)
def stop(self):
self.stop_requested = True # 设置停止标志
class PingApp(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("IP网络扫描工具")
self.setGeometry(200, 200, 800, 600)
# 设置最小宽度为1064像素
self.setMinimumWidth(1064)
# 设置全局字体为Segoe UI
font = QFont("Segoe UI", 10)
self.setFont(font)
# UI控件
self.layout = QVBoxLayout()
self.status_label = QLabel("请输入待检测的网段列表 (例如:192.168.1, 192.168.2,支持多网段扫描,网段之间用英文','隔开)", self)
self.layout.addWidget(self.status_label)
self.input_field = QLineEdit(self)
self.layout.addWidget(self.input_field)
# 创建水平布局来放置开始、停止和导出按钮
self.button_layout = QHBoxLayout()
# 修改按钮背景色为淡蓝色
self.start_button = QPushButton("开始扫描", self)
self.start_button.setStyleSheet("background-color: lightblue;")
self.start_button.clicked.connect(self.start_ping)
self.button_layout.addWidget(self.start_button)
self.stop_button = QPushButton("停止扫描", self)
self.stop_button.setStyleSheet("background-color: lightblue;")
self.stop_button.clicked.connect(self.stop_ping)
self.stop_button.setEnabled(False) # 初始时不可用
self.button_layout.addWidget(self.stop_button)
self.export_button = QPushButton("导出数据", self)
self.export_button.setStyleSheet("background-color: lightblue;")
self.export_button.clicked.connect(self.export_data)
self.button_layout.addWidget(self.export_button)
# 将按钮布局添加到主布局中
self.layout.addLayout(self.button_layout)
# 添加文本输出区域
self.output_text = QTextEdit(self)
self.layout.addWidget(self.output_text)
# 设置最大高度来缩减文本显示区域的高度
self.output_text.setMaximumHeight(200) # 设置最大高度为200
# 添加进度条
self.progress_bar = QProgressBar(self)
self.progress_bar.setRange(0, 100)
self.layout.addWidget(self.progress_bar)
# 添加表格
self.table_widget = QTableWidget(self)
self.layout.addWidget(self.table_widget)
self.setLayout(self.layout)
def start_ping(self):
user_input = self.input_field.text().strip()
if not user_input:
self.output_text.append("输入无效,请输入网段列表!")
return
# 校验输入是否是有效的网段格式
if not self.is_valid_ip_prefix(user_input):
self.output_text.append("无效的网段格式,请确保输入的是有效网段")
return
self.output_text.append(f"开始检测:{user_input}")
self.start_button.setEnabled(False)
self.stop_button.setEnabled(True) # 启用停止按钮
ip_queue = Queue()
ip_prefixes = user_input.split(',') # 支持多个网段
total_ips = len(ip_prefixes) * 256 # 每个网段包含256个IP地址
# 遍历多个网段并生成IP地址
for prefix in ip_prefixes:
prefix = prefix.strip()
for i in range(256):
ip = f"{prefix}.{i}" # 生成每个网段对应的IP
ip_queue.put(ip)
# 动态设置表格的行数和列数
rows = (total_ips + 9) // 10 # 计算需要的行数,最多每行10个IP
self.table_widget.setRowCount(rows) # 设置表格的行数
self.table_widget.setColumnCount(10) # 每行10个IP
# 填充表格
ip_index = 0
for prefix in ip_prefixes: # 遍历每个网段
prefix = prefix.strip()
for i in range(256):
if ip_index < total_ips:
ip = f"{prefix}.{i}" # 生成完整的IP地址
row = ip_index // 10 # 计算行数
col = ip_index % 10 # 计算列数
self.table_widget.setItem(row, col, QTableWidgetItem(ip)) # 设置表格单元格的IP
ip_index += 1
# 启动ping线程
self.ping_thread = PingThread(ip_queue, total_ips)
self.ping_thread.result_signal.connect(self.update_result)
self.ping_thread.progress_signal.connect(self.update_progress) # 连接进度更新信号
self.ping_thread.finished.connect(self.on_ping_finished)
self.ping_thread.start()
def stop_ping(self):
if self.ping_thread.isRunning():
self.ping_thread.stop() # 请求停止线程
self.output_text.append("检测已停止")
self.start_button.setEnabled(True)
self.stop_button.setEnabled(False) # 停止后禁用停止按钮
def export_data(self):
# 使用 QInputDialog.getItem() 来显示选择框
export_type, ok = QInputDialog.getItem(self, "选择导出项", "选择导出数据项",
["在线IP", "掉线IP", "日志记录"], 0, False)
if ok:
if export_type == "在线IP":
self.export_file("在线IP.txt")
elif export_type == "掉线IP":
self.export_file("掉线IP.txt")
elif export_type == "日志记录":
self.export_log()
def export_file(self, file_name):
# 弹出文件保存对话框,选择保存路径
file_path, _ = QFileDialog.getSaveFileName(self, f"保存{file_name}", file_name, "文本文件 (*.txt)")
if file_path:
# 使用内存列表替代读取文件
ip_list = self.get_ip_list(file_name)
# 排序IP地址(从小到大)
ip_list.sort(key=self.ip_to_tuple)
# 写入排序后的文件,每个IP地址按行分开
with open(file_path, 'w') as f:
for ip in ip_list:
f.write(f"{ip}\n") # 每个IP地址后加上换行符
QMessageBox.information(self, "导出成功", f"{file_name} 已成功导出到 {file_path}")
def export_log(self):
# 弹出文件保存对话框,选择保存日志路径
file_path, _ = QFileDialog.getSaveFileName(self, "保存日志文件", "日志记录.txt", "文本文件 (*.txt)")
if file_path:
with open(file_path, 'w') as f:
f.write(self.output_text.toPlainText())
QMessageBox.information(self, "导出成功", f"日志记录已成功导出到 {file_path}")
def get_ip_list(self, file_name):
# 根据文件名从内存中获取相应的IP列表
ip_list = []
if file_name == "在线IP.txt":
ip_list = self.ping_thread.online_ips # 获取在线IP列表
elif file_name == "掉线IP.txt":
ip_list = self.ping_thread.offline_ips # 获取掉线IP列表
return ip_list
def ip_to_tuple(self, ip):
# 将IP地址转换为元组,用于排序
return tuple(map(int, ip.split('.')))
def is_valid_ip_prefix(self, ip_prefix):
# 简单的IP前缀校验(支持多个网段)
parts = ip_prefix.split(',')
if len(parts) < 1:
return False
for part in parts:
part = part.strip()
if not self.is_valid_single_ip_prefix(part):
return False
return True
def is_valid_single_ip_prefix(self, ip_prefix):
# 校验单一网段是否有效(例如:192.168.1)
parts = ip_prefix.split('.')
if len(parts) != 3:
return False
for part in parts:
if not part.isdigit() or not 0 <= int(part) <= 255:
return False
return True # 必须输入三个部分
def update_result(self, ip, status):
# 更新表格单元格的背景色
for row in range(self.table_widget.rowCount()):
for col in range(self.table_widget.columnCount()):
item = self.table_widget.item(row, col)
if item and item.text() == ip:
if status == "在线":
item.setBackground(QColor('green')) # 使用QColor类
else:
item.setBackground(QColor('gray')) # 使用QColor类
# 在文本输出区域显示每个IP的状态
self.output_text.append(f"{ip} - {status}")
def update_progress(self, progress):
self.progress_bar.setValue(progress) # 更新进度条的值
def on_ping_finished(self):
# 计算在线和掉线主机数量
online_count = len(self.ping_thread.online_ips)
offline_count = len(self.ping_thread.offline_ips)
# 显示检测完成信息和统计
self.output_text.append(f"扫描完成!")
self.output_text.append(f"在线主机数: {online_count}")
self.output_text.append(f"掉线主机数: {offline_count}")
self.start_button.setEnabled(True)
self.stop_button.setEnabled(False) # 禁用停止按钮
# 弹出完成提示框
QMessageBox.information(self, "提示", "扫描已完成!")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = PingApp()
window.show()
sys.exit(app.exec_())