from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
import datetime
import time
import sys
import os
import glob
import re
def clear_download_dir(download_dir):
# 要删除的文件类型
patterns = ["*.xlsx", "*.xls", "*.csv", "*.pdf"] # 可根据需要扩展
deleted_count = 0
for pattern in patterns:
file_list = glob.glob(os.path.join(download_dir, pattern))
for file_path in file_list:
os.remove(file_path)
print(f"已删除: {os.path.basename(file_path)}")
deleted_count += 1
else:
print(f"文件不存在")
if deleted_count == 0:
print("✅ 下载目录为空,无需清理")
else:
print(f"🧹 共清理 {deleted_count} 个旧文件")
def load_all_operations(module):
combined = {}
for attr_name in dir(module):
if attr_name.startswith("OPERATIONS_"):
obj = getattr(module, attr_name)
if isinstance(obj, dict):
duplicate_keys = set(obj.keys()) & set(combined.keys())
if duplicate_keys:
print(f"⚠️ 警告: '{attr_name}' 包含重复任务名: {duplicate_keys} → 将被覆盖")
combined.update(obj)
print(f"✅ 加载操作集: {attr_name} ({len(obj)} 项)")
else:
print(f"❌ 跳过非字典成员: {attr_name}")
return combined
# ========================================
class EnergySystemLogin:
def __init__(self):
self.login_url = "http://10.11.20.117:7001/energy4/#/login"
self.credentials = {'username': 'E915285', 'password': '123456'}
self.driver = None
self.wait_timeout = 30
def initialize_driver(self):
"""初始化Chrome浏览器(无头模式 + 支持文件下载)"""
try:
options = webdriver.ChromeOptions()
# --- 反检测设置 ---
options.add_argument("--disable-blink-features=AutomationControlled")
options.add_argument("--start-maximized")
options.add_argument("--headless=new") # 新版无头模式
options.add_argument("--disable-gpu")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--window-size=1920,1080")
options.add_argument("--log-level=3")
# 关闭自动化控制提示
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
# 下载目录配置
download_dir = r"D:\Class\能管比对\导出文件"
if not os.path.exists(download_dir):
os.makedirs(download_dir)
prefs = {
"download.default_directory": download_dir,
"download.prompt_for_download": False,
"download.directory_upgrade": True,
"safebrowsing.enabled": True,
"profile.default_content_settings.popups": 0,
"plugins.always_open_pdf_externally": False,
}
options.add_experimental_option("prefs", prefs)
self.driver = webdriver.Chrome(options=options)
self.driver.implicitly_wait(10)
# 防止被识别为机器人
self.driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => false});")
self.log_action(f"浏览器初始化完成,下载路径: {download_dir}")
return True
except:
pass
def log_action(self, message):
timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print(f"[{timestamp}] {message}")
def log_error(self, message):
timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print(f"[{timestamp}] 错误: {message}")
def locate_and_input(self, by, value, text, description):
try:
element = WebDriverWait(self.driver, self.wait_timeout).until(
EC.visibility_of_element_located((by, value))
)
element.clear()
element.send_keys(text)
self.log_action(f"{description} 输入完成")
return True
except:
pass
def perform_login(self):
try:
self.log_action("访问登录页面")
self.driver.get(self.login_url)
# 等待页面加载完成
WebDriverWait(self.driver, self.wait_timeout).until(
lambda d: d.execute_script("return document.readyState") == "complete"
)
# 输入用户名
if not self.locate_and_input(
By.CSS_SELECTOR,
"input.el-input__inner[type='text']",
self.credentials['username'],
"用户名"
):
return False
# 输入密码
if not self.locate_and_input(
By.CSS_SELECTOR,
"input.el-input__inner[type='password']",
self.credentials['password'],
"密码"
):
return False
# 点击登录按钮
try:
login_btn = WebDriverWait(self.driver, self.wait_timeout).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "button.loginBtn"))
)
login_btn.click()
self.log_action("点击登录按钮")
except Exception as e:
self.log_error(f"点击登录按钮失败: {str(e)}")
return False
# 等待登录成功跳转
try:
WebDriverWait(self.driver, 20).until(
EC.url_contains("/energy4/#/")
)
WebDriverWait(self.driver, 10).until_not(
EC.presence_of_element_located((By.CLASS_NAME, "login-container"))
)
self.log_action("登录成功")
self.driver.save_screenshot("login_success.png")
return True
except TimeoutException:
self.log_error("登录验证超时,可能未成功登录")
return False
except Exception as e:
self.log_error(f"登录过程发生异常: {str(e)}")
return False
def get_driver(self):
return self.driver
def cleanup(self):
if self.driver:
try:
self.driver.quit()
except Exception as e:
print(f"清理浏览器资源时出错: {str(e)}")
# ========================================
class MenuNavigator:
def __init__(self, driver, operations_dict, download_dir=r"D:\Class\能管比对\导出文件"):
self.driver = driver
self.wait_timeout = 10
self.click_delay = 3
self.download_dir = download_dir
self.OPERATIONS = operations_dict # 接收外部传入的所有操作流程
def get_timestamp(self):
return datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
def log_action(self, message):
print(f"[{self.get_timestamp()}] 操作: {message}")
def log_error(self, message):
print(f"[{self.get_timestamp()}] 错误: {message}")
try:
print("有点问题")
except:
pass
def click_element(self, xpath, description):
try:
element = WebDriverWait(self.driver, self.wait_timeout).until(
EC.visibility_of_element_located((By.XPATH, xpath))
)
element.click()
self.log_action(f"点击成功: {description}")
time.sleep(self.click_delay) # 固定等待页面响应
return True
except Exception as e:
self.log_error(f"点击失败 ({description}): {str(e)}")
return False
def get_latest_downloaded_file(self, timeout=30):
"""等待并返回最新稳定的 .xlsx 文件"""
start_time = time.time()
while time.time() - start_time < timeout:
list_of_files = [
f for f in glob.glob(os.path.join(self.download_dir, "*.xls*"))
if not os.path.basename(f).startswith("~$")
]
if list_of_files:
latest_file = max(list_of_files, key=os.path.getmtime)
size1 = os.path.getsize(latest_file)
time.sleep(1)
size2 = os.path.getsize(latest_file)
if size1 == size2: # 大小稳定表示写入完成
return latest_file
time.sleep(1)
raise Exception("等待下载超时,未检测到新文件")
def extract_filename(self, task_key):
filename = f"{task_key}.xlsx"
return filename
def rename_latest_file(self, new_name):
"""将最新下载的文件重命名为指定名称,避免覆盖"""
try:
old_file = self.get_latest_downloaded_file()
new_file_path = os.path.join(self.download_dir, new_name)
counter = 1
base_name, ext = os.path.splitext(new_name)
while os.path.exists(new_file_path):
new_file_path = os.path.join(self.download_dir, f"{base_name}_{counter}{ext}")
counter += 1
os.rename(old_file, new_file_path)
self.log_action(f"文件已重命名: {os.path.basename(old_file)} → {os.path.basename(new_file_path)}")
return True
except Exception as e:
self.log_error(f"文件重命名失败: {str(e)}")
return False
def navigate(self, task_key):
"""执行单个任务流程"""
if task_key not in self.OPERATIONS:
self.log_error(f"未找到名为 '{task_key}' 的操作流程")
return False
operations = self.OPERATIONS[task_key]
self.log_action(f"开始执行任务: {task_key}")
for op in operations:
if not self.click_element(op["xpath"], op["desc"]):
self.log_error(f"操作中断于: {op['desc']}")
return False
# 🔁 判断是否为预操作:不需要导出文件
if task_key == "0.预操作":
self.log_action("✅ 任务 '0.预操作' 执行完成(无导出动作)")
return True
# 其他任务继续走导出逻辑
auto_filename = self.extract_filename(task_key)
self.log_action("正在等待导出文件生成...")
if self.rename_latest_file(auto_filename):
self.log_action(f"✅ 任务 '{task_key}' 成功,文件已保存为: {auto_filename}")
return True
else:
self.log_error(f"❌ 任务 '{task_key}' 失败:无法获取或重命名导出文件")
return False
def wait_for_file_stable(self, file_path, timeout=10):
"""等待指定文件大小不再变化"""
if not os.path.exists(file_path):
return
try:
size1 = os.path.getsize(file_path)
for _ in range(timeout):
time.sleep(1)
if not os.path.exists(file_path):
break
size2 = os.path.getsize(file_path)
if size1 == size2:
break
size1 = size2
except Exception as e:
self.log_error(f"检查文件稳定性时出错: {str(e)}")
if __name__ == "__main__":
login = None
download_dir = r"D:\Class\能管比对\导出文件" # ← 统一管理路径
clear_download_dir(download_dir)
try:
login = EnergySystemLogin()
driver = login.get_driver()
# 🔍 支持加载多个 operations 模块
module_names = [
"menu_operations0",
# "menu_operations1",
# "menu_operations2",
# "menu_operations3",
"menu_operations4",
]
ALL_OPERATIONS = {}
for mod_name in module_names:
try:
module = __import__(mod_name)
import importlib
importlib.reload(module)
combined_in_mod = load_all_operations(module)
duplicate_keys = set(combined_in_mod.keys()) & set(ALL_OPERATIONS.keys())
if duplicate_keys:
print(f"⚠️ 跨模块重复任务名: {duplicate_keys} → 将被后加载的覆盖")
ALL_OPERATIONS.update(combined_in_mod)
print(f"📁 已从模块 '{mod_name}' 加载 {len(combined_in_mod)} 项任务\n")
except:
break
navigator = MenuNavigator(driver, operations_dict=ALL_OPERATIONS)
tasks_to_run = list(ALL_OPERATIONS.keys())
print(f"\n📋 共发现 {len(tasks_to_run)} 个待执行任务:")
for i, task in enumerate(tasks_to_run, 1):
print(f" {i}. {task}")
success_count = 0
for task in tasks_to_run:
print(f"\n🔁 正在执行任务: {task}")
if navigator.navigate(task):
success_count += 1
else:
print(f"任务 '{task}' 执行失败")
print(f"\n🎉 总结:{success_count}/{len(tasks_to_run)} 个任务执行成功")
except KeyboardInterrupt:
print("\n\n🛑 用户手动中断程序")
except Exception as e:
print(f"🚨 程序发生未预期异常: {str(e)}")
finally:
if login:
login.cleanup()
print("🔚 程序结束")
——"D:\Program Files\Python39\python.exe" C:\Users\E918928\PycharmProjects\pythonProject\LZRR\代码1、\能管检查\去OPERATE.py
文件不存在
文件不存在
文件不存在
文件不存在
✅ 下载目录为空,无需清理
✅ 加载操作集: OPERATIONS_0 (1 项)
📁 已从模块 'menu_operations0' 加载 1 项任务
✅ 加载操作集: OPERATIONS_4 (23 项)
📁 已从模块 'menu_operations4' 加载 23 项任务
📋 共发现 24 个待执行任务:
1. 0.预操作
2. 70.电系统-行政、后勤类-生活类
3. 71.电系统-行政、后勤类-办公类
4. 72.电系统-行政、后勤类-其他辅助类
5. 73.电系统-工艺类-FAB1
6. 74.电系统-工艺类-OS1 Wafersort
7. 75.电系统-工艺类-实验室
8. 76.电系统-厂务类-AC
9. 77.电系统-厂务类-GC
10. 78.电系统-厂务类-water
11. 79.电系统-厂务类-Elec
12. 80.电系统-厂务类-CFC
13. 81.电系统-大宗气站-大宗气站1
14. 82.电系统-大宗气站-大宗气站2
15. 83.FAB
16. 84.电系统-220Kv PS1a-CUB
17. 85.电系统-220Kv PS1a-WWT
18. 86.电系统-220Kv PS1a-OS
19. 87.电系统-220Kv PS1a-ADB
20. 88.电系统-220Kv PS1a-XDH
21. 89.电系统-220Kv PS1a-生活区
22. 90.电系统-220Kv PS1a-未来馆
23. 91.电系统-工艺厂务能耗比
24. 92.电系统-工艺+厂务耗电量
🔁 正在执行任务: 0.预操作
[2025-11-05 11:14:29] 操作: 开始执行任务: 0.预操作
[2025-11-05 11:14:29] 错误: 点击失败 (能耗统计): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: 能耗统计
有点问题
任务 '0.预操作' 执行失败
🔁 正在执行任务: 70.电系统-行政、后勤类-生活类
[2025-11-05 11:14:29] 操作: 开始执行任务: 70.电系统-行政、后勤类-生活类
[2025-11-05 11:14:29] 错误: 点击失败 (电系统): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: 电系统
有点问题
任务 '70.电系统-行政、后勤类-生活类' 执行失败
🔁 正在执行任务: 71.电系统-行政、后勤类-办公类
[2025-11-05 11:14:29] 操作: 开始执行任务: 71.电系统-行政、后勤类-办公类
[2025-11-05 11:14:29] 错误: 点击失败 (办公类): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: 办公类
有点问题
任务 '71.电系统-行政、后勤类-办公类' 执行失败
🔁 正在执行任务: 72.电系统-行政、后勤类-其他辅助类
[2025-11-05 11:14:29] 操作: 开始执行任务: 72.电系统-行政、后勤类-其他辅助类
[2025-11-05 11:14:29] 错误: 点击失败 (其他辅助类): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: 其他辅助类
有点问题
任务 '72.电系统-行政、后勤类-其他辅助类' 执行失败
🔁 正在执行任务: 73.电系统-工艺类-FAB1
[2025-11-05 11:14:29] 操作: 开始执行任务: 73.电系统-工艺类-FAB1
[2025-11-05 11:14:29] 错误: 点击失败 (工艺类): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: 工艺类
有点问题
任务 '73.电系统-工艺类-FAB1' 执行失败
🔁 正在执行任务: 74.电系统-工艺类-OS1 Wafersort
[2025-11-05 11:14:29] 操作: 开始执行任务: 74.电系统-工艺类-OS1 Wafersort
[2025-11-05 11:14:29] 错误: 点击失败 (OS1 Wafersort): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: OS1 Wafersort
有点问题
任务 '74.电系统-工艺类-OS1 Wafersort' 执行失败
🔁 正在执行任务: 75.电系统-工艺类-实验室
[2025-11-05 11:14:29] 操作: 开始执行任务: 75.电系统-工艺类-实验室
[2025-11-05 11:14:29] 错误: 点击失败 (实验室): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: 实验室
有点问题
任务 '75.电系统-工艺类-实验室' 执行失败
🔁 正在执行任务: 76.电系统-厂务类-AC
[2025-11-05 11:14:29] 操作: 开始执行任务: 76.电系统-厂务类-AC
[2025-11-05 11:14:29] 错误: 点击失败 (厂务类): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: 厂务类
有点问题
任务 '76.电系统-厂务类-AC' 执行失败
🔁 正在执行任务: 77.电系统-厂务类-GC
[2025-11-05 11:14:29] 操作: 开始执行任务: 77.电系统-厂务类-GC
[2025-11-05 11:14:29] 错误: 点击失败 (GC): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: GC
有点问题
任务 '77.电系统-厂务类-GC' 执行失败
🔁 正在执行任务: 78.电系统-厂务类-water
[2025-11-05 11:14:29] 操作: 开始执行任务: 78.电系统-厂务类-water
[2025-11-05 11:14:29] 错误: 点击失败 (water): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: water
有点问题
任务 '78.电系统-厂务类-water' 执行失败
🔁 正在执行任务: 79.电系统-厂务类-Elec
[2025-11-05 11:14:29] 操作: 开始执行任务: 79.电系统-厂务类-Elec
[2025-11-05 11:14:29] 错误: 点击失败 (Elec): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: Elec
有点问题
任务 '79.电系统-厂务类-Elec' 执行失败
🔁 正在执行任务: 80.电系统-厂务类-CFC
[2025-11-05 11:14:29] 操作: 开始执行任务: 80.电系统-厂务类-CFC
[2025-11-05 11:14:29] 错误: 点击失败 (CFC): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: CFC
有点问题
任务 '80.电系统-厂务类-CFC' 执行失败
🔁 正在执行任务: 81.电系统-大宗气站-大宗气站1
[2025-11-05 11:14:29] 操作: 开始执行任务: 81.电系统-大宗气站-大宗气站1
[2025-11-05 11:14:29] 错误: 点击失败 (大宗气站): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: 大宗气站
有点问题
任务 '81.电系统-大宗气站-大宗气站1' 执行失败
🔁 正在执行任务: 82.电系统-大宗气站-大宗气站2
[2025-11-05 11:14:29] 操作: 开始执行任务: 82.电系统-大宗气站-大宗气站2
[2025-11-05 11:14:29] 错误: 点击失败 (大宗气站2): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: 大宗气站2
有点问题
任务 '82.电系统-大宗气站-大宗气站2' 执行失败
🔁 正在执行任务: 83.FAB
[2025-11-05 11:14:29] 操作: 开始执行任务: 83.FAB
[2025-11-05 11:14:29] 错误: 点击失败 (220kV PS1a(按建筑分)): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: 220kV PS1a(按建筑分)
有点问题
任务 '83.FAB' 执行失败
🔁 正在执行任务: 84.电系统-220Kv PS1a-CUB
[2025-11-05 11:14:29] 操作: 开始执行任务: 84.电系统-220Kv PS1a-CUB
[2025-11-05 11:14:29] 错误: 点击失败 (CUB): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: CUB
有点问题
任务 '84.电系统-220Kv PS1a-CUB' 执行失败
🔁 正在执行任务: 85.电系统-220Kv PS1a-WWT
[2025-11-05 11:14:29] 操作: 开始执行任务: 85.电系统-220Kv PS1a-WWT
[2025-11-05 11:14:29] 错误: 点击失败 (WWT): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: WWT
有点问题
任务 '85.电系统-220Kv PS1a-WWT' 执行失败
🔁 正在执行任务: 86.电系统-220Kv PS1a-OS
[2025-11-05 11:14:29] 操作: 开始执行任务: 86.电系统-220Kv PS1a-OS
[2025-11-05 11:14:29] 错误: 点击失败 (OS): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: OS
有点问题
任务 '86.电系统-220Kv PS1a-OS' 执行失败
🔁 正在执行任务: 87.电系统-220Kv PS1a-ADB
[2025-11-05 11:14:29] 操作: 开始执行任务: 87.电系统-220Kv PS1a-ADB
[2025-11-05 11:14:29] 错误: 点击失败 (ADB): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: ADB
有点问题
任务 '87.电系统-220Kv PS1a-ADB' 执行失败
🔁 正在执行任务: 88.电系统-220Kv PS1a-XDH
[2025-11-05 11:14:29] 操作: 开始执行任务: 88.电系统-220Kv PS1a-XDH
[2025-11-05 11:14:29] 错误: 点击失败 (XDH): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: XDH
有点问题
任务 '88.电系统-220Kv PS1a-XDH' 执行失败
🔁 正在执行任务: 89.电系统-220Kv PS1a-生活区
[2025-11-05 11:14:29] 操作: 开始执行任务: 89.电系统-220Kv PS1a-生活区
[2025-11-05 11:14:29] 错误: 点击失败 (生活区): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: 生活区
有点问题
任务 '89.电系统-220Kv PS1a-生活区' 执行失败
🔁 正在执行任务: 90.电系统-220Kv PS1a-未来馆
[2025-11-05 11:14:29] 操作: 开始执行任务: 90.电系统-220Kv PS1a-未来馆
[2025-11-05 11:14:29] 错误: 点击失败 (未来馆): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: 未来馆
有点问题
任务 '90.电系统-220Kv PS1a-未来馆' 执行失败
🔁 正在执行任务: 91.电系统-工艺厂务能耗比
[2025-11-05 11:14:29] 操作: 开始执行任务: 91.电系统-工艺厂务能耗比
[2025-11-05 11:14:29] 错误: 点击失败 (工艺厂务能耗比): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: 工艺厂务能耗比
有点问题
任务 '91.电系统-工艺厂务能耗比' 执行失败
🔁 正在执行任务: 92.电系统-工艺+厂务耗电量
[2025-11-05 11:14:29] 操作: 开始执行任务: 92.电系统-工艺+厂务耗电量
[2025-11-05 11:14:29] 错误: 点击失败 (工艺+厂务耗电量): 'NoneType' object has no attribute 'find_element'
有点问题
[2025-11-05 11:14:29] 错误: 操作中断于: 工艺+厂务耗电量
有点问题
任务 '92.电系统-工艺+厂务耗电量' 执行失败
🎉 总结:0/24 个任务执行成功
🔚 程序结束
Process finished with exit code 0
什么情况
最新发布