提取PDF发票信息的Python脚本

工作中常常需要把发票中的关键信息(发票号码, 开票日期, 价格等)整理成Excel表格,手工复制粘贴太慢,于是弄了个脚本来干。

这个脚本原型是从CSDN上找的,使用过程中逐渐优化,适应的发票样式更多一点,目前基本覆盖了我经手的所有发票。

在此向原作者致谢并致歉------我实在不记得原始链接了,如果原作者认出了本代码的前世,可留个言。

工作原理:

使用PDF库提取文字,用正则表达式查找目标字段信息,导出到Excel中。

虽然在我使用的场景中已经很稳定了,但因为PDF上的文字往往不像看到的那样是个连续的字符串,所以有时候会匹配不到,或者字段内容找不全,因此生成的文件最好还是人工过目一遍。

更稳妥的办法是用AI基于图像识别的方式来做,那就是另外的路线了。

使用方法:

修改代码中的target_dir,这是存放你要处理的发票PDF的文件夹,然后运行脚本即可。

脚本运行后会在这个文件夹中生成一个Excel文件,记录几个关键字段和对应的发票文件路径。

python 复制代码
# 需要导入以下包
import pdfplumber
import os
from openpyxl import Workbook
import re

# 存放待提取发票的完整路径
target_dir = fr"/Users/xxx/财务/发票"

# 定义表格的标题
titles = ["发票号码", "开票日期", "价税合计", "开票人", "文件名"]

# 定义查找发票字段的正则表达式数组,每行对应一个字段,可使用多个表达式,按优先度排列,找到了就不再使用下一个表达式。
regex_patterns = [
    ['发\s*票\s*号\s*码\s*[::]\s*(\d+)', '票\s*据\s*号\s*码\s*[::]\s*(\d+)'],
    ['开\s*票\s*日\s*期\s*[::]\s*(.*?)\n', '开\s*票\s*日\s*期\s*[::]\s*([\d-]+)\n'],
    ['价\s*税\s*合\s*计.*?[¥¥]\s*([\d.]+)', '[\((]小写[\))]¥?\s*([\d.,]+)'],
    ['[销售]\s*名\s*称\s*[::]\s*(.*?)\n', '[\((]小写[\))].+名\s*称\s*[::]\s*(.*?)\n', '售\s*名\s*称\s*[::]*\s*(.*?)\n']
]

def main():
    # 创建存放发票信息的文件
    wenjian = Workbook()
    sheet = wenjian.active

    # 先把要提取内容的抬头写入单元格
    for col, title in enumerate(titles, start=1):
        sheet.cell(1, col, title)

    # 从表格第二行开始写入数据
    row = 2
    # 下面三行提取所有发票PDF的文件名
    files = []
    for (dirpath, dirnames, filenames) in os.walk(target_dir):
        files.extend(os.path.join(dirpath, file) for file in filenames if file.lower().endswith('.pdf'))

    for file in files:
        with pdfplumber.open(fr"{file}") as pdf:
            print(f"Processing {file}")
            first_page = pdf.pages[0]
            text = first_page.extract_text()
            print(f"Extracted text from {file}:\n{text}\n")
            fill_excel(wenjian, file, text, regex_patterns, row)
        row += 1

    # 提取好后保存文件
    wenjian.save(os.path.join(target_dir, "发票列表.xlsx"))

# 使用正则表达式查找对象
def find_with_regex(text, regex_list):
    for regex in regex_list:
        result = re.findall(regex, text, re.DOTALL)
        if result:
            return result[0]
    return None # 如果没有找到匹配项,返回None

def fill_excel(wb, file, text, regex_patterns, row):
    sheet = wb.active
    relative_path = os.path.relpath(file, target_dir)
    sheet.cell(row, len(titles), relative_path)
    for column in range(0, len(regex_patterns)):
        result = find_with_regex(text, regex_patterns[column])
        if result:
            # 去掉结果中的所有空格(有时候销售名称中会有空格)
            result = result.replace(" ", "")

            print(f"\t{titles[column]}: {result}")
            if (titles[column] == "价税合计"):
                cell = sheet.cell(row, column+1, float(result))
                # 把单元格设置为数值类型
                cell.number_format = '#,##0.00'
            else:
                if (titles[column] == "开票日期"):
                    # 去掉日期中的空格
                    result = result.replace(" ", "")

                cell = sheet.cell(row, column+1, result)
            
        else:
            print(f"\t{titles[column]}: <没找到!>")

if __name__ == "__main__":
    main()
相关推荐
海的透彻20 分钟前
dockerfile如何快速构建容器(java服务)
java·开发语言
code2cat33 分钟前
Java工具篇之Gradle:用任务图组织构建与交付
java·开发语言·单元测试·gradle
北京中科新远科技38 分钟前
高速交换机端口预算怎么做?下行、上联、拆分和冗余计算框架
开发语言·php
zh路西法1 小时前
【PACE】面向永磁同步电机PMSM 执行器的参数辨识与能耗感知强化学习 Sim-to-Real
python·cma-es·pmsm·sim-to-real·issac·pace
xiaoye-duck1 小时前
《C++ 项目:手写大模型接入 SDK》DeepSeek接口设计(下):流式响应实现和测试
开发语言·网络·c++·语言模型
后台模板学习1 小时前
大模型 Prompt 工程在电商订单系统中的踩坑记录
java·python·prompt
2601_967987372 小时前
如何运行 Python 代码
python·最佳实践·脚本文件·代码运行·交互式环境
WiKiLeaks_successor2 小时前
重构Time库的抽象设计
python·重构
AI源码2 小时前
基于 Python 的短视频用户行为分析系统
开发语言·python
第九序言T2 小时前
数据分析岗 Pandas 面试题 20 道(上):概念与读代码
python·数据分析·pandas·数据处理