使用Python与OpenPyXL处理Excel重复行的数值合并及去重需求
后续开发指导与代码优化建议
一、后续代码编写方向
当前代码完成了数据分类和重复行标记,但核心的重复行数据合并+重复行删除逻辑还未实现,可按以下步骤推进:
1. 重构数据存储逻辑,关联姓名与对应行数据
放弃当前单纯用数组分类的方式,改用字典存储每个姓名对应的所有行信息:
- 定义字典(如
name_rows_map),key为姓名,value为该姓名的所有行对象列表(包含首次行和所有重复行) - 遍历工作表时,直接将每行数据按姓名分组存入字典,同时记录首次出现的行作为主行
2. 实现手机号与邮箱的合并规则
遍历name_rows_map中的每个姓名组:
- 取出该组的首行作为主行,其余为重复行
- 处理手机号:
- 先判断主行手机号是否为空,或主行手机号属于免费号码(用你已有的区号判断逻辑)
- 若满足条件,遍历重复行,找到第一个非免费的手机号,替换主行的手机号单元格
- 处理邮箱:
- 判断主行邮箱是否为空,或主行邮箱属于非个人邮箱(不在你的
emailDomains列表中) - 若满足条件,遍历重复行,找到第一个个人邮箱,替换主行的邮箱单元格
- 判断主行邮箱是否为空,或主行邮箱属于非个人邮箱(不在你的
- 注:如果有多个重复行符合条件,可选择保留最优的那个(比如第一个符合的,或所有符合的用逗号拼接,根据需求调整)
3. 统一删除重复行
收集所有重复行的行号,从大到小排序后逐个删除(因为openpyxl删除行后,后续行的行号会前移,从下往上删可避免行号混乱)
- 示例:
unwanted_rows.sort(reverse=True),然后遍历行号执行worksheet.delete_rows(row_num)
4. 保存处理后的工作簿
最后调用workBook.save("处理后的文件名.xlsx"),建议保存为新文件,避免覆盖原数据。
二、代码优化建议
针对你已完成的代码,可从以下方面优化:
1. 变量与命名规范
- 常量用大写命名:比如
FREE_AREA_CODES = [888, 877, 866, 855, 844, 833, 800],PERSONAL_EMAIL_DOMAINS = ["@gmail", "@yahoo", "@ymail", "@outlook", "@hotmail", "@aol", "@msn", "@me."] - 变量名用小驼峰或下划线:比如
work_sheet替代workSheet,file_name替代filename,提升可读性 - 避免错误的空列表初始化:
unwantedRows, rows, nonPrefNum, prefNum, nonPrefEmail, prefEmail = []会报错,应逐个初始化:unwanted_rows = []、seen_names = set()等
2. 函数单一职责优化
把当前的批量分类函数改成单个数据的判断函数,复用性更强:
import re def is_free_phone_number(phone_str): if not phone_str: return False # 去除所有非数字字符 clean_num = re.sub(r'\D', '', str(phone_str)) # 判断区号是否在免费列表中 if len(clean_num) >= 3: area_code = clean_num[:3] return area_code in [str(code) for code in FREE_AREA_CODES] return False def is_personal_email(email_str): if not email_str: return False email_lower = str(email_str).lower() return any(domain in email_lower for domain in PERSONAL_EMAIL_DOMAINS)
3. 提升遍历效率
- 当前三个函数分别遍历工作表,数千行数据会重复遍历3次,改成一次遍历完成所有数据收集与标记,减少IO开销
indexDuplicates中用列表判断subList not in rows的时间复杂度是O(n),改用集合seen_names = set(),判断姓名是否已存在的时间复杂度为O(1)
4. 空值与异常处理
- 处理单元格空值:比如
row[10].value可能为None,直接转字符串会得到"None",需先判断:phone = row[10].value if row[10].value else "" - 增加文件加载异常捕获:比如工作表名称错误、文件不存在的情况:
try: work_book = load_workbook(filename=file_name) work_sheet = work_book["Worksheet Name"] except FileNotFoundError: print(f"[-] 文件 {file_name} 不存在") sys.exit(1) except KeyError: print("[-] 工作表 'Worksheet Name' 不存在") sys.exit(1)
5. 手机号处理简化
用正则表达式替换所有非数字字符,替代逐个字符替换:
clean_num = re.sub(r'\D', '', str(phone_str)) # 去除所有非数字字符
调整后的核心代码示例(部分)
def process_duplicates(): name_rows_map = {} # 一次遍历完成行分组 for row in work_sheet.iter_rows(min_row=2): # 假设第一行是表头 name = row[0].value if not name: continue if name not in name_rows_map: name_rows_map[name] = [] name_rows_map[name].append(row) unwanted_rows = [] # 处理每个姓名的行组 for name, rows in name_rows_map.items(): if len(rows) <= 1: continue main_row = rows[0] # 处理手机号 main_phone = main_row[10].value if not main_phone or is_free_phone_number(main_phone): for duplicate_row in rows[1:]: dup_phone = duplicate_row[10].value if dup_phone and not is_free_phone_number(dup_phone): main_row[10].value = dup_phone break # 找到第一个符合的就停止,可根据需求调整 # 处理邮箱 main_email = main_row[9].value if not main_email or not is_personal_email(main_email): for duplicate_row in rows[1:]: dup_email = duplicate_row[9].value if dup_email and is_personal_email(dup_email): main_row[9].value = dup_email break # 收集重复行的行号(注意行号是row[0].row) unwanted_rows.extend([row[0].row for row in rows[1:]]) # 从下往上删除重复行 unwanted_rows = sorted(list(set(unwanted_rows)), reverse=True) for row_num in unwanted_rows: work_sheet.delete_rows(row_num) # 保存文件 work_book.save(f"processed_{file_name}")
内容的提问来源于stack exchange,提问作者Throwaway account
相关产品推荐
相关产品推荐

