当前位置:首页>python>告别手动整理!Python轻松搞定PDF发票!

告别手动整理!Python轻松搞定PDF发票!

  • 2026-10-21 21:22:00
告别手动整理!Python轻松搞定PDF发票!

公司的发票处理流程是这样的:

  1. 下载或接收大量PDF格式的电子发票

  2. 逐一点开每张发票

  3. 肉眼查找日期、金额、发票号码等关键信息

  4. 手动重命名发票PDF文件

  5. 归档保存

这个过程存在明显痛点:

  • 时间消耗巨大:处理100张发票可能需要2-3小时

  • 错误率偏高:手动输入容易出错,特别是长串的数字

  • 重复劳动:每月、每周甚至每天都要重复相同步骤

  • 精神消耗:极度枯燥,容易疲劳分神

如果你也为此头疼,那么今天要搞的这款Python小工具,可能就是你的办公救星!

全部源码和打包好的EXE程序包已经备好,关注公众号在后台回复:fpcmm

“零门槛”PDF发票重命名小工具

无需安装,开箱即用

  • 不需要安装Python环境

  • 不需要配置任何软件

  • 双击即可运行,真正的“绿色软件”

完全免费,无任何限制

  • 无次数限制

  • 无水印添加

  • 无广告推送

操作简单,三步完成

  1. 选择PDF发票文件夹

  2. 点击【开始重命名】按钮

  3. 批量重命名为日期+金额+发票号码

安全可靠,隐私无忧

  • 本地操作,文档不上传云端

  • 无网络也能使用

  • 保证源文件隐私安全

Python如何“读懂”PDF发票?

本质上是通过pdfplumber模块,提取出PDF发票上的信息,再通过re模块匹配关键词

  1. 提取文字内容:将PDF中的文字信息转换为可处理的文本

  2. 关键内容解析:从文本中匹配提取关键词(日期、金额、发票号码等)

下面是转换的Python脚本,若你有Python环境,可直接使用

# -*- coding: utf-8 -*-"""@Time    : 2025/12/13 9:11@Author  : Driver_Won@微信公众号: Python与手工"""import refrom pathlib import Pathimport pdfplumber as pdfplumberdef rename_invoice_pdf(path):    """    PDF发票重命名    :return:    """    # 只处理文件夹下pdf文件    pdf_files = [f for f in path.iterdir() if f.suffix == '.pdf']    pdf_num = len(pdf_files)    renamed_num = 0    print(f"共{pdf_num}个PDF文件,开始重命名========>>")    # 开始提取信息    for i, pdf_path in enumerate(pdf_files):        try:            with pdfplumber.open(pdf_path) as f:                page = f.pages[0]                text = page.extract_text()                # 开票日期                match_rq = re.search(r"开票日期[::]\s*(\d{4}年\d{2}月\d{2}日)", text)                rq = match_rq.group(1).strip() if match_rq else f'未获取到日期{renamed_num + 1}'                # 金额                match_je = re.findall(r"[¥¥]\s*(\d+\.\d+)", text)                je = match_je[-1] if match_je else f'未获取到金额{renamed_num + 1}'                # 发票号码                match_hm = re.search(r"发票号码[::]\s*(\w+)", text, re.IGNORECASE)                hm = match_hm.group(1).strip() if match_hm else f'未获取到号码{renamed_num + 1}'            new_pdf_name = f"{rq}+{je}+{hm}.pdf"            new_pdf_path = pdf_path.with_name(new_pdf_name)            pdf_path.rename(new_pdf_path)            renamed_num += 1            print(f'✔ 第{i + 1}个/共{pdf_num}个,{pdf_path.name}重命名成功!')        except Exception as e:            print(f'✘ 第{i + 1}个/共{pdf_num}个,{pdf_path.name}重命名失败! {e}')if __name__ == '__main__':    p = Path(r'C:\Users\Administrator\Desktop\发票')    rename_invoice_pdf(p)

再用ttkbootstrap搞个GUI界面,方便操作使用(可直接打包为exe程序)

# -*- coding: utf-8 -*-"""@Time    : 2025/12/13 9:10@Author  : Driver_Won@微信公众号: Python与手工"""import reimport threadingimport pdfplumberimport ttkbootstrap as ttkfrom ttkbootstrap.constants import *from ttkbootstrap.dialogs import Messageboxfrom tkinter.filedialog import askdirectoryfrom pathlib import Pathclass GUI:    def __init__(self, master):        """Invoice_Rename GUI"""        self.root = master        self.invoice_pdf_dir = ttk.StringVar()  # 生成一个StringVar对象,来保存发票文件夹路径        self.converting = False        self.create_page()    def create_page(self):        """创建页面元素"""        # 主控件        self.mf = ttk.Frame(self.root)        self.mf.pack()        # 标题标签        ttk.Label(self.mf, text='\U00002b50PDF发票重命名\U00002b50', font=('宋体', 30), bootstyle=PRIMARY).pack(pady=20)        # 文件夹选择        lf1 = ttk.LabelFrame(self.mf, text="文件夹:", bootstyle=PRIMARY)        lf1.pack(pady=2)        ttk.Entry(lf1, width=74, textvariable=self.invoice_pdf_dir, bootstyle=PRIMARY).pack(side=LEFT, pady=2)        self.b1 = ttk.Button(lf1, text='浏览', command=self.select_path, bootstyle=PRIMARY)        self.b1.pack(side=RIGHT, pady=2)        # 开始按钮        self.bt_start = ttk.Button(self.mf, text='开始重命名', width=10, command=self.start, bootstyle='outline-toolbutton')        self.bt_start.pack(pady=2)        # 进度条        self.pb = ttk.Progressbar(self.mf, length=740, mode=INDETERMINATE, bootstyle=PRIMARY)        self.pb.pack(pady=2)        # 日志显示        lf2 = ttk.LabelFrame(self.mf, text="日志:", bootstyle=PRIMARY)        lf2.pack(pady=10)        self.text = ttk.Text(lf2, wrap=ttk.WORD, height=12)        self.text.config(state=DISABLED)  # 默认text文本框不可编辑        self.text.pack(fill="both")    def select_path(self):        """        选择文件夹按钮对应的事件,打开文件夹选择对话框        :return:        """        path_pdf_dir = askdirectory(title='选择文件夹', )  # 文件选择对话框        if path_pdf_dir:  # 选择了文件夹            self.invoice_pdf_dir.set(path_pdf_dir)            self.insert_text(f'选择的文件夹: {path_pdf_dir}')    def insert_text(self, text):        """        向文本框中插入文字        :return:        """        self.text.config(state=NORMAL)        self.text.insert(ttk.END, text)        self.text.insert(ttk.END, "\n")        self.text.see(ttk.END)  # 设置文本框内容超出显示范围时自动滚动到底部        self.text.config(state=DISABLED)        # self.text.update()    def rename_invoice_pdf(self):        """        PDF发票重命名        :return:        """        target_dir = Path(self.invoice_pdf_dir.get()) if not self.invoice_pdf_dir.get() == '' else False        if not target_dir or not target_dir.is_dir():            self.root.after(0, lambda: Messagebox.show_error(message='请先选择发票PDF文件夹!', title='出错啦!'))            self.stop()            return        # 只处理文件夹下pdf文件        pdf_files = [f for f in target_dir.iterdir() if f.suffix == '.pdf']        pdf_num = len(pdf_files)        renamed_num = 0        self.insert_text(f"共{pdf_num}个PDF文件,开始重命名========>>")        # 开始提取信息        for i, pdf_path in enumerate(pdf_files):            try:                with pdfplumber.open(pdf_path) as f:                    page = f.pages[0]                    text = page.extract_text()                    # 开票日期                    match_rq = re.search(r"开票日期[::]\s*(\d{4}年\d{2}月\d{2}日)", text)                    rq = match_rq.group(1).strip() if match_rq else f'未获取到日期{renamed_num+1}'                    # 金额                    match_je = re.findall(r"[¥¥]\s*(\d+\.\d+)", text)                    je = match_je[-1] if match_je else f'未获取到金额{renamed_num+1}'                    # 发票号码                    match_hm = re.search(r"发票号码[::]\s*(\w+)", text, re.IGNORECASE)                    hm = match_hm.group(1).strip() if match_hm else f'未获取到号码{renamed_num+1}'                new_pdf_name = f"{rq}+{je}+{hm}.pdf"                new_pdf_path = pdf_path.with_name(new_pdf_name)                pdf_path.rename(new_pdf_path)                renamed_num += 1                self.insert_text(f'✔ 第{i + 1}个/共{pdf_num}个,{pdf_path.name}重命名成功!')            except Exception as e:                self.insert_text(f'✘ 第{i + 1}个/共{pdf_num}个,{pdf_path.name}重命名失败! {e}')        self.insert_text('========PDF发票全部重命名完成========\n')        self.stop()        self.root.after(0, lambda: Messagebox.show_info(message=f'成功重命名{pdf_num}个PDF发票!', title='恭喜发财!'))    def start(self):        """        开始        :return:        """        if self.converting:            return        self.converting = True        self.bt_start.config(state=DISABLED)        self.pb.start(12)        threading.Thread(target=self.rename_invoice_pdf, daemon=True).start()    def stop(self):        """        停止        :return:        """        self.pb.stop()        self.bt_start.config(state=NORMAL)        self.converting = Falseif __name__ == "__main__":    root = ttk.Window(        title="Invoice_Rename",  # 窗口标题        themename="superhero"  # 主题    )    root.place_window_center()    GUI(root)  # 展示GUI界面    root.mainloop()  # 进入消息循环(必需)

避坑指南

  • 若是使用Python脚本,请提前安装pdfplumber库

  • 发票PDF文件已经在其他程序打开的情况下,重命名会失败

  • 只提取了日期、金额、发票号码,若是需要提取其他关键信息,可添加正则表达式提取规则

  • 小工具为EXE程序,只支持Windows电脑(MAC可使用脚本转换)

最新文章

随机文章