Python私活300元:提取PDF的Excel表格
蚂蚁学Python
共 14976字,需浏览 30分钟
· 2022-10-21
在群里看到老师发这个需求时,我之前就写过提取pdf的单子,所以没有犹豫,立马接了下来
一 需求分析
单单提取pdf中的表格数据还是很简单的,使用pdfplumber这个库,就可以很简单的实现,提取后保存到excel中,再使用pandas处理数据,最后通过openpyxl设置表格样式,难点在于人民币金额自动转大写,这个我开始也没有思路,最后在度娘那找到了解决方案
二 解决步骤
实现人民币金额自动转大写
import pdfplumber,openpyxl
import pandas as pd
from pathlib import Path
from openpyxl import load_workbook
from openpyxl.styles import Font, PatternFill, Border, Side, Alignment
def numtomoney(money):
cnNums = ["零","壹","贰","叁","肆","伍","陆","柒","捌","玖"] #汉字的数字
cnIntRadice = ["", "拾", "佰", "仟"] #基本单位
cnIntUnits = ["", "万", "亿", "兆"] #对应整数部分扩展单位
cnDecUnits = ["角", "分", "毫", "厘"] #对应小数部分单位
cnInteger = "整" #整数金额时后面跟的字符
cnIntLast = "元" #整型完以后的单位
maxNum = 999999999999999.9999 #最大处理的数字
# IntegerNum 金额整数部分
# DecimalNum 金额小数部分
ChineseStr = "" #输出的中文金额字符串
parts =[] #分离金额后用的数组,预定义
Symbol = "" #正负值标记
if money == "":
return ""
money = float(money)
if money >= maxNum:
return "超出最大范围"
if money == 0:
ChineseStr = cnNums[0] + cnIntLast + cnInteger
return ChineseStr
if money < 0:
money = -money
Symbol = "负 "
money = str(money) #转换为字符串
if money.find(".") == -1:
IntegerNum = money
DecimalNum = ""
else:
parts = money.split(".")
IntegerNum = parts[0]
DecimalNum = parts[1][0:4]
if int(IntegerNum) > 0:#获取整型部分转换
zeroCount = 0
IntLen = len(IntegerNum)
for i in range(0,IntLen):
n = IntegerNum[i]#整数部分字符串的第i个字符
p = IntLen - i - 1
q = p // 4#地板除,p<4时,为0,4>p<8时,等于1,等于8时,等于2
m = p % 4#求余数,p<4时,为p,=4时为0
if n == "0":
zeroCount +=1
else:
if zeroCount > 0:
ChineseStr += cnNums[0]
zeroCount = 0 #归零
ChineseStr += cnNums[int(n)] + cnIntRadice[m]
if m == 0 and zeroCount < 4:
ChineseStr += cnIntUnits[q]
ChineseStr += cnIntLast#整型部分处理完毕
if DecimalNum != "":#小数部分
decLen = len(DecimalNum)
for i in range(0,decLen):
n = DecimalNum[i]
if n != "0":
ChineseStr += cnNums[int(n)] + cnDecUnits[i]
if ChineseStr == "":
ChineseStr += cnNums[0] + cnIntLast + cnInteger
elif DecimalNum == "0":
ChineseStr += cnInteger
ChineseStr = Symbol + ChineseStr
return ChineseStr
numtomoney(5648.89)
数据提取和数据处理部分
def get_data(file_path):
pdf_path=Path(file_path)
pdf_files=pdf_path.glob('*.pdf')
for pdf_file in pdf_files:
df_list=[]
pdf = pdfplumber.open(path_or_fp=pdf_file)
for page in pdf.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.NA
df_total.drop(index=df_total.index.tolist()[-2:],inplace=True)#删除最后两行数据
df_total=df_total.applymap(lambda x : x.replace('\n','') if isinstance(x,str) else x)#去除单元格中的换行
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/'已提取文件'
if not p1.exists():#判断文件夹是否存在,不存在就新建文件夹
p1.mkdir(exist_ok=True)
df_total.to_excel(f'./已提取文件/{pdf_file.stem}.xlsx',index=False)
设置表格样式
def modify_style(wb,first_row_value,second_row_value,place,file_name):
'''
first_row_value 第一行标题内容
second_row_value 第二行标题内容
place,file 表格中'存放位置'列内容
file_name 最终保存的文件名称
'''
for sheet_name in wb.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_value
ws['A1'].font=Font(name='宋体', size=16, bold=True, color='FF000000')
ws['A1'].alignment=alignment
ws['A2'].value=second_row_value
ws['A2'].font=Font(name='宋体', size=11, bold=True, color='FF000000')
ws['A2'].alignment=alignment
ws.merge_cells('A1:I1')#合并单元格
ws.merge_cells('A2:I2')
maxrows = ws.max_row # 获取最大行
ws.column_dimensions["B"].width = 24 # 设置B列宽度为15
for col in ['C','D','G','H']:
ws.column_dimensions[col].width = 12 # 批量设置指定列宽度为12
for col in ['A','E','F','I']:
ws.column_dimensions[col].width = 4.5 # 批量设置指定列宽度为4.5
for i in range(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)
for cell in cells:
cell.font = font
cell.alignment = alignment
cell.border = border
ws['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#设置倒数第一行行高
for i in range(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=place
for col in ['G','H']:#设置金钱符号
for i in range(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')
for file in files:
wb=load_workbook(file)
file_name=file.stem
modify_style(wb,'绍兴市柯桥区职业教育中心耗材入库单','项目名称:柯桥区职业教育中心建筑专业实训耗材采购项目\n入库时间:2022年9月29日 ','1号实训楼2楼库房',file_name)
三 最终结果
四 技术总结
代码编写过程遇到一个大坑,是表格合并时,index没有重置,导致表格有多个index为0和1的行,再删除第一行和合同价格汇总行时,会误删需要的行,这里推荐下老师的pandas课程,里面有很多干货,报名老师的课程,让我学到了很多技术,解决了许多日常工作中的问题,大大提高我的工作效率,让我的工作也轻松了不少,更重要的是,有了个门做兼职的技术!
五 答案地址
在本公众号《蚂蚁学Python》后台回复: 1015PDF代码
可以得到数据素材和代码答案
今晚来蚂蚁老师抖音直播间,Python带副业全套餐有优惠!!!
评论
了解加密货币到加密货币的互换
1、什么是加密货币互换?加密货币到加密货币的互换是指以现行市场汇率将一种加密货币直接兑换为另一种加密货币。与需要法定货币存款和较长流程的传统交易所不同,加密货币到加密货币的互换可以无缝地促进交换。掉期在提高加密货币的流动性和效率方面发挥着重要作用。该功能使用户能够将他们的加密货币与钱包中的其他代币进
区块链头条
0
李彦宏:开源大模型不如闭源,后者会持续领先;周鸿祎:“开源不如闭源” 的言论是胡说八道
架构师大咖
架构师大咖,打造有价值的架构师交流平台。分享架构师干货、教程、课程、资讯。架构师大咖,每日推送。
公众号该公众号已被封禁0、李彦宏:开源大模型不如闭源,后者会持续领先当今
源码共读
0
【第129期】程序员的新宠:三款终端工具,让你告别Xshell!
概述 WindTerm:跨平台的SSH利器 首先介绍的是WindTerm,这是一款使用C语言开发的跨平台SSH客户端。它不仅完全免费,而且没有商业使用的限制。WindTerm支持SSH v2、Telnet、Raw Tcp等协议,而且性能出色,甚至超过了FinalShell和Electerm。功能
前端微服务
0
字节员工:35岁以后被裁员的,后来都走了哪条路?现在2-2,要不要利用最后一年拼命上个岸。
架构师大咖
架构师大咖,打造有价值的架构师交流平台。分享架构师干货、教程、课程、资讯。架构师大咖,每日推送。
公众号该公众号已被封禁在当今竞争激烈的职场环境中,年龄并不总是一个决定性
源码共读
0
上班的时候,有一群摸鱼搭子非常重要...
上班的时候,有一群摸鱼搭子非常重要!一到上班时间,他们就从四面八方涌进群里冒泡...从八卦聊到股市、从职场聊到乌X兰局势,偶尔还会复读、相亲、battle...然后,下午6点钟准时消失不见...所以你要不要加入我们一起摸鱼?我们有北京、上海、深圳、广州、杭州、武汉、成都、南京等8个城市的摸鱼群,还有
产品经理日记
0
周四002 瑞超:同样落寞的境遇——北雪平vs埃尔夫斯堡
上赛季最终排名联赛第9的北雪平本赛季伊始表现不佳,4轮战罢他们仅以1胜1平2负的战绩排在倒数第三,这支历史上曾夺得13次联赛冠军、6次杯赛冠军老牌劲旅,正如英格兰赛场上的一众百年俱乐部,在低谷中不断探索着出路。球队主教练安德烈亚斯·阿尔姆曾是AIK索尔纳及赫根队的主教练,他于今年年初刚刚拿起球队教鞭
产品与体验
0
雷军辟谣了!不是高考状元,卡里也没有冰冷的 40 亿
架构师大咖
架构师大咖,打造有价值的架构师交流平台。分享架构师干货、教程、课程、资讯。架构师大咖,每日推送。
公众号该公众号已被封禁最近很火的雷军简历,听说落魄时卡里只有冰冷的 40
源码共读
0
日本影山优佳最新杂志照,展现充满透明感的美丽
今天的图文分享的是影山优佳的杂志写真。元日向坂46的影山优佳,登上了写真杂志《周刊FLASH》5/7和5/14合并号的封面。影山优佳是日本艺人、女演员、前偶像。身高155厘米。2001年5月8日出生于东京都。2023年7月从组合日向坂46毕业,之后作为演员活跃的影山优佳,在《周刊FLAS
python教程
0