2012-03-19 11:15 关于excel 2007需要关闭两次的解决办法

解决Excel关闭不彻底的问题,只需删除特定文件夹内的文件即可实现一键完全退出。

最近我发现我关EXCEL中,我是关闭最上面右角的叉,但是不能完全退出EXCEL,第一次关闭时,只能把EXCEL的内容关闭,第二次才能退掉EXCEL~关两次好累哦!
今天终于解决了,解决方法是:
在系统盘中搜索XLSTART这个文件夹,你有可能会搜索到好几个,每一个都进去看,大部分是空的,其中有一个不是空的,里面有文件,把文件全删了,关闭~OK!解决了~

个人研究成果,拿出来分享一下,哈哈~当然不嫌累的点程序菜单,再选择退出程序也行~

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 # 导入操作配置模块 # import menu_operations def load_all_operations(module): """ 自动扫描模块中所有名为 OPERATIONS_ 开头的字典变量,合并为一个大字典 若有重复 key,后加载的覆盖前面的(可加入警告提示) """ 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 Exception as e: self.log_error(f"浏览器初始化失败: {str(e)}") return False 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') self.log_action(f"错误: {message}") if self.driver: try: self.driver.save_screenshot(f"error_{timestamp}.png") except Exception as e: print(f"[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 截图失败: {str(e)}") 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 Exception as e: self.log_error(f"{description} 输入失败: {str(e)}") return False 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: self.driver.save_screenshot(f"error_{int(time.time())}.png") 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 if __name__ == "__main__": login = None try: login = EnergySystemLogin() # ✅ 第一步:初始化浏览器 if not login.initialize_driver(): print("❌ 浏览器初始化失败,程序退出") sys.exit(1) # ✅ 第二步:执行登录 if not login.perform_login(): print("❌ 登录失败,程序退出") sys.exit(1) # ✅ 第三步:获取已登录的 driver driver = login.get_driver() # 🔍 支持加载多个 operations 模块 module_names = [ "menu_operations0", "menu_operations1", "menu_operations2", # "menu_operations3", ] 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 ImportError as e: print(f"❌ 无法导入模块 '{mod_name}': {e}") except Exception as e: print(f"❌ 加载模块 '{mod_name}' 时发生异常: {str(e)}") 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("🔚 程序结束") —— """ 能源系统自动化脚本的操作路径配置文件 支持多个 OPERATIONS_* 字典,main.py 会自动扫描并合并所有任务 """ OPERATIONS_PART1 = { "1.生产用水": [ {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='水处理系统(旧)']]", "desc": "水处理系统(旧)"}, {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='生产用水']]", "desc": "生产用水"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], "2.全厂耗水分析": [ {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='全厂耗水分析']]", "desc": "全厂耗水分析"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], "3.City Water Consumption": [ {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='纯水曲线看板']]","desc": "纯水曲线看板"}, {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='City Water Consumption']]","desc": "City Water Consumption"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], "4.UPW Consumption": [ {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='UPW Consumption']]","desc": "UPW Consumption"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], "5.Other Water Consumption": [ {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='Other Water Consumption']]","desc": "Other Water Consumption"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], "6.全期DIR&LSR回收水量": [ {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='DIR&LSR回收水量']]","desc": "DIR&LSR回收水量"}, {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='全期DIR&LSR回收水量']]","desc": "全期DIR&LSR回收水量"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], "7.全期 DIR系统产水": [ {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='全期 DIR系统产水']]","desc": "全期 DIR系统产水"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], "8.全期 LSR系统产水": [ {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='全期 LSR系统产水']]","desc": "全期 LSR系统产水"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], # "9.DIR回收率": [ # {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='DIR回收率']]","desc": "DIR回收率"}, # {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, # {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, # ], "10.WWT各系统水量": [ {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='废水曲线看板']]", "desc": "废水曲线看板"}, {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='WWT各系统水量']]", "desc": "WWT各系统水量"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], "11.WWT放流水_pH": [ {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='WWT放流水_pH']]", "desc": "WWT放流水_pH"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], "12.WWT放流水质_F&NH3_N": [ {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='WWT放流水质_F&NH3_N']]","desc": "WWT放流水质_F&NH3_N"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], "13.WWT放流水质_COD&SS": [ {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='WWT放流水质_COD&SS']]","desc": "WWT放流水质_COD&SS"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], "14.WWT放流水质_Cu": [ {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='WWT放流水质_Cu']]","desc": "WWT放流水质_Cu"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], "15.废水产量": [ {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='废水产量']]","desc": "废水产量"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], "16.超纯水耗电量": [ {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='超纯水耗电量']]","desc": "超纯水耗电量"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], "17.UPW_ECF_参考": [ {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='UPW_ECF_参考']]", "desc": "UPW_ECF_参考"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], "18.纯水电量": [ {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='纯水电量']]", "desc": "纯水电量"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], "19.纯水电盘电量": [ {"xpath": "//div[@class='el-tree-node__content'][.//span[@title='纯水电盘电量']]", "desc": "纯水电盘电量"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], } # # 示例:第一组操作(冷热水系统) # OPERATIONS_PART1 = { # "1.排气系统风量": [ # {"xpath": "//span[@slot='title'][text()='能耗统计']", "desc": "能耗统计"}, # {"xpath": "//span[text()='分类统计' and contains(@data-v-11708afe,'')]", "desc": "分类统计"}, # {"xpath": "//div[@role='button' and contains(@class,'el-collapse-item__header')][normalize-space()='系统分组']", "desc": "系统分组"}, # {"xpath": "//span[@data-v-4fe475b6 and @title='空调系统' and normalize-space()='空调系统']", "desc": "空调系统"}, # {"xpath": "//div[@class='el-tree-node__content' and contains(@style,'padding-left: 18px;')]//span[@title='排气系统']", "desc": "排气系统"}, # {"xpath": "//div[contains(@class, 'el-tree-node__content') and contains(., '排气系统风量')]","desc": "排气系统风量"}, # {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, # {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, # ], # # "40.西侧2#外气焓值": [ # {"xpath": "//div[contains(@class, 'el-tree-node__content') and contains(., '西侧2#外气焓值')]","desc": "西侧2#外气焓值"}, # {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, # {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, # ], # "41.一般空调耗电量": [ # {"xpath": "//div[@class='el-tree-node__content' and contains(@style,'padding-left: 18px;')]//span[@title='一般空调耗电量']","desc": "一般空调耗电量"}, # {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, # {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, # ], # } ——这里头有个问题导致我最后导出文件少了一个——🔁 正在执行任务: 2.全厂耗水分析 [2025-11-03 15:56:58] 操作: 开始执行任务: 2.全厂耗水分析 [2025-11-03 15:56:59] 操作: 点击成功: 全厂耗水分析 [2025-11-03 15:57:02] 操作: 点击成功: 查询 [2025-11-03 15:57:07] 操作: 点击成功: 导出 [2025-11-03 15:57:10] 操作: 正在等待导出文件生成... [2025-11-03 15:57:11] 操作: 文件已重命名: 列表.xlsx → 2.全厂耗水分析.xlsx [2025-11-03 15:57:11] 操作: ✅ 任务 '2.全厂耗水分析' 成功,文件已保存为: 2.全厂耗水分析.xlsx,🔁 正在执行任务: 3.City Water Consumption [2025-11-03 15:57:11] 操作: 开始执行任务: 3.City Water Consumption [2025-11-03 15:57:11] 操作: 点击成功: 纯水曲线看板 [2025-11-03 15:57:16] 操作: 点击成功: City Water Consumption [2025-11-03 15:57:20] 操作: 点击成功: 查询 [2025-11-03 15:57:23] 操作: 点击成功: 导出 [2025-11-03 15:57:26] 操作: 正在等待导出文件生成... [2025-11-03 15:57:27] 操作: 文件已重命名: 2.全厂耗水分析.xlsx → 3.City Water Consumption.xlsx [2025-11-03 15:57:27] 操作: ✅ 任务 '3.City Water Consumption' 成功,文件已保存为: 3.City Water Consumption.xlsx,为什么把全场耗水分析更名为了下一个任务的名字,导致少了一个导出文件
最新发布
11-04
你遇到的问题非常典型,也是自动化导出中常见的“**文件名覆盖/错乱问题**”。 --- ### ❌ 问题描述 你的日志显示: ``` [2025-11-03 15:57:10] 操作: 正在等待导出文件生成... [2025-11-03 15:57:11] 操作: 文件已重命名: 列表.xlsx → 2.全厂耗水分析.xlsx ✅ 任务 '2.全厂耗水分析' 成功 ... [2025-11-03 15:57:26] 操作: 正在等待导出文件生成... [2025-11-03 15:57:27] 操作: 文件已重命名: 2.全厂耗水分析.xlsx → 3.City Water Consumption.xlsx ``` > 🚨 **原本已经保存好的 `2.全厂耗水分析.xlsx` 被后续任务改名操作覆盖了!** 也就是说: 👉 第二个任务成功后生成的文件被正确命名为 `2.全厂耗水分析.xlsx`, 但第三个任务执行完导出后,它去重命名“最新下载的文件”时,发现这个文件是 `列表.xlsx`(可能是浏览器缓存或未及时清理),但它错误地把 **前一个已命名的文件** 当成了“最新文件”,从而导致了文件名替换异常! --- ## 🔍 根本原因分析 ### ✅ 真正的问题在于: > **`get_latest_downloaded_file()` 方法判断的是整个目录下最近修改的 `.xlsx` 文件,而不是“本次点击‘导出’按钮之后新生成的那个文件”。** 由于多个任务连续执行、导出的文件都叫 `列表.xlsx`(常见于前端固定导出名),Selenium 下载机制不会立刻删除旧临时文件,所以: | 时间 | 动作 | 实际文件 | |------|------|--------| | T1 | 导出任务2 → 下载 `列表.xlsx` | → 重命名为 `2.全厂耗水分析.xlsx` | | T2 | 导出任务3 → 再次下载 `列表.xlsx` | 浏览器可能仍用同名文件写入 | | T3 | `get_latest_downloaded_file()` 扫描所有 `.xlsx` | 发现 `2.全厂耗水分析.xlsx` 修改时间更新(因为刚创建)→ 错误认为这是“最新下载”的文件! | 于是调用 `os.rename("2.全厂耗水分析.xlsx", "3.City Water Consumption.xlsx")` —— 相当于把前一个文件的名字抢走了! 💥 结果就是: - `2.全厂耗水分析.xlsx` 不见了; - 原本应有两个独立文件,现在只剩一个; - 文件内容其实是 **City Water Consumption 的数据**,名字却是从别人那儿“偷”来的。 --- ## ✅ 解决方案:精准识别“本次下载的新文件” 我们需要让程序只关注 **当前任务触发后才出现的新文件**,而不是扫描整个目录找最新的 `.xlsx`。 ### ✅ 改进后的 `get_latest_downloaded_file()` 加上“起始时间过滤” ```python def get_latest_downloaded_file(self, timeout=30): """等待并返回从调用此函数时刻起,新生成且写入完成的 .xlsx 文件""" start_time = time.time() existing_files = set(glob.glob(os.path.join(self.download_dir, "*.xls*"))) # 记录调用前已存在的文件 while time.time() - start_time < timeout: # 获取所有非临时的 Excel 文件 current_files = { f for f in glob.glob(os.path.join(self.download_dir, "*.xls*")) if not os.path.basename(f).startswith("~$") } # 找出本次调用后新增的文件 new_files = [f for f in current_files if f not in existing_files] if new_files: # 按修改时间排序,取最新的 latest_file = max(new_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("等待下载超时,未检测到新文件") ``` --- ### ✅ 补充建议:避免文件名冲突和浏览器缓存影响 #### 1. 清理下载目录中的临时文件(可选) 在每次开始前清空旧的 `列表.xlsx` 类型临时文件: ```python def clear_download_dir(self): """清理下载目录中的临时或旧导出文件""" temp_files = [ f for f in glob.glob(os.path.join(self.download_dir, "*.*")) if os.path.basename(f).startswith("列表.") or f.endswith(".crdownload") ] for f in temp_files: try: os.remove(f) self.log_action(f"已清理临时文件: {f}") except: pass ``` 并在 `navigate()` 开始前调用一次: ```python 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}") # 可选:清理上一轮遗留的临时文件 # self.clear_download_dir() 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 ``` --- ## ✅ 验证修复效果 修复后逻辑变为: | 任务 | 行为 | |------|------| | `2.全厂耗水分析` | 触发下载 → 找到新出现的 `列表.xlsx` → 重命名为 `2.全厂耗水分析.xlsx` | | `3.City Water Consumption` | 再次触发下载 → 新增另一个 `列表.xlsx` → 被识别为“新文件” → 重命名为 `3.City Water Consumption.xlsx` | | ✅ 结果 | 两个文件独立存在,互不干扰 | --- ## ✅ 总结:关键修复点 | 问题 | 修复方式 | |------|----------| | `get_latest_downloaded_file` 扫描全局导致误判 | ✅ 引入 `existing_files` 集合,只认“新增文件” | | 多个导出共用 `列表.xlsx` 名字 | ✅ 不依赖原始文件名,只关心“哪个是新的” | | 文件被 rename 覆盖 | ✅ 新增文件不会与已有命名文件混淆 | --- ## ✅ 最终推荐完整补丁代码(仅替换方法即可) ```python def get_latest_downloaded_file(self, timeout=30): """等待并返回本次操作期间新生成且写入稳定的 .xlsx 文件""" start_time = time.time() # 记录调用此函数前已存在的文件 existing_files = set( glob.glob(os.path.join(self.download_dir, "*.xls*")) + glob.glob(os.path.join(self.download_dir, "*.crdownload")) ) while time.time() - start_time < timeout: # 当前所有合法的 Excel 文件(排除临时文件) current_files = { f for f in glob.glob(os.path.join(self.download_dir, "*.xls*")) if not os.path.basename(f).startswith(("~$", "error_", "列表.")) # 可额外排除一些中间名 } # 找出本次下载过程中新出现的文件 new_files = [f for f in current_files if f not in existing_files] if not new_files: time.sleep(1) continue # 取最新修改的 latest_file = max(new_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 raise Exception("等待下载超时,未检测到新文件") ``` > 💡 提示:如果你能控制前端,最好让每个导出有不同的默认名称(如含时间戳),但这通常不可行。因此上述方案是最实用的。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值