Python自动化:基于文件名与正则表达式批量分类PDF发票文件
最近在整理财务数据时,你是不是也遇到过这样的麻烦事?销售部一次性发来上千张电子发票PDF,要求按客户名称分门别类归档。手动操作?光是想想就让人头皮发麻——打开、查看、复制、新建文件夹、粘贴……重复一千次,不仅耗时大半天,还极易出错,万一漏掉几张,后续对账就是灾难。
这正是许多财务、行政和业务支持岗位的日常痛点。面对海量、格式统一但需要精细分类的文件,传统的手工方式效率低下且不可靠。本文将彻底解决这个问题:如何通过Python编程,实现将上千张发票PDF文件,根据其文件名或文件内容中的客户名称信息,自动、准确地批量划分到对应文件夹中。
读完本文,你将获得一个完整的、可立即上手的解决方案。我们不止步于“能运行”的脚本,更会深入探讨如何应对真实场景中的各种“坑”,比如文件名格式不统一、客户名存在别名、以及如何设计容错机制。即使你是Python新手,也能跟随步骤,搭建起属于自己的自动化文件整理工具,将重复性劳动交给程序,解放你的双手。
1. 核心问题拆解:从需求到技术方案
首先,我们要明确这个任务的核心。它不仅仅是“移动文件”,而是一个典型的**“模式识别”与“批量操作”**相结合的问题。关键在于如何从每一张发票中提取出唯一的客户标识(客户名称)。
根据发票文件的现状,主要有两种技术路径:
- 基于文件名的提取(简单,但依赖规范):如果发票文件名本身就包含了客户名称,例如
"XX科技有限公司-发票20240301.pdf"或"INV_2024_003_阿里巴巴集团.pdf",那么问题就简化为字符串处理。这是最快、最直接的方案。 - 基于文件内容的提取(复杂,但更通用):如果文件名是乱码或无意义数字(如
"fapiao_001.pdf"),客户信息只存在于PDF文件的内部文字中,那么就需要用到PDF文本解析技术(如PyPDF2或pdfplumber)。这是更健壮、适应性更强的方案。
本文将重点讲解第一种方案(基于文件名),因为它覆盖了80%的常见场景,且易于理解和实现。同时,我会在高级实践部分,为你勾勒出第二种方案(基于内容)的实现框架和关键代码,让你可以根据实际情况灵活升级。
我们的技术栈选择:
- 语言:Python。因其在文件处理、字符串操作和自动化脚本方面的绝对优势。
- 核心库:
os(操作系统交互)、shutil(文件移动/复制)、re(正则表达式,用于复杂文件名匹配)。 - 可选库:
PyPDF2/pdfplumber(用于方案二)。
2. 环境准备与前置条件
在开始编写代码之前,请确保你的工作环境已就绪。
- 操作系统:Windows 10/11, macOS 或 Linux 均可。本文示例路径将使用Windows风格(
C:\invoices),其他系统请相应调整路径分隔符(Linux/macOS 用/)。 - Python环境:确保已安装Python 3.6或更高版本。打开终端(Windows CMD/PowerShell, macOS/Linux Terminal),输入
python --version检查。 - 代码编辑器:推荐使用 VS Code、PyCharm 或任何你熟悉的文本编辑器。
- 文件结构准备:
- 将所有待处理的发票PDF文件,集中放在一个文件夹内,例如:
C:\待处理发票。 - 准备一个空文件夹作为“分拣目的地”,例如:
C:\已分类发票。程序将在这里为每个客户创建子文件夹。
- 将所有待处理的发票PDF文件,集中放在一个文件夹内,例如:
假设你的源文件夹 (C:\待处理发票) 内容如下:
C:\待处理发票\ ├── 阿里巴巴集团-发票20240301.pdf ├── 腾讯科技-服务费20240302.pdf ├── 百度在线-2024年3月账单.pdf ├── 阿里巴巴集团-20240315.pdf ├── 字节跳动-发票0320.pdf └── 腾讯科技-采购订单20240310.pdf我们的目标是,运行程序后,C:\已分类发票文件夹变成:
C:\已分类发票\ ├── 阿里巴巴集团\ │ ├── 阿里巴巴集团-发票20240301.pdf │ └── 阿里巴巴集团-20240315.pdf ├── 腾讯科技\ │ ├── 腾讯科技-服务费20240302.pdf │ └── 腾讯科技-采购订单20240310.pdf ├── 百度在线\ │ └── 百度在线-2024年3月账单.pdf └── 字节跳动\ └── 字节跳动-发票0320.pdf3. 基础版本:基于固定分隔符的简单划分
我们先从最简单的情况开始:假设所有文件名都严格遵循客户名称-发票详情.pdf的格式,即客户名称和文件详情之间有一个固定的分隔符(如“-”)。
这个方案的逻辑非常清晰:
- 遍历源文件夹中的所有PDF文件。
- 对每个文件名,用分隔符“-”进行分割。
- 分割后的第一部分就是客户名称。
- 根据客户名称,在目标文件夹创建对应的子文件夹(如果不存在)。
- 将文件移动或复制到该子文件夹。
以下是完整的实现代码,你可以保存为一个.py文件,例如organize_invoices.py。
# organize_invoices_simple.py import os import shutil def organize_invoices_by_separator(source_dir, target_dir, separator="-"): """ 根据固定分隔符划分发票文件到客户文件夹 :param source_dir: 源文件夹路径,存放所有发票PDF :param target_dir: 目标根文件夹路径,分类后的文件夹将创建于此 :param separator: 文件名中分隔客户名和其余部分的分隔符,默认为'-' """ # 1. 检查源文件夹是否存在 if not os.path.exists(source_dir): print(f"错误:源文件夹 '{source_dir}' 不存在!") return # 2. 确保目标根文件夹存在 os.makedirs(target_dir, exist_ok=True) # 3. 遍历源文件夹中的所有文件 for filename in os.listdir(source_dir): # 只处理PDF文件 if filename.lower().endswith('.pdf'): file_path = os.path.join(source_dir, filename) # 4. 使用分隔符分割文件名 # 例如:'阿里巴巴集团-发票20240301.pdf' -> ['阿里巴巴集团', '发票20240301.pdf'] parts = filename.split(separator, 1) # 只分割第一次出现的分隔符 if len(parts) == 2: customer_name = parts[0].strip() # 提取客户名,并去除两端空格 # 5. 构建客户目标文件夹路径 customer_dir = os.path.join(target_dir, customer_name) # 如果文件夹不存在,则创建 os.makedirs(customer_dir, exist_ok=True) # 6. 构建文件在目标文件夹中的新路径 target_file_path = os.path.join(customer_dir, filename) # 7. 移动文件(使用复制 shutil.copy2 更安全,避免误操作丢失) # shutil.move(file_path, target_file_path) # 移动操作,慎用! shutil.copy2(file_path, target_file_path) # 复制操作,保留元数据 print(f"已复制: {filename} -> {customer_name}/") else: # 如果文件名不符合分隔符格式,则跳过或放入“未分类”文件夹 print(f"跳过(格式不符): {filename}") print("\n文件整理完成!") # 使用示例 if __name__ == "__main__": # 请根据你的实际情况修改以下路径 SOURCE_DIRECTORY = r"C:\待处理发票" # 你的发票源文件夹 TARGET_DIRECTORY = r"C:\已分类发票" # 你的目标文件夹 organize_invoices_by_separator(SOURCE_DIRECTORY, TARGET_DIRECTORY, separator="-")关键代码解释:
os.listdir(source_dir): 列出源文件夹下所有文件和文件夹名。filename.split(separator, 1): 这是核心。参数1表示只分割第一个遇到的separator,防止客户名本身包含分隔符(虽然少见)。os.makedirs(customer_dir, exist_ok=True): 创建文件夹,exist_ok=True确保如果文件夹已存在也不会报错。shutil.copy2: 复制文件,并尽可能保留文件的元数据(如修改时间)。强烈建议在首次运行时使用copy2而不是move,待确认分类无误后,再手动删除源文件或改用移动操作。这是数据安全的基本准则。
运行与验证:
- 将上述代码保存为
organize_invoices_simple.py。 - 修改代码末尾的
SOURCE_DIRECTORY和TARGET_DIRECTORY为你的实际路径。 - 在终端中,切换到脚本所在目录,执行:
python organize_invoices_simple.py。 - 观察终端输出,查看文件复制过程。最后打开目标文件夹
C:\已分类发票,检查是否已按客户名称正确创建子文件夹并归集文件。
4. 进阶版本:使用正则表达式应对复杂文件名
现实往往比理想骨感。文件名可能千奇百怪:“发票_百度在线网络技术公司_0325.pdf”、“2024-03-01-腾讯科技服务费.pdf”、“FAPIAO_ALIBABA_20240301.pdf”。固定的分隔符split方法此时就力不从心了。
这时,我们需要更强大的工具:正则表达式 (Regular Expression)。它的核心思想是模式匹配,我们可以定义规则,从复杂的字符串中“抽取”出我们想要的部分(客户名)。
假设我们的文件名模式是:开头或中间某处是客户名,客户名通常由中文字符、英文字母和数字组成(不包括日期、发票、FAPIAO等固定词汇)。
我们可以尝试定义规则:匹配第一个连续的中文字符串(或中英文混合字符串)作为客户名。这需要一些尝试和调整。
# organize_invoices_regex.py import os import shutil import re def extract_customer_name_by_regex(filename): """ 使用正则表达式从文件名中提取客户名称。 这是一个示例规则,你可能需要根据实际文件名模式调整正则表达式。 :param filename: 文件名(不含路径) :return: 提取到的客户名称,如果提取失败则返回None """ # 移除文件扩展名 name_without_ext = os.path.splitext(filename)[0] # 示例正则表达式1:匹配以中文、英文、数字、空格组成的字符串(尽可能长) # 这个模式假设客户名是文件名中第一个这样的连续块 pattern1 = r'^[^\d\W_\-]+' # 匹配开头,非数字、非标点、非下划线/连字符的连续字符 # 示例正则表达式2:匹配包含至少两个中文字符的连续片段 pattern2 = r'[\u4e00-\u9fa5]{2,}' # [\u4e00-\u9fa5] 代表中文字符 # 先尝试匹配较宽泛的 pattern1 match = re.search(pattern1, name_without_ext) if match: candidate = match.group() # 如果匹配到的结果太短(比如只有一个字),可能不是客户名,尝试 pattern2 if len(candidate) < 2: match = re.search(pattern2, name_without_ext) if match: return match.group() return candidate # 如果 pattern1 没匹配到,尝试 pattern2 match = re.search(pattern2, name_without_ext) if match: return match.group() # 如果都匹配不到,返回 None return None def organize_invoices_by_regex(source_dir, target_dir, unclassified_dir_name="未分类"): """ 使用正则表达式提取客户名并分类文件 :param source_dir: 源文件夹路径 :param target_dir: 目标根文件夹路径 :param unclassified_dir_name: 未分类文件的存放文件夹名 """ if not os.path.exists(source_dir): print(f"错误:源文件夹 '{source_dir}' 不存在!") return os.makedirs(target_dir, exist_ok=True) # 创建“未分类”文件夹 unclassified_dir = os.path.join(target_dir, unclassified_dir_name) os.makedirs(unclassified_dir, exist_ok=True) for filename in os.listdir(source_dir): if filename.lower().endswith('.pdf'): file_path = os.path.join(source_dir, filename) customer_name = extract_customer_name_by_regex(filename) if customer_name: customer_dir = os.path.join(target_dir, customer_name) os.makedirs(customer_dir, exist_ok=True) target_file_path = os.path.join(customer_dir, filename) shutil.copy2(file_path, target_file_path) print(f"已分类: {filename} -> {customer_name}/") else: # 无法提取客户名,放入“未分类”文件夹 target_file_path = os.path.join(unclassified_dir, filename) shutil.copy2(file_path, target_file_path) print(f"未分类: {filename} -> {unclassified_dir_name}/") print("\n文件整理完成!请检查‘未分类’文件夹。") # 使用示例 if __name__ == "__main__": SOURCE_DIRECTORY = r"C:\待处理发票" TARGET_DIRECTORY = r"C:\已分类发票" organize_invoices_by_regex(SOURCE_DIRECTORY, TARGET_DIRECTORY)正则表达式策略解析:
pattern1 = r'^[^\d\W_\-]+':^表示从字符串开头匹配。[^\d\W_\-]是一个否定字符集,匹配不是数字(\d)、不是非单词字符(\W, 但这里需要排除下划线和连字符)、不是下划线(_)、不是连字符(-) 的字符。+表示匹配一个或多个。这个模式旨在匹配文件名开头的一串“干净”的字符(通常是公司名)。pattern2 = r'[\u4e00-\u9fa5]{2,}':[\u4e00-\u9fa5]匹配任意一个中文字符。{2,}表示匹配至少2个。这个模式专门用于抓取文件名中的中文名称。re.search(pattern, string):在字符串中搜索第一个匹配正则表达式的位置。- 逻辑是:先尝试用宽泛规则
pattern1匹配,如果匹配到的结果太短,则用专门的中文规则pattern2再试一次。如果都失败,则归入“未分类”。
重要提示:正则表达式需要根据你的实际文件名模式进行定制和调试。没有放之四海而皆准的规则。你可以先用一小部分文件测试,打印出提取的客户名,不断调整正则表达式,直到满意为止。
5. 企业级实践:映射表与容错处理
在实际企业环境中,情况可能更复杂:
- 客户别名:系统中客户叫“阿里巴巴(中国)有限公司”,但发票上可能写“阿里集团”或“Alibaba”。
- 名称不统一:同一客户,有的文件写全称,有的写简称。
- 需要人工复核:对于程序无法确定或匹配失败的文件,需要单独列出,供人工处理。
一个更健壮的方案是引入“客户名称映射表”。我们可以创建一个配置文件(如JSON或CSV),建立标准客户名与各种可能出现的文件标识之间的映射关系。
步骤:
- 创建一个
customer_mapping.json文件。 - 程序读取映射表。
- 对于每个文件,尝试用映射表中的“关键字”去匹配文件名。
- 匹配成功,则归入标准客户名对应的文件夹。
- 匹配失败,则放入“待处理”文件夹。
// customer_mapping.json { "阿里巴巴集团": ["阿里巴巴", "阿里集团", "Alibaba", "alibaba"], "腾讯科技": ["腾讯", "Tencent", "tencent", "腾讯控股"], "百度在线": ["百度", "Baidu", "baidu"], "字节跳动": ["字节", "ByteDance", "bytedance", "抖音集团"] }# organize_invoices_mapping.py import os import shutil import json def organize_invoices_with_mapping(source_dir, target_dir, mapping_file, unclassified_dir_name="待处理"): """ 使用客户名称映射表进行文件分类 :param source_dir: 源文件夹路径 :param target_dir: 目标根文件夹路径 :param mapping_file: 映射表JSON文件路径 :param unclassified_dir_name: 未匹配文件的存放文件夹名 """ # 1. 加载客户映射表 try: with open(mapping_file, 'r', encoding='utf-8') as f: customer_mapping = json.load(f) except FileNotFoundError: print(f"错误:映射文件 '{mapping_file}' 未找到!") return except json.JSONDecodeError: print(f"错误:映射文件 '{mapping_file}' 格式错误!") return # 2. 反转映射,便于查找:关键字 -> 标准客户名 keyword_to_customer = {} for standard_name, keywords in customer_mapping.items(): for keyword in keywords: # 关键字统一转为小写,实现不区分大小写的匹配 keyword_to_customer[keyword.lower()] = standard_name if not os.path.exists(source_dir): print(f"错误:源文件夹 '{source_dir}' 不存在!") return os.makedirs(target_dir, exist_ok=True) unclassified_dir = os.path.join(target_dir, unclassified_dir_name) os.makedirs(unclassified_dir, exist_ok=True) classified_count = 0 unclassified_count = 0 for filename in os.listdir(source_dir): if not filename.lower().endswith('.pdf'): continue file_path = os.path.join(source_dir, filename) # 将文件名转为小写,用于匹配 filename_lower = filename.lower() target_customer = None # 3. 遍历映射表中的所有关键字,检查是否包含在文件名中 for keyword, standard_name in keyword_to_customer.items(): if keyword in filename_lower: target_customer = standard_name break # 找到第一个匹配的关键字即停止 if target_customer: # 4. 找到匹配,放入对应客户文件夹 customer_dir = os.path.join(target_dir, target_customer) os.makedirs(customer_dir, exist_ok=True) target_file_path = os.path.join(customer_dir, filename) shutil.copy2(file_path, target_file_path) print(f"已分类 [{target_customer}]: {filename}") classified_count += 1 else: # 5. 未找到匹配,放入“待处理”文件夹 target_file_path = os.path.join(unclassified_dir, filename) shutil.copy2(file_path, target_file_path) print(f"待处理: {filename}") unclassified_count += 1 print(f"\n整理完成!共处理文件 {classified_count + unclassified_count} 个。") print(f" -> 已自动分类: {classified_count} 个") print(f" -> 待人工处理: {unclassified_count} 个 (位于 '{unclassified_dir_name}' 文件夹)") # 使用示例 if __name__ == "__main__": SOURCE_DIRECTORY = r"C:\待处理发票" TARGET_DIRECTORY = r"C:\已分类发票" MAPPING_FILE = r"C:\script\customer_mapping.json" # 映射表文件路径 organize_invoices_with_mapping(SOURCE_DIRECTORY, TARGET_DIRECTORY, MAPPING_FILE)这个方案的最大优势是可控和可维护。当出现新的客户或新的文件名变体时,你只需要更新customer_mapping.json文件,而无需修改Python代码。它清晰地分离了“业务规则”(映射关系)和“程序逻辑”(文件操作)。
6. 运行结果与效果验证
运行上述任何一个脚本后,你应该在终端看到类似以下的输出:
已分类 [阿里巴巴集团]: 阿里巴巴集团-发票20240301.pdf 已分类 [腾讯科技]: 腾讯科技-服务费20240302.pdf 已分类 [百度在线]: 百度在线-2024年3月账单.pdf 已分类 [阿里巴巴集团]: 阿里巴巴集团-20240315.pdf 已分类 [字节跳动]: 字节跳动-发票0320.pdf 已分类 [腾讯科技]: 腾讯科技-采购订单20240310.pdf 整理完成!共处理文件 6 个。 -> 已自动分类: 6 个 -> 待人工处理: 0 个 (位于 '待处理' 文件夹)验证步骤:
- 检查目标文件夹结构:打开你设置的目标文件夹(如
C:\已分类发票),确认是否按预期的客户名称创建了子文件夹。 - 核对文件归属:随机打开几个客户子文件夹,检查里面的PDF文件是否确实属于该客户。可以双击打开几个PDF,核对内容中的客户名称。
- 检查“未分类/待处理”文件夹:如果使用了进阶或映射表版本,检查这个文件夹。里面的文件就是需要你人工干预或调整规则的。
- 对比文件数量:确保源文件夹的文件总数等于目标文件夹(所有子文件夹文件数之和)加上“未分类”文件夹的文件数。防止文件在操作中丢失。
首次运行强烈建议:
- 使用
shutil.copy2(复制)而不是shutil.move(移动)。 - 在一个测试文件夹中用少量文件(10-20个)运行脚本,验证无误后,再处理整个包含上千个文件的正式文件夹。
- 处理前,备份你的源文件。
7. 常见问题与排查思路
在实际操作中,你可能会遇到以下问题。这里提供一份排查清单:
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 程序运行后没有任何文件被处理,终端也无输出。 | 1. 源文件夹路径错误。 2. 源文件夹为空。 3. 脚本中的文件扩展名判断有误(如 .PDF与.pdf)。 | 1. 打印source_dir路径,检查是否存在。2. 打印 os.listdir(source_dir)的结果。3. 检查 if filename.lower().endswith(‘.pdf’)这行代码。 | 1. 使用绝对路径,并用r前缀防止转义(Windows)。2. 确保文件夹内有文件。 3. 确认文件名后缀大小写。代码中已用 .lower()处理,通常是安全的。 |
| 所有文件都被放到了“未分类”文件夹。 | 1. 文件名提取规则(分隔符或正则)与实际情况不匹配。 2. 映射表(如果使用)中的关键字未正确匹配。 | 1. 在提取客户名的函数中,打印出filename和提取到的customer_name。2. 检查映射表JSON格式是否正确,关键字是否包含在文件名中(注意大小写)。 | 1. 调整分隔符或修改正则表达式。先用几个典型文件名测试你的正则。 2. 在匹配时,统一将文件名和关键字转为小写再比较(如示例代码所示)。 |
| 同一个客户的文件被分到了多个不同的文件夹(如“阿里”和“阿里巴巴”)。 | 提取出的客户名称不统一,存在简称、全称、别名。 | 查看目标文件夹,对比产生不同文件夹名的那些原始文件名。 | 采用映射表方案。将所有变体映射到同一个标准客户名。这是最根本的解决方法。 |
程序报错PermissionError或FileNotFoundError。 | 1. 文件正在被其他程序(如PDF阅读器)打开。 2. 目标路径包含非法字符或权限不足。 3. 移动文件时,目标文件夹不存在且创建失败。 | 查看完整的错误信息,它会指明是哪个文件或路径出了问题。 | 1. 关闭所有可能占用源文件的程序。 2. 避免在路径中使用特殊字符。以管理员身份运行命令行(如果需要)。 3. 确保 os.makedirs被正确调用,且目标路径有效。 |
| 处理速度很慢(针对上千个文件)。 | 1. 如果是基于内容的提取(方案二),PDF解析本身较慢。 2. 磁盘IO性能瓶颈。 | 观察程序运行时CPU和磁盘活动情况。 | 1. 对于纯文件名操作,速度应该很快。如果慢,检查是否有不必要的循环或IO操作。 2. 考虑将“复制”改为“移动”(在确认无误后),减少磁盘写入量。 3. 对于方案二,可以考虑使用更快的库如 pdfplumber,或先批量解析再批量移动。 |
8. 最佳实践与工程建议
将一个小脚本打磨成可靠的生产力工具,还需要考虑以下几点:
日志记录:不要只依赖
print。使用Python的logging模块,将运行过程、成功/失败记录写入日志文件,便于后续审计和排查。import logging logging.basicConfig(filename='invoice_organizer.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') # 在代码中用 logging.info(f“已分类: {filename}”) 替代 print配置文件化:将源路径、目标路径、分隔符、映射表路径等参数提取到外部配置文件(如
config.ini或settings.json)中,避免硬编码。# config.json { "source_dir": "C:/待处理发票", "target_dir": "C:/已分类发票", "mapping_file": "C:/script/mapping.json", "mode": "copy" # 或 "move" }异常处理与事务性:考虑更完善的异常处理。例如,在移动大量文件时,万一中途出错,是希望部分成功,还是全部回滚?复杂的场景可能需要先模拟运行(dry-run),列出所有待执行的操作,确认无误后再真实执行。
扩展性:从文件名到内容。如果必须从PDF内容提取客户名,以下是使用
pdfplumber库的基本框架:import pdfplumber def extract_customer_from_pdf(pdf_path): try: with pdfplumber.open(pdf_path) as pdf: # 通常客户信息在第一页 first_page = pdf.pages[0] text = first_page.extract_text() # 这里需要根据你发票的固定格式,用正则从text中查找客户名 # 例如,查找“客户名称:”或“Buyer:”后面的字符串 # pattern = r'客户名称[::]\s*(\S+)' # match = re.search(pattern, text) # if match: return match.group(1) return None # 示例返回 except Exception as e: print(f"解析PDF失败 {pdf_path}: {e}") return None注意:PDF解析受文档格式、编码影响极大,需要针对你的发票模板专门设计提取逻辑,通用性较差。
版本管理与迭代:将你的脚本和配置文件纳入版本控制系统(如Git)。当文件名规则变化或新增客户时,你可以清晰地管理映射表的变更历史。
9. 总结与后续方向
通过本文,我们从一个具体的痛点出发,构建了一个从简单到复杂、逐步强健的发票自动分类解决方案。核心思路是“识别-归类-移动”,技术关键在于如何从文件名中稳定地提取客户标识。
- 对于规则明确的场景,使用固定分隔符方案最快最直接。
- 对于文件名格式多样但仍有迹可循的场景,正则表达式是你的利器,但需要耐心调试。
- 对于企业级、需要高准确率和可维护性的场景,客户名称映射表是最佳实践,它将易变的业务规则从代码中分离。
下一步,你可以这样深化:
- 打造图形界面 (GUI):使用
tkinter或PyQt为脚本包装一个简单的桌面界面,让非技术同事也能一键使用。 - 集成到工作流:将脚本设置为Windows计划任务或macOS/Linux的cron job,定期自动处理指定文件夹中的新发票。
- 增加更多元数据:在移动文件的同时,是否可以读取发票号、日期、金额,并生成一个汇总的Excel报告?
- 探索更智能的识别:如果文件名和PDF内容都不可靠,是否可以尝试调用OCR API(如百度OCR、腾讯OCR)识别扫描版发票上的印刷体文字?这将是更终极的解决方案,但会引入成本和网络依赖。
自动化处理重复性文件整理工作,其价值远不止节省几个小时时间。它减少了人为错误,保证了流程的一致性,并让你能专注于更有价值的分析和管理任务。希望这个工具能成为你效率工具箱中得力的一员。建议收藏本文,并根据你的实际需求,灵活组合或修改其中的代码模块。
