import os
import shutil
import mimetypes
from pathlib import Path
from datetime import datetime
import hashlib
class FileOrganizer:
def __init__(self, source_dir, target_dir):
self.source_dir = Path(source_dir)
self.target_dir = Path(target_dir)
self.file_types = {
'images': ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.svg', '.webp'],
'documents': ['.pdf', '.doc', '.docx', '.txt', '.rtf', '.odt', '.md', '.tex'],
'videos': ['.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.webm'],
'audio': ['.mp3', '.wav', '.flac', '.aac', '.ogg', '.wma'],
'archives': ['.zip', '.rar', '.7z', '.tar', '.gz', '.bz2'],
'executables': ['.exe', '.msi', '.bat', '.sh', '.apk'],
'code': ['.py', '.js', '.html', '.css', '.java', '.cpp', '.c', '.php', '.rb', '.go'],
'spreadsheets': ['.xlsx', '.xls', '.csv', '.ods'],
'presentations': ['.pptx', '.ppt', '.odp']
}
def get_file_type(self, file_path):
"""根据文件扩展名确定文件类型"""
ext = file_path.suffix.lower()
for category, extensions in self.file_types.items():
if ext in extensions:
return category
return 'others'
def organize_by_extension(self):
"""按文件扩展名分类整理"""
organized_count = 0
for file_path in self.source_dir.rglob('*'):
if file_path.is_file():
file_type = self.get_file_type(file_path)
target_folder = self.target_dir / file_type
target_folder.mkdir(exist_ok=True)
target_path = target_folder / file_path.name
# 处理重名文件
if target_path.exists():
name_without_ext = file_path.stem
ext = file_path.suffix
counter = 1
while target_path.exists():
new_name = f"{name_without_ext}_{counter}{ext}"
target_path = target_folder / new_name
counter += 1
shutil.move(str(file_path), str(target_path))
organized_count += 1
print(f"已移动: {file_path.name} -> {file_type}/")
print(f"按扩展名分类完成,共整理 {organized_count} 个文件")
def organize_by_date(self):
"""按修改日期分类整理"""
organized_count = 0
for file_path in self.source_dir.rglob('*'):
if file_path.is_file():
# 获取文件修改时间
mtime = file_path.stat().st_mtime
date_folder = datetime.fromtimestamp(mtime).strftime('%Y-%m')
target_folder = self.target_dir / date_folder
target_folder.mkdir(exist_ok=True)
target_path = target_folder / file_path.name
# 处理重名文件
if target_path.exists():
name_without_ext = file_path.stem
ext = file_path.suffix
counter = 1
while target_path.exists():
new_name = f"{name_without_ext}_{counter}{ext}"
target_path = target_folder / new_name
counter += 1
shutil.move(str(file_path), str(target_path))
organized_count += 1
print(f"已移动: {file_path.name} -> {date_folder}/")
print(f"按日期分类完成,共整理 {organized_count} 个文件")
def organize_by_size(self):
"""按文件大小分类整理"""
organized_count = 0
size_categories = {
'small': (0, 1024*1024), # < 1MB
'medium': (1024*1024, 10*1024*1024), # 1MB - 10MB
'large': (10*1024*1024, 100*1024*1024), # 10MB - 100MB
'huge': (100*1024*1024, float('inf')) # > 100MB
}
for file_path in self.source_dir.rglob('*'):
if file_path.is_file():
file_size = file_path.stat().st_size
# 确定文件大小类别
size_category = 'others'
for category, (min_size, max_size) in size_categories.items():
if min_size <= file_size < max_size:
size_category = category
break
target_folder = self.target_dir / size_category
target_folder.mkdir(exist_ok=True)
target_path = target_folder / file_path.name
# 处理重名文件
if target_path.exists():
name_without_ext = file_path.stem
ext = file_path.suffix
counter = 1
while target_path.exists():
new_name = f"{name_without_ext}_{counter}{ext}"
target_path = target_folder / new_name
counter += 1
shutil.move(str(file_path), str(target_path))
organized_count += 1
print(f"已移动: {file_path.name} -> {size_category}/")
print(f"按大小分类完成,共整理 {organized_count} 个文件")
def remove_duplicates(self):
"""查找并删除重复文件"""
file_hashes = {}
duplicates = []
for file_path in self.source_dir.rglob('*'):
if file_path.is_file():
# 计算文件哈希值
hash_value = self.calculate_file_hash(file_path)
if hash_value in file_hashes:
duplicates.append(file_path)
print(f"发现重复文件: {file_path} (与 {file_hashes[hash_value]} 相同)")
else:
file_hashes[hash_value] = file_path
# 删除重复文件
for duplicate in duplicates:
duplicate.unlink()
print(f"已删除重复文件: {duplicate}")
print(f"共删除 {len(duplicates)} 个重复文件")
def calculate_file_hash(self, file_path):
"""计算文件的MD5哈希值"""
hash_md5 = hashlib.md5()
with open(file_path, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
hash_md5.update(chunk)
return hash_md5.hexdigest()
def create_folder_structure(self):
"""创建目标文件夹结构"""
for folder_name in self.file_types.keys():
folder_path = self.target_dir / folder_name
folder_path.mkdir(exist_ok=True)
(self.target_dir / 'others').mkdir(exist_ok=True)
def main():
print("=== 文件整理分类工具 ===")
source_dir = input("请输入源文件夹路径: ").strip()
target_dir = input("请输入目标文件夹路径: ").strip()
if not os.path.exists(source_dir):
print("源文件夹不存在!")
return
# 创建目标文件夹
Path(target_dir).mkdir(exist_ok=True)
organizer = FileOrganizer(source_dir, target_dir)
print("\n请选择整理方式:")
print("1. 按文件类型整理")
print("2. 按修改日期整理")
print("3. 按文件大小整理")
print("4. 删除重复文件")
choice = input("请输入选择(1-4): ").strip()
if choice == '1':
organizer.create_folder_structure()
organizer.organize_by_extension()
elif choice == '2':
organizer.organize_by_date()
elif choice == '3':
organizer.organize_by_size()
elif choice == '4':
organizer.remove_duplicates()
else:
print("无效选择!")
if __name__ == "__main__":
main()
--------------------
PS F:\python> & C:/Users/Administrator/AppData/Local/Microsoft/WindowsApps/python3.13.exe f:/python/python_new/自动化/ServerMonitor.py
2026-03-25 17:52:02,637 - ERROR - 发送告警邮件时出错: Connection unexpectedly closed
============================================================
服务器状态检查报告 - 2026-03-25 17:52:02
============================================================
✓ 本地服务器 [正常]
cpu_usage: 8.5%
memory_usage: 83.7%
disk_usage: 17.7%
✗ 本地服务器 [异常]
host: 127.0.0.1
port: 80
⚠️ 服务不可达
✓ 百度 [正常]
host: www.baidu.com
port: 443
============================================================
import psutil
import smtplib
import schedule
import time
import logging
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from datetime import datetime
import json
import os
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('server_monitor.log'),
logging.StreamHandler()
]
)
class ServerMonitorMailer:
def __init__(self, config_file='monitor_config.json'):
"""初始化监控邮件系统"""
self.config = self.load_config(config_file)
self.smtp_config = self.config['smtp_settings']
self.recipients = self.config['recipients']
self.monitor_settings = self.config['monitor_settings']
def load_config(self, config_file):
"""加载配置文件"""
try:
with open(config_file, 'r', encoding='utf-8') as f:
return json.load(f)
except FileNotFoundError:
# 创建默认配置文件
default_config = {
"smtp_settings": {
"smtp_server": "smtp.gmail.com",
"smtp_port": 587,
"sender_email": "your_email@gmail.com",
"sender_password": "your_app_password"
},
"recipients": [
{
"name": "管理员",
"email": "admin@company.com",
"role": "admin"
}
],
"monitor_settings": {
"cpu_threshold": 80,
"memory_threshold": 85,
"disk_threshold": 90,
"check_interval_minutes": 30
},
"report_schedule": {
"daily_report_time": "09:00",
"weekly_report_day": "monday",
"monthly_report_date": 1
}
}
with open(config_file, 'w', encoding='utf-8') as f:
json.dump(default_config, f, indent=4, ensure_ascii=False)
logging.info(f"已创建默认配置文件: {config_file}")
return default_config
def get_system_info(self):
"""获取系统状态信息"""
try:
# CPU信息
cpu_percent = psutil.cpu_percent(interval=1)
# 内存信息
memory = psutil.virtual_memory()
memory_percent = memory.percent
# 磁盘信息
disk = psutil.disk_usage('/')
disk_percent = (disk.used / disk.total) * 100
# 网络信息
net_io = psutil.net_io_counters()
# 系统启动时间
boot_time = datetime.fromtimestamp(psutil.boot_time())
system_info = {
'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'cpu_percent': cpu_percent,
'memory_percent': memory_percent,
'disk_percent': disk_percent,
'boot_time': boot_time.strftime('%Y-%m-%d %H:%M:%S'),
'bytes_sent': net_io.bytes_sent,
'bytes_recv': net_io.bytes_recv
}
return system_info
except Exception as e:
logging.error(f"获取系统信息时出错: {e}")
return None
def create_html_report(self, system_info, report_type="status"):
"""创建HTML格式的报告"""
if not system_info:
return ""
css_style = """
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.container { max-width: 800px; margin: 0 auto; background: white; padding: 20px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
.header { text-align: center; color: #333; border-bottom: 2px solid #007cba; padding-bottom: 15px; }
.status-card { background: #f8f9fa; border-left: 4px solid #007cba; padding: 15px; margin: 15px 0; border-radius: 5px; }
.warning { border-left-color: #ffc107; background: #fff3cd; }
.danger { border-left-color: #dc3545; background: #f8d7da; }
.metric { display: flex; justify-content: space-between; margin: 10px 0; }
.metric-name { font-weight: bold; }
.metric-value { color: #007cba; }
.footer { text-align: center; margin-top: 20px; color: #666; font-size: 12px; }
</style>
"""
# 根据指标值确定状态级别
cpu_level = "danger" if system_info['cpu_percent'] > self.monitor_settings['cpu_threshold'] else \
"warning" if system_info['cpu_percent'] > self.monitor_settings['cpu_threshold'] * 0.8 else ""
memory_level = "danger" if system_info['memory_percent'] > self.monitor_settings['memory_threshold'] else \
"warning" if system_info['memory_percent'] > self.monitor_settings['memory_threshold'] * 0.8 else ""
disk_level = "danger" if system_info['disk_percent'] > self.monitor_settings['disk_threshold'] else \
"warning" if system_info['disk_percent'] > self.monitor_settings['disk_threshold'] * 0.8 else ""
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>服务器状态报告</title>
{css_style}
</head>
<body>
<div class="container">
<div class="header">
<h1>服务器状态监控报告</h1>
<p>{system_info['timestamp']}</p>
</div>
<div class="status-card">
<h2>CPU使用情况</h2>
<div class="metric">
<span class="metric-name">CPU使用率:</span>
<span class="metric-value">{system_info['cpu_percent']:.1f}%</span>
</div>
{f'<p style="color:red;">⚠️ CPU使用率过高,请关注!</p>' if cpu_level == 'danger' else ''}
{f'<p style="color:orange;">⚠️ CPU使用率偏高</p>' if cpu_level == 'warning' else ''}
</div>
<div class="status-card">
<h2>内存使用情况</h2>
<div class="metric">
<span class="metric-name">内存使用率:</span>
<span class="metric-value">{system_info['memory_percent']:.1f}%</span>
</div>
{f'<p style="color:red;">⚠️ 内存使用率过高,请关注!</p>' if memory_level == 'danger' else ''}
{f'<p style="color:orange;">⚠️ 内存使用率偏高</p>' if memory_level == 'warning' else ''}
</div>
<div class="status-card">
<h2>存储使用情况</h2>
<div class="metric">
<span class="metric-name">磁盘使用率:</span>
<span class="metric-value">{system_info['disk_percent']:.1f}%</span>
</div>
{f'<p style="color:red;">⚠️ 磁盘空间不足,请及时清理!</p>' if disk_level == 'danger' else ''}
{f'<p style="color:orange;">⚠️ 磁盘使用率偏高</p>' if disk_level == 'warning' else ''}
</div>
<div class="status-card">
<h2>网络流量统计</h2>
<div class="metric">
<span class="metric-name">发送字节数:</span>
<span class="metric-value">{system_info['bytes_sent']:,} bytes</span>
</div>
<div class="metric">
<span class="metric-name">接收字节数:</span>
<span class="metric-value">{system_info['bytes_recv']:,} bytes</span>
</div>
</div>
<div class="status-card">
<h2>系统信息</h2>
<div class="metric">
<span class="metric-name">系统启动时间:</span>
<span class="metric-value">{system_info['boot_time']}</span>
</div>
</div>
<div class="footer">
<p>此邮件由服务器自动发送,请勿回复</p>
<p>如有疑问请联系系统管理员</p>
</div>
</div>
</body>
</html>
"""
return html_content
def send_email(self, subject, content, recipients=None, is_html=False):
"""发送邮件"""
try:
if recipients is None:
recipients = self.recipients
# 创建邮件对象
msg = MIMEMultipart()
msg['From'] = self.smtp_config['sender_email']
msg['To'] = ', '.join([r['email'] for r in recipients])
msg['Subject'] = subject
# 添加邮件正文
if is_html:
msg.attach(MIMEText(content, 'html', 'utf-8'))
else:
msg.attach(MIMEText(content, 'plain', 'utf-8'))
# 连接SMTP服务器并发送邮件
server = smtplib.SMTP(self.smtp_config['smtp_server'], self.smtp_config['smtp_port'])
server.starttls()
server.login(self.smtp_config['sender_email'], self.smtp_config['sender_password'])
text = msg.as_string()
server.sendmail(self.smtp_config['sender_email'],
[r['email'] for r in recipients], text)
server.quit()
logging.info(f"邮件发送成功: {subject}")
return True
except Exception as e:
logging.error(f"发送邮件时出错: {e}")
return False
def send_status_alert(self, system_info):
"""发送状态告警邮件"""
# 检查是否有超出阈值的情况
alerts = []
if system_info['cpu_percent'] > self.monitor_settings['cpu_threshold']:
alerts.append(f"CPU使用率过高: {system_info['cpu_percent']:.1f}%")
if system_info['memory_percent'] > self.monitor_settings['memory_threshold']:
alerts.append(f"内存使用率过高: {system_info['memory_percent']:.1f}%")
if system_info['disk_percent'] > self.monitor_settings['disk_threshold']:
alerts.append(f"磁盘使用率过高: {system_info['disk_percent']:.1f}%")
if alerts:
subject = f"【警告】服务器状态异常 - {system_info['timestamp']}"
html_content = self.create_html_report(system_info)
self.send_email(subject, html_content, is_html=True)
return True
return False
def send_daily_report(self):
"""发送每日汇总报告"""
system_info = self.get_system_info()
if system_info:
subject = f"【日报】服务器状态汇总 - {system_info['timestamp'][:10]}"
html_content = self.create_html_report(system_info, "daily")
self.send_email(subject, html_content, is_html=True)
logging.info("每日报告已发送")
def send_weekly_report(self):
"""发送每周汇总报告"""
system_info = self.get_system_info()
if system_info:
subject = f"【周报】服务器状态汇总 - {system_info['timestamp'][:10]}"
html_content = self.create_html_report(system_info, "weekly")
self.send_email(subject, html_content, is_html=True)
logging.info("每周报告已发送")
def start_monitoring(self):
"""开始监控"""
logging.info("开始服务器状态监控...")
# 设置定时任务
schedule.every(self.monitor_settings['check_interval_minutes']).minutes.do(
self.check_and_alert
)
# 设置日常报告
daily_time = self.config['report_schedule']['daily_report_time']
schedule.every().day.at(daily_time).do(self.send_daily_report)
# 设置周报
weekly_day = self.config['report_schedule']['weekly_report_day']
getattr(schedule.every(), weekly_day).at("09:00").do(self.send_weekly_report)
# 立即执行一次检查
self.check_and_alert()
# 持续运行
while True:
schedule.run_pending()
time.sleep(60)
def check_and_alert(self):
"""检查系统状态并发送告警"""
system_info = self.get_system_info()
if system_info:
logging.info(f"系统检查完成 - CPU: {system_info['cpu_percent']:.1f}%, "
f"内存: {system_info['memory_percent']:.1f}%, "
f"磁盘: {system_info['disk_percent']:.1f}%")
# 发送告警邮件(如果有异常)
self.send_status_alert(system_info)
def main():
"""主函数"""
try:
monitor = ServerMonitorMailer()
# 根据命令行参数决定运行模式
import sys
if len(sys.argv) > 1:
if sys.argv[1] == '--once':
# 单次检查模式
system_info = monitor.get_system_info()
if system_info:
html_content = monitor.create_html_report(system_info)
print("系统状态检查完成,报告已生成")
elif sys.argv[1] == '--test-email':
# 测试邮件发送
system_info = monitor.get_system_info()
if system_info:
html_content = monitor.create_html_report(system_info)
success = monitor.send_email(
"服务器监控系统测试邮件",
html_content,
is_html=True
)
if success:
print("测试邮件发送成功")
else:
print("测试邮件发送失败")
else:
print("未知参数。可用参数: --once (单次检查), --test-email (测试邮件)")
else:
# 持续监控模式
monitor.start_monitoring()
except KeyboardInterrupt:
logging.info("监控程序已停止")
except Exception as e:
logging.error(f"程序运行出错: {e}")
if __name__ == "__main__":
main()
浙公网安备 33010602011771号