找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
热搜: 活动 交友 discuz
查看: 16|回复: 1

[Python 原创] 多个Excel文件中搜索关键字的工具

[复制链接]

3

主题

1

回帖

11

积分

新手上路

积分
11
发表于 2025-3-29 10:18:51 | 显示全部楼层 |阅读模式
工作需要,因为是重复工作就写了个工具


    一个用于在多个Excel文件中搜索关键字的工具。
    可以选择多个Excel文件,搜索包含特定关键字的行,
    并按文件名分组显示结果的execl行数据。


发现还挺多人看的 初入萌新很激动呐 感谢大家支持 虽然打包的挺大 但很多人有需求就给大家放个exe

本来是考虑把标题都展示的 不用回头看excel文件了,然后发现很多可能标题行在第二行,动态适应查找比较麻烦
所以目前就是显示的行 用于快速定位位置 具体内容还是在具体文件中看吧

蓝奏云:https://wwl.lanzoum.com/iAt2w2r0dgyd
密码:52pj


[Python] [color=rgb(51, 102, 153) !important]纯文本查看 [color=rgb(51, 102, 153) !important]复制代码


  1. import os
  2. import tkinter as tk
  3. from tkinter import filedialog, ttk
  4. import pandas as pd
  5. from pathlib import Path

  6. class ExcelSearchTool:
  7.     """
  8.     一个用于在多个Excel文件中搜索关键字的工具。
  9.     可以选择多个Excel文件,搜索包含特定关键字的行,
  10.     并按文件名分组显示结果。
  11.     """
  12.      
  13.     def __init__(self, root):
  14.         """
  15.         初始化Excel搜索工具界面
  16.          
  17.         @Param {tk.Tk} root - tkinter根窗口对象
  18.         """
  19.         self.root = root
  20.         self.root.title("Excel多文件搜索工具")
  21.         self.root.geometry("1200x800")  # 增加窗口大小
  22.          
  23.         # 设置全局样式
  24.         style = ttk.Style()
  25.         style.configure("Treeview", font=('微软雅黑', 10))  # 设置树形视图字体
  26.         style.configure("Treeview.Heading", font=('微软雅黑', 10, 'bold'))  # 设置表头字体
  27.         style.configure("TLabelframe.Label", font=('微软雅黑', 10, 'bold'))  # 设置LabelFrame标题字体
  28.         style.configure("TButton", font=('微软雅黑', 10))  # 设置按钮字体
  29.          
  30.         self.selected_files = []
  31.         self.create_widgets()
  32.          
  33.     def create_widgets(self):
  34.         """
  35.         创建并配置界面组件
  36.         """
  37.         # 主容器
  38.         main_container = ttk.Frame(self.root, padding="10")
  39.         main_container.pack(fill="both", expand=True)
  40.          
  41.         # 文件选择框架
  42.         file_frame = ttk.LabelFrame(main_container, text="选择Excel文件", padding="10")
  43.         file_frame.pack(fill="x", pady=(0, 10))
  44.          
  45.         self.file_label = ttk.Label(file_frame, text="尚未选择文件", font=('微软雅黑', 10))
  46.         self.file_label.pack(side="left", padx=(5, 10), fill="x", expand=True)
  47.          
  48.         select_btn = ttk.Button(file_frame, text="选择文件", command=self.select_files, width=15)
  49.         select_btn.pack(side="right", padx=5)
  50.          
  51.         # 搜索框架
  52.         search_frame = ttk.LabelFrame(main_container, text="搜索内容", padding="10")
  53.         search_frame.pack(fill="x", pady=(0, 10))
  54.          
  55.         ttk.Label(search_frame, text="关键字:", font=('微软雅黑', 10)).pack(side="left", padx=(5, 10))
  56.          
  57.         self.search_entry = ttk.Entry(search_frame, width=50, font=('微软雅黑', 10))
  58.         self.search_entry.pack(side="left", padx=(0, 10), fill="x", expand=True)
  59.          
  60.         search_btn = ttk.Button(search_frame, text="搜索", command=self.search_keyword, width=15)
  61.         search_btn.pack(side="left")
  62.          
  63.         # 结果显示框架
  64.         result_frame = ttk.LabelFrame(main_container, text="搜索结果", padding="10")
  65.         result_frame.pack(fill="both", expand=True)
  66.          
  67.         # 创建带滚动条的框架
  68.         tree_frame = ttk.Frame(result_frame)
  69.         tree_frame.pack(fill="both", expand=True)
  70.          
  71.         # 创建Treeview显示结果
  72.         self.result_tree = ttk.Treeview(tree_frame, show="headings", selectmode="browse")
  73.         self.result_tree["columns"] = ("file", "sheet", "row", "data")
  74.          
  75.         # 配置列
  76.         self.result_tree.column("file", width=200, anchor="w")
  77.         self.result_tree.column("sheet", width=100, anchor="w")
  78.         self.result_tree.column("row", width=80, anchor="center")
  79.         self.result_tree.column("data", width=800, anchor="w")
  80.          
  81.         # 配置表头
  82.         self.result_tree.heading("file", text="文件名", anchor="w")
  83.         self.result_tree.heading("sheet", text="工作表", anchor="w")
  84.         self.result_tree.heading("row", text="行号", anchor="center")
  85.         self.result_tree.heading("data", text="数据内容", anchor="w")
  86.          
  87.         # 添加垂直滚动条
  88.         scrollbar_y = ttk.Scrollbar(tree_frame, orient="vertical", command=self.result_tree.yview)
  89.         self.result_tree.configure(yscrollcommand=scrollbar_y.set)
  90.          
  91.         # 添加水平滚动条
  92.         scrollbar_x = ttk.Scrollbar(tree_frame, orient="horizontal", command=self.result_tree.xview)
  93.         self.result_tree.configure(xscrollcommand=scrollbar_x.set)
  94.          
  95.         # 布局滚动条和树形视图
  96.         self.result_tree.grid(row=0, column=0, sticky="nsew")
  97.         scrollbar_y.grid(row=0, column=1, sticky="ns")
  98.         scrollbar_x.grid(row=1, column=0, sticky="ew")
  99.          
  100.         # 配置grid权重
  101.         tree_frame.grid_rowconfigure(0, weight=1)
  102.         tree_frame.grid_columnconfigure(0, weight=1)
  103.          
  104.         # 添加进度框架
  105.         self.progress_frame = ttk.LabelFrame(main_container, text="查询进度", padding="10")
  106.         self.progress_frame.pack(fill="x", pady=(0, 10))
  107.          
  108.         self.progress_label = ttk.Label(self.progress_frame, text="", font=('微软雅黑', 10))
  109.         self.progress_label.pack(fill="x", padx=5)
  110.          
  111.         self.progress_bar = ttk.Progressbar(self.progress_frame, mode='determinate')
  112.         self.progress_bar.pack(fill="x", padx=5, pady=(5, 0))
  113.          
  114.         # 状态栏
  115.         self.status_var = tk.StringVar()
  116.         self.status_var.set("就绪")
  117.         status_bar = ttk.Label(
  118.             main_container,
  119.             textvariable=self.status_var,
  120.             relief="sunken",
  121.             anchor="w",
  122.             font=('微软雅黑', 9),
  123.             padding=(5, 2)
  124.         )
  125.         status_bar.pack(fill="x", pady=(10, 0))
  126.          
  127.     def select_files(self):
  128.         """
  129.         打开文件选择对话框,允许用户选择多个Excel文件
  130.         """
  131.         filetypes = [("Excel 文件", "*.xlsx;*.xls")]
  132.         files = filedialog.askopenfilenames(filetypes=filetypes)
  133.          
  134.         if files:
  135.             self.selected_files = files
  136.             if len(files) > 1:
  137.                 self.file_label.config(text=f"已选择 {len(files)} 个文件")
  138.             else:
  139.                 self.file_label.config(text=f"已选择: {Path(files[0]).name}")
  140.             self.status_var.set(f"已选择 {len(files)} 个Excel文件")
  141.          
  142.     def search_keyword(self):
  143.         """
  144.         在选定的Excel文件中搜索关键字,并在Treeview中显示结果
  145.         """
  146.         # 清除现有的搜索结果
  147.         for item in self.result_tree.get_children():
  148.             self.result_tree.delete(item)
  149.             
  150.         keyword = self.search_entry.get().strip()
  151.         if not keyword:
  152.             self.status_var.set("错误: 请输入搜索关键字")
  153.             return
  154.             
  155.         if not self.selected_files:
  156.             self.status_var.set("错误: 请先选择Excel文件")
  157.             return
  158.             
  159.         # 初始化进度条
  160.         total_files = len(self.selected_files)
  161.         self.progress_bar["maximum"] = total_files
  162.         self.progress_bar["value"] = 0
  163.          
  164.         found_count = 0
  165.         error_files = []
  166.         empty_files = []
  167.          
  168.         try:
  169.             for file_idx, file_path in enumerate(self.selected_files, 1):
  170.                 file_name = Path(file_path).name
  171.                 self.progress_label.config(text=f"正在搜索: {file_name} ({file_idx}/{total_files})")
  172.                 self.progress_bar["value"] = file_idx
  173.                 self.root.update()
  174.                  
  175.                 try:
  176.                     excel = pd.ExcelFile(file_path)
  177.                     file_found = False
  178.                      
  179.                     for sheet_name in excel.sheet_names:
  180.                         try:
  181.                             df = pd.read_excel(file_path, sheet_name=sheet_name)
  182.                             sheet_found = False
  183.                              
  184.                             for row_idx, row in df.iterrows():
  185.                                 row_found = False
  186.                                 row_values = []
  187.                                  
  188.                                 for col_name, value in row.items():
  189.                                     if isinstance(value, (str, int, float)) and str(keyword).lower() in str(value).lower():
  190.                                         row_found = True
  191.                                         row_values = [str(v) for v in row.values]
  192.                                         break
  193.                                  
  194.                                 if row_found:
  195.                                     tag = 'even' if found_count % 2 == 0 else 'odd'
  196.                                     values = (
  197.                                         file_name,
  198.                                         sheet_name,
  199.                                         f"第{row_idx+1}行",
  200.                                         " | ".join(row_values)
  201.                                     )
  202.                                     self.result_tree.insert("", "end", values=values, tags=(tag,))
  203.                                     found_count += 1
  204.                                     file_found = True
  205.                                     sheet_found = True
  206.                         
  207.                         except Exception as sheet_error:
  208.                             error_files.append(f"{file_name} (工作表: {sheet_name})")
  209.                      
  210.                     if not file_found:
  211.                         empty_files.append(file_name)
  212.                      
  213.                 except Exception as file_error:
  214.                     error_files.append(file_name)
  215.                  
  216.                 self.root.update()
  217.          
  218.             # 设置交替行的背景色
  219.             self.result_tree.tag_configure('odd', background='#F5F5F5')
  220.             self.result_tree.tag_configure('even', background='white')
  221.             
  222.             # 构建详细的状态信息
  223.             status_messages = []
  224.             if found_count > 0:
  225.                 status_messages.append(f"找到 {found_count} 个匹配项")
  226.             if empty_files:
  227.                 status_messages.append(f"{len(empty_files)} 个文件未找到匹配内容")
  228.             if error_files:
  229.                 status_messages.append(f"{len(error_files)} 个文件出现错误")
  230.             
  231.             status_text = " | ".join(status_messages)
  232.             self.status_var.set(f"搜索完成 - {status_text}")
  233.             
  234.             # 更新进度标签显示详细结果
  235.             result_details = []
  236.             if empty_files:
  237.                 result_details.append(f"未找到匹配的文件:{', '.join(empty_files)}")
  238.             if error_files:
  239.                 result_details.append(f"搜索出错的文件:{', '.join(error_files)}")
  240.             
  241.             self.progress_label.config(text="\n".join(result_details) if result_details else "搜索完成")
  242.             
  243.         except Exception as e:
  244.             self.status_var.set(f"搜索过程出现错误: {str(e)}")
  245.         finally:
  246.             self.progress_bar["value"] = self.progress_bar["maximum"]

  247. def main():
  248.     """
  249.     程序入口点
  250.     """
  251.     root = tk.Tk()
  252.     app = ExcelSearchTool(root)
  253.     root.mainloop()

  254. if __name__ == "__main__":
  255.     main()
复制代码
软件界面

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×

2

主题

1

回帖

8

积分

新手上路

积分
8
发表于 2025-3-29 10:20:36 | 显示全部楼层
打包试了下,Python打包体积还是挺大的,33.2MB,打开速度也不太快需要4-6秒。
另外试了下用UPX压缩后26.8MB,但打开速度太慢了需要10-15秒。就不上传了。
https://wwse.lanzoub.com/iqda72r06tej
密码:8yqq


试了下:
1、基本单元格搜索还是可以的(见图示2),但不支持搜索【注释】和【批注】内容,希望楼主能优化支持。
2、发现了个【搜索n字符bug】小问题,详见图示1




本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|一起港湾 ( 青ICP备2025004122号-1 )

GMT+8, 2025-4-8 04:05 , Processed in 0.086006 second(s), 21 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表