社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

Python私活300元:提取PDF的Excel表格

蚂蚁学Python • 1 年前 • 270 次点击  

在群里看到老师发这个需求时,我之前就写过提取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         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                     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带副业全套餐有优惠!!!


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/148265
 
270 次点击