最近遇到一个“史诗级”的难题:

现有8个班400余名学生的体质健康登记卡,分别保存在8个word文档中。现在需要把这些学生的个人信息及体测成绩汇总到一个excel表格中去。
挑战在于,男生和女生测试的项目数量一样,个别内容却不一样。仅男生体测的项目有引体向上和1000米跑,仅女生体测的项目为一分钟仰卧起坐和800跑。男生和女生的登记卡样式如下:


如果一个一个地打开word,找到对应的数据,再逐项复制粘贴到excel里,对人的耐心简直是终极考验,不仅让人眼花缭乱,而且还很容易出错。一个学生按1分钟计算,还要400多分钟,七八个小时,想想都能让人头皮发麻。
这种重复性的、规则明确的数据搬运工作,正是Python的强项。
技术实现:代码实现的思路
仔细观察:每个学生由三个表格组成。第一个表格为个人基本信息,第二个表格为学生成绩,第三个表格为学校签章。需要汇总的信息在第一个和第二个表格中,按图索骥,只要找到规律,问题便能迎刃而解。
核心代码三大步骤:
1、精准定位,提取数据
from docx import Documentfrom openpyxl import load_workbookimport timefrom pathlib import Path start_time = time.time()path = Path(r"E:\学生体育成绩汇总")path_xlsx = r"E:\学生体育成绩汇总\体测成绩导入模板.xlsx"student_dict = {}docxs = path.glob("*.doc*")for path_docx in docxs: docx = Document(path_docx) tables = docx.tables for i in range(0,len(tables),3): #每个学生由三个表组成,依次循环 id = tables[i].rows[0].cells[5].text.strip() table2 = tables[i + 1] # 表2第1-4行代码:导入所需的模块,模块也可以称为库或包。docx用于读取word文件,openpyxl用于把数据写入excel表格。pathlib用于处理文件路径。第9行代码:student_dict = {}新建一个空字典,用于保存提取的数据。第10行代码:path.glob("*.doc*")可获取path路径下所有doc和docx格式的文档。第13行代码:docx.tables可获取docx文件中的所有表格。第15行代码:for i in range(0,len(tables),3):每三个表格为一组,依次遍历每组表格。第16行代码:tables[i].rows[0].cells[5].text.strip()取得当前组第一个表格tables[i]的第一行rows[0]的第六列cells[5]的文本去除前后空格的值,即当前学生的学号,赋值给id。第17行代码:table2 = tables[i + 1]:获取第二个表格。
2、智能映射,写入字典。
student_dict[id] = { "姓名":tables[i].rows[0].cells[1].text, "性别":tables[i].rows[0].cells[3].text, "班级":tables[i].rows[1].cells[1].text, "民族":tables[i].rows[1].cells[3].text, "出生日期":tables[i].rows[1].cells[5].text, "标准分": table2.rows[9].cells[7].text, "加分指标-引体向上/一分钟仰卧起坐附加分": table2.rows[11].cells[8].text, "加分指标-1000米/800米跑附加分": table2.rows[12].cells[8].text, "学年总分": table2.rows[13].cells[7].text, "等级评定": table2.rows[14].cells[7].text } for row_index in range(2, 9): row = table2.rows[row_index] xm = row.cells[0].text.split("(")[0].strip() student_dict[id][f"{xm}成绩"] = row.cells[7].text.strip() student_dict[id][f"{xm}得分"] = row.cells[8].text.strip() student_dict[id][f"{xm}等级"] = row.cells[9].text.strip()第1-12行:把学生固定信息写到嵌套字典中。rows[0]代表指定的行,cells[1]代表指定的单元格。方括号中的索引从0开始。第14行:for index in range(2,9):获取表2第3行(索引为2)到第9行(索引为8)的行号。第16行:xm=row.cells[0].text.split("(")[0].strip()首先获取指定位置的文本,然后以“(”为分割符进行分割,取第1个值后再去掉前后的空格。这样做的原因是因为原内容与excel模板的表不一致,分割后确保内容相同。第18-20行:xm与成绩、得分、等级合并组成新的键后再取值写入字典。
3、批量写入,一气呵成
wb = load_workbook(path_xlsx)ws = wb["体测成绩"]row = 1for key in student_dict.keys(): row += 1 data = student_dict[key] ws.cell(row,1).value = data["班级"][2:6] ws.cell(row,2).value = data["班级"] ws.cell(row,3).value = key for col in range(4,ws.max_column+1): k = ws.cell(1, col).value.split("(")[0] if k in data: ws.cell(row,col).value = data[k]wb.save(path_xlsx)end_time = time.time()print(f"班级数量:{len(list(docxs))}个")print(f"学生人数:{len(student_dict)}名")print(f"共用时{end_time-start_time:.2f}秒")第4行:for key in student_dict.keys()遍历字典中所有的键,key即学籍号。第11行:for col in range(4,ws.max_column+1)遍历第4列到最后一列。第12行: ws.cell(1, col).value.split("(")[0]表示对第一行的数值进行分割后取每一个值,目的是让这个值与字典的键保持一致,确保数据对应,避免出错。第13行:if k in data因为男生与女生的项目不一样,进行判断避免代码运行出错。第15行:wb.save(path_xlsx)保存工作簿,这里别指定路径,相当于另存为,避免覆盖原文档。第17-19行:显示运行结果及时长。
如果代码运行到这里,说明所有的数据行云流水般写入到了excel的指定位置。
原本需要七八个小时的工作,现在不到一分钟就完成了。运行结果如下:



以上代码的运行思路类似于邮件合并的反操作,不仅适用于健康登记卡的整理,还适合于学生档案整理、问卷调查结果汇总及任何结构化文档的数据提取等。
正所谓举一反三、触类旁通,只有掌握核心思路,类似的问题都会迎刃而解。
如果这篇文章对你有所帮助和启发,还请点赞+转发支持!关注我,学*更多的自动化技巧,让你从重复劳动中解放出来~~~
版权声明:本文转载于今日头条,版权归作者所有,如果侵权,请联系本站编辑删除
