您的位置:首页 >聚焦 >

世界球精选!Python私活300元:提取PDF的Excel表格

2022-10-21 16:01:17    来源:程序员客栈

在群里看到老师发这个需求时,我之前就写过提取pdf的单子,所以没有犹豫,立马接了下来


(资料图)

一 需求分析

单单提取pdf中的表格数据还是很简单的,使用pdfplumber这个库,就可以很简单的实现,提取后保存到excel中,再使用pandas处理数据,最后通过openpyxl设置表格样式,难点在于人民币金额自动转大写,这个我开始也没有思路,最后在度娘那找到了解决方案

二 解决步骤

实现人民币金额自动转大写

importpdfplumber,openpyxlimportpandasaspdfrompathlibimportPathfromopenpyxlimportload_workbookfromopenpyxl.stylesimportFont,PatternFill,Border,Side,Alignmentdefnumtomoney(money):cnNums=["零","壹","贰","叁","肆","伍","陆","柒","捌","玖"]#汉字的数字cnIntRadice=["","拾","佰","仟"]#基本单位cnIntUnits=["","万","亿","兆"]#对应整数部分扩展单位cnDecUnits=["角","分","毫","厘"]#对应小数部分单位cnInteger="整"#整数金额时后面跟的字符cnIntLast="元"#整型完以后的单位maxNum=999999999999999.9999#最大处理的数字#IntegerNum金额整数部分#DecimalNum金额小数部分ChineseStr=""#输出的中文金额字符串parts=[]#分离金额后用的数组,预定义Symbol=""#正负值标记ifmoney=="":return""money=float(money)ifmoney>=maxNum:return"超出最大范围"ifmoney==0:ChineseStr=cnNums[0]+cnIntLast+cnIntegerreturnChineseStrifmoney<0:money=-moneySymbol="负"money=str(money)#转换为字符串ifmoney.find(".")==-1:IntegerNum=moneyDecimalNum=""else:parts=money.split(".")IntegerNum=parts[0]DecimalNum=parts[1][0:4]ifint(IntegerNum)>0:#获取整型部分转换zeroCount=0IntLen=len(IntegerNum)foriinrange(0,IntLen):n=IntegerNum[i]#整数部分字符串的第i个字符p=IntLen-i-1q=p//4#地板除,p<4时,为0,4>p<8时,等于1,等于8时,等于2m=p%4#求余数,p<4时,为p,=4时为0ifn=="0":zeroCount+=1else:ifzeroCount>0:ChineseStr+=cnNums[0]zeroCount=0#归零ChineseStr+=cnNums[int(n)]+cnIntRadice[m]ifm==0andzeroCount<4:ChineseStr+=cnIntUnits[q]ChineseStr+=cnIntLast#整型部分处理完毕ifDecimalNum!="":#小数部分decLen=len(DecimalNum)foriinrange(0,decLen):n=DecimalNum[i]ifn!="0":ChineseStr+=cnNums[int(n)]+cnDecUnits[i]ifChineseStr=="":ChineseStr+=cnNums[0]+cnIntLast+cnIntegerelifDecimalNum=="0":ChineseStr+=cnIntegerChineseStr=Symbol+ChineseStrreturnChineseStrnumtomoney(5648.89)

数据提取和数据处理部分

defget_data(file_path):pdf_path=Path(file_path)pdf_files=pdf_path.glob("*.pdf")forpdf_fileinpdf_files:df_list=[]pdf=pdfplumber.open(path_or_fp=pdf_file)forpageinpdf.pages:table=page.extract_table()#提取page对象中的表格数据为列表df=pd.DataFrame(table)#把列表数据存入DataFrame中df_list.append(df)df_total=pd.concat(df_list)#合并表格df_total=df_total.reset_index(drop=True)#重置索引,并删除原索引df_total.columns=df_total.iloc[0,:].tolist()#用第一行作为表头df_total.drop(index=0,inplace=True)#删除第一行df_total=df_total.reindex(columns=df_total.columns[[0,1,3,2,6,5,7,4]])#调整列顺序df_total.drop(columns=["配置要求"],inplace=True)#删除配置要求列df_total.columns=["序号","名称","规格及型号","品牌","单位","数量","单价(元)"]df_total["数量"]=pd.to_numeric(df_total["数量"],errors="coerce")df_total["单价(元)"]=pd.to_numeric(df_total["单价(元)"],errors="coerce")df_total["合价(元)"]=df_total["数量"]*df_total["单价(元)"]df_total["存放位置"]=pd.NAdf_total.drop(index=df_total.index.tolist()[-2:],inplace=True)#删除最后两行数据df_total=df_total.applymap(lambdax:x.replace("\n","")ifisinstance(x,str)elsex)#去除单元格中的换行total=df_total["合价(元)"].sum(axis=0)money=numtomoney(total)row_num=df_total.shape[0]df_total.loc[row_num+2,"序号"]=f"{money}(¥{"%.2f"%total})"#2位小数df_total.loc[row_num+3,"序号"]="采购申请人签字:"df_total.loc[row_num+4,"序号"]="实训室管理员签字:"p=Path(Path.cwd())p1=p/"已提取文件"ifnotp1.exists():#判断文件夹是否存在,不存在就新建文件夹p1.mkdir(exist_ok=True)df_total.to_excel(f"./已提取文件/{pdf_file.stem}.xlsx",index=False)

设置表格样式

defmodify_style(wb,first_row_value,second_row_value,place,file_name):"""first_row_value第一行标题内容second_row_value第二行标题内容place,file表格中"存放位置"列内容file_name最终保存的文件名称"""forsheet_nameinwb.sheetnames:ws=wb[sheet_name]ws.insert_rows(1,2)#在第一行前面插入2行空行alignment=Alignment(horizontal="center",vertical="center",wrap_text=True)ws["A1"].value=first_row_valuews["A1"].font=Font(name="宋体",size=16,bold=True,color="FF000000")ws["A1"].alignment=alignmentws["A2"].value=second_row_valuews["A2"].font=Font(name="宋体",size=11,bold=True,color="FF000000")ws["A2"].alignment=alignmentws.merge_cells("A1:I1")#合并单元格ws.merge_cells("A2:I2")maxrows=ws.max_row#获取最大行ws.column_dimensions["B"].width=24#设置B列宽度为15forcolin["C","D","G","H"]:ws.column_dimensions[col].width=12#批量设置指定列宽度为12forcolin["A","E","F","I"]:ws.column_dimensions[col].width=4.5#批量设置指定列宽度为4.5foriinrange(3,maxrows+1):cells=ws[i]font=Font(name="宋体",size=11,bold=False,italic=False,color="FF000000")alignment=Alignment(horizontal="center",vertical="center",wrap_text=True)#先定好side的格式side_left=Side(style="thin",color="FF000000")side_right=Side(style="thin",color="FF000000")#代入边线中border=Border(left=side_left,right=side_right,top=side_right,bottom=side_left)forcellincells:cell.font=fontcell.alignment=alignmentcell.border=borderws["A1"].font=Font(size=16,bold=True)ws["A2"].font=Font(bold=True)ws["A2"].alignment=Alignment(horizontal="center",vertical="center",wrap_text=True)ws.row_dimensions[2].height=32#设置第二行行高ws.row_dimensions[maxrows-1].height=28#设置倒数第二行行高ws.row_dimensions[maxrows].height=28#设置倒数第一行行高foriinrange(3):ws.merge_cells(f"A{maxrows-2+i}:H{maxrows-2+i}")ws[f"A{maxrows-2+i}"].alignment=Alignment(horizontal="left",vertical="center",wrap_text=True)ws["I4"].value=placeforcolin["G","H"]:#设置金钱符号foriinrange(4,maxrows+1):ws[f"{col}{i}"].number_format="¥#,##0.00;¥-#,##0.00"ws.merge_cells(f"I4:I{maxrows}")wb.save("已完成-"+f"{file_name}.xlsx")file_path=Path(Path.cwd()/"已提取文件")files=file_path.glob("*.xlsx")forfileinfiles:wb=load_workbook(file)file_name=file.stemmodify_style(wb,"绍兴市柯桥区职业教育中心耗材入库单","项目名称:柯桥区职业教育中心建筑专业实训耗材采购项目\n入库时间:2022年9月29日","1号实训楼2楼库房",file_name)

三 最终结果 四 技术总结

代码编写过程遇到一个大坑,是表格合并时,index没有重置,导致表格有多个index为0和1的行,再删除第一行和合同价格汇总行时,会误删需要的行,这里推荐下老师的pandas课程,里面有很多干货,报名老师的课程,让我学到了很多技术,解决了许多日常工作中的问题,大大提高我的工作效率,让我的工作也轻松了不少,更重要的是,有了个门做兼职的技术!

五 答案地址

在本公众号《蚂蚁学Python》后台回复: 1015PDF代码

可以得到数据素材和代码答案

今晚来蚂蚁老师抖音直播间,Python带副业全套餐有优惠!!!

关键词: 整数部分 小数部分 标题内容

相关阅读