XPath理解:使用extract()时[0]位置的理解

本文介绍如何使用XPath从快代理网站抓取代理IP信息。通过具体实例,展示了如何利用response对象提取HTML元素,包括使用xpath和extract方法获取 tbody 中的 tr 数据。文章深入解析了SelectorList对象的用法,以及如何处理提取到的HTML内容。

以爬取快代理https://www.kuaidaili.com/free/为例。

response是整个页面返回的数据,页面内容。 

  • response.xpath('//*[@id="list"]/table/tbody/tr')

返回结果:

返回一个SelectorList对象,SelectorList 类是内建 list 类的子类,它是一个列表,列表的每一项都是一个如下图所示的元素:

提供了一些额外的方法:
 xpath(query)
 css(query)
 extract()
 re()
 __nonzero__()

  • response.xpath('//*[@id="list"]/table/tbody/tr').extract()

    返回结果:

.extract()提取HTML内容,返回一个列表。 

  • response.xpath('//*[@id="list"]/table/tbody/tr').extract()[0]
    response.xpath('//*[@id="list"]/table/tbody/tr')[0].extract()

这两个一个是先得到一个HTML内容组成的列表,再取第一个元素;一个是先得到SelectorList第一个元素,再提取成HTML内容,总之返回的结果一样。

  • response.xpath('//*[@id="list"]/table/tbody/tr')[0].extract()[0]

返回结果:

返回的是上一个结果(字符串)的第一个字符。 

# 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, NoSuchElementException # import datetime # import time # import sys # import os # import glob # # # # ======================================== # # 能源系统登录类 # # ======================================== # 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, download_dir=r"D:\Class\能管比对\导出文件"): # self.driver = driver # self.wait_timeout = 10 # self.click_delay = 3 # 可根据需要调整点击后等待间 # self.download_dir = download_dir # 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: # # 匹配所有非临的 Excel 文件 # 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 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 # # # ================================ # # 🌟 所有预设的操作路径集中在这里 # # ================================ # OPERATIONS = { # "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": "//span[@data-v-4fe475b6 and @title='冷热水系统' and normalize-space()='冷热水系统']", "desc": "冷热水系统"}, # {"xpath": "//div[contains(@class, 'el-tree-node__content') and contains(@style, 'padding-left: 36px')]//span[contains(@title, '低温冰水系统')]", "desc": "低温冰水系统"}, # {"xpath": "//span[@class='el-radio-button__inner' and normalize-space()='下级分类']", "desc": "下级分类"}, # {"xpath": "//div[contains(@style, 'padding-left: 54px')]//span[contains(@title, '低温冰水制冷功率') and @data-v-4fe475b6]","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' and .//span[contains(text(), '低温冰水电功耗')]]","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.中温冰水制冷功率": [ # {"xpath": "//div[@class='el-tree-node__content' and .//span[contains(@title, '中温冰水系统')]]","desc": "中温冰水系统"}, # {"xpath": "//span[@title='中温冰水制冷功率']/ancestor::div[@class='el-tree-node__content']","desc": "中温冰水制冷功率"}, # {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, # {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, # ], # "4.中温冰水电功率": [ # {"xpath": "//span[@title='中温冰水电功率']/ancestor::div[@class='el-tree-node__content']","desc": "中温冰水电功率"}, # {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, # {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, # ], # # ##跳过高温热水 # # "5.低温热水系统": [ # {"xpath": "//div[@class='el-tree-node__content' and .//span[contains(@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": "导出"}, # ], # "6.冷却水系统": [ # {"xpath": "//div[@class='el-tree-node__content' and .//span[contains(@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": "导出"}, # ], # "7.冰机油泵耗电量": [ # {"xpath": "//div[@class='el-tree-node__content' and .//span[contains(@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": "导出"}, # ], # "8.低10a冷库耗电量": [ # {"xpath": "//div[@class='el-tree-node__content' and .//span[contains(@title, '10a冷库耗电量')]]","desc": "10a冷库耗电量"}, # {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, # {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, # ], # } # # def navigate(self, task_key): # """ # 根据 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 # # # === 导出完成后处理文件名 === # expected_filename = self.task_file_mapping.get(task_key, f"{task_key}.xlsx") # self.log_action("正在等待导出文件生成...") # # if self.rename_latest_file(expected_filename): # self.log_action(f"✅ 任务 '{task_key}' 成功,文件已保存为: {expected_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 = login.get_driver() # navigator = MenuNavigator(driver) # # # ✅ 自动获取所有定义在 OPERATIONS 中的任务 # tasks_to_run = list(navigator.OPERATIONS.keys()) # # print(f"📋 共发现 {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): # print(f"✅ 任务 '{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("🔚 程序结束") 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, NoSuchElementException import datetime import time import sys import os import glob import re # ======================================== # 能源系统登录类 # ======================================== 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, download_dir=r"D:\Class\能管比对\导出文件"): self.driver = driver self.wait_timeout = 10 self.click_delay = 3 self.download_dir = download_dir 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): """ 从 task_key 中提取文件名,去掉前面的编号(如 "1.", "8.") 示例: "1.低温冰水制冷功率" -> "低温冰水制冷功率.xlsx" """ # 移除开头的数字加点格式,例如 "1.", "10." match = re.match(r'^\d+\.\s*(.+)$', task_key.strip()) if match: return match.group(1) + ".xlsx" else: return task_key + ".xlsx" # fallback 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 # ================================ # 🌟 所有预设的操作路径集中在这里 # ================================ OPERATIONS = { "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": "//span[@data-v-4fe475b6 and @title='冷热水系统' and normalize-space()='冷热水系统']", "desc": "冷热水系统"}, {"xpath": "//div[contains(@class, 'el-tree-node__content') and contains(@style, 'padding-left: 36px')]//span[contains(@title, '低温冰水系统')]", "desc": "低温冰水系统"}, {"xpath": "//span[@class='el-radio-button__inner' and normalize-space()='下级分类']", "desc": "下级分类"}, {"xpath": "//div[contains(@style, 'padding-left: 54px')]//span[contains(@title, '低温冰水制冷功率') and @data-v-4fe475b6]", "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' and .//span[contains(text(), '低温冰水电功耗')]]", "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.中温冰水制冷功率": [ {"xpath": "//div[@class='el-tree-node__content' and .//span[contains(@title, '中温冰水系统')]]", "desc": "中温冰水系统"}, {"xpath": "//span[@title='中温冰水制冷功率']/ancestor::div[@class='el-tree-node__content']", "desc": "中温冰水制冷功率"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], "4.中温冰水电功率": [ {"xpath": "//span[@title='中温冰水电功率']/ancestor::div[@class='el-tree-node__content']", "desc": "中温冰水电功率"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], "5.低温热水系统": [ {"xpath": "//div[@class='el-tree-node__content' and .//span[contains(@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": "导出"}, ], "6.冷却水系统": [ {"xpath": "//div[@class='el-tree-node__content' and .//span[contains(@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": "导出"}, ], "7.冰机油泵耗电量": [ {"xpath": "//div[@class='el-tree-node__content' and .//span[contains(@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": "导出"}, ], "8.低10a冷库耗电量": [ {"xpath": "//div[@class='el-tree-node__content' and .//span[contains(@title, '10a冷库耗电量')]]", "desc": "10a冷库耗电量"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '查 询')]", "desc": "查询"}, {"xpath": "//button[contains(@class, 'el-button') and contains(span/text(), '导出Excel')]", "desc": "导出"}, ], } def navigate(self, task_key): """ 根据 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 # === 自动生成文件名(去编号前缀)=== 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 = login.get_driver() navigator = MenuNavigator(driver) tasks_to_run = list(navigator.OPERATIONS.keys()) print(f"📋 共发现 {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): print(f"✅ 任务 '{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("🔚 程序结束") ——auto_filename对应代码中的什么,详细说明逻辑
10-31
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值