使用Python从发票中提取数据

Portrait of Sylvain Josserand
作者 Sylvain Josserand
5 分钟阅读
最后更新于

要点总结:

  • Python 可以帮助自动化提取发票数据,但没有通用的“一刀切”解决方案
  • Parseur 利用 Python 为你自动提取发票数据
  • PDF 并非真正的数据文件格式,而是纸质文档的排版表现

PDF格式

PDF 格式非常灵活,能够精准呈现如发票等纸质文档,并且对页面的设计没有限制。 PDF 源自纸质打印领域,旨在作为打印页面的数字化等效物。 正因如此,PDF 给予文档创作者极大的自由度,以满足各种标准或法规要求。

然而,一旦数据被封装在 PDF 文件中,数据读取就成了挑战。 PDF 的灵活性和复杂层次,往往与企业希望统一管理和自动化处理结构化数据的需求发生冲突。

A screen capture of PDF file format layers
PDF file format layers

从发票中提取数据的步骤有哪些?

发票通常以 PDF 文件呈现。 发票本质上是记录供应商和客户之间一笔产品或服务交易的商业文件。 要从这类文档中提取结构化数据,通常涉及如下步骤:

  1. 明确你要从发票中提取的数据结构(schema)
  2. 将发票的图片或非文本内容转换为文本
  3. 基于数据结构从文本中提取所需信息
  4. 汇总和输出已提取数据

A screen capture of Invoice data extraction process
Invoice data extraction process

为你的发票数据定义结构(schema)

发票来源各异,不同供应商往往采用不同的发票版式。 尽管发票的具体样式多种多样,但其核心内容大致类似:供应商、客户、发票号、日期,以及一份包含条目数量、描述和金额的明细。 定义数据结构时,可以参考你的会计系统的字段,因为你最终很可能要将数据导入会计软件。 如果需要更标准化且通用的结构,推荐你访问 schema.org 的行业标准格式,涉及时发票可参考Invoice类。 Parseur 为发票提供了默认数据结构,你也可在发票邮箱自定义所需字段,详细说明见此处。 一旦确定好数据格式的定义和字段,就可以进入下一步,将发票影像转换为可处理文本。

例如,以下是使用 JSON Swagger 格式为发票定义的字段示例:

{
    "InvoiceNumber": {
        "type": "string",
        "description": "发票号"
    },
    "InvoiceIssueDate": {
        "type": "string",
        "description": "开票日期"
    },
    "Items": {
        "type": "array",
        "description": "发票中的商品列表",
        "items": {
            "type": "object",
            "properties": {
                "quantity": {
                    "type": "number",
                    "description": "商品数量"
                },
                "description": {
                    "type": "string",
                    "description": "商品描述"
                },
                "unit_price": {
                    "type": "number",
                    "description": "商品单价"
                },
                "price": {
                    "type": "number",
                    "description": "商品总价"
                }
            }
        }
    }
}

将你的发票从图片转换为文本

A screen capture of an invoice taken from a smartphone
Picture of an invoice taken from a smartphone

PDF 文件有时包含图片内容。 比如,有员工可能直接用手机拍摄发票照片,再存为 PDF 发送给财务部门。 会计人员需要从这些文件中提取信息并录入会计系统。 这就需要通过光学字符识别(OCR)技术,把图片转换为文本内容。 业界流行的 OCR 系统之一是 Tesseract,其核心由 C 和 C++ 实现。若要在 Python 项目中调用 Tesseract,可借助 PyTesseract 这类 Python 接口绑定。绑定库允许你在 Python 中直接调用底层非 Python 语言编写的库(如 Tesseract)。 目前市场上 OCR 工具种类繁多,效果受文档清晰度和底层算法影响很大。 Parseur 会自动检测你的文件是否为图片,并在后台自动进行 OCR 识别,为后续的数据抽取提供可用文本。

根据数据结构从发票中提取文本

当你的 PDF 已被转换为纯文本(或已可直接检索文本)后,可使用 pdftotext Python 库把 PDF 文件内容导出为文本。 下面是一个使用该库将 PDF 转换成文本的代码示例:

import pdftotext

# 加载你的发票
with open("invoice.pdf", "rb") as file_handle:
    pdf = pdftotext.PDF(file_handle)

# 遍历所有页面
for page in pdf:
    print(page)

将此脚本命名为 convert_pdf_to_text.py 并运行,即可在命令行输出发票文本。 如需输出到文件,可使用:

$ python convert_pdf_to_text.py > invoice.txt

取得纯文本发票内容后,就可以根据实际需求进一步提取信息了,常见方法包括:

  • 正则表达式进行内容提取。正则适合快速抓取数据,但对格式变动敏感,维护成本较高,尤其在表格类数据处理时局限较多。
  • 利用可视化模板系统,结合动态 OCR区域 OCR技术,实现更稳健的数据提取。不过开发和维护较复杂。
  • 或使用机器学习系统。机器学习适合大规模复杂文档处理,但需大量训练数据和人工校验,实施周期较长。

用 Python 的 re 模块配合正则表达式,可以快速从文本中提取关键信息。以下例子演示如何抓取发票号码:

import re

# 加载发票文本
with open("invoice.txt", "r") as file_handle:
    invoice = file_handle.read()

# 提取发票号
invoice_number = re.search(r"Invoice number: (\w+)", invoice).group(1)
print(invoice_number)

将脚本命名为 extract.py 并运行,终端将输出发票号:

$ python extract.py

你将看到类似如下结果:

INV-1234

提醒:这种方法仅适用于格式完全匹配 Invoice number: INV-1234 的发票。 若发票格式各异,你需要对正则表达式做出相应调整。 如遇多种版式,维护脚本的工作量会明显增加。

Parseur 可以为你极大节省这些重复性工作。

注册您的免费账户
使用 Parseur 节省时间和精力。自动处理您的文档。

如果你选择采用正则表达式进行内容提取,我们的模板引擎可协助管理和维护模板, 同时还可调取我们积累的丰富模板库资源。 你可按需选择:正则表达式、可视化模板(OCR)、或机器学习(AI 引擎)等多种方式,并能灵活组合,充分发挥各自优势。 Parseur 还支持结果的人工校对和反馈,帮助你持续提升数据提取的准确性和一致性。

收集已提取的数据

通过 Python,你可以循环遍历文件夹下全部发票文件,自动化进行数据提取。 如果只需提取如发票号码和总金额并输出为 CSV 文件,可使用如下代码:

import os
import re

import pdftotext

# 打印CSV列标题
print("InvoiceNumber,TotalAmount")

# 遍历文件夹下所有PDF文件
for filename in os.listdir("invoices/"):
    if not filename.endswith(".pdf"):
        continue

    # 加载发票
    with open("invoices/" + filename, "rb") as file_handle:
        pdf = pdftotext.PDF(file_handle)

    # 遍历所有页面
    for page in pdf:
        # 提取发票号
        invoice_number = re.search(r"Invoice number: (\w+)", page).group(1)
        total_amount = re.search(r"Total amount: (\w+)", page).group(1)
        print(invoice_number, total_amount, sep=",")

把这个脚本保存为 extract_to_csv.py,执行后可在命令行输出所有发票的编号与金额。 结果可重定向到 CSV 文件,在 Excel 或其他表格工具中打开:

$ python extract_to_csv.py > invoices.csv

在 Parseur 里,所有发票数据信息抽取后,可一键下载为表格, 或者通过 Webhook 集成MakeZapierMicrosoft Power Automate 等方案自动对接你的会计系统。也可以通过我们的 API 按 JSON 格式获取数据。

结论

希望本文对你有所帮助! 总的来说,使用 Python 处理发票数据是一项复杂但可控的工作,前提是发票格式统一且有充足的开发和维护时间。 当面临发票格式多样、时间和精力有限时, Parseur 可凭借多年自动化文档处理经验,帮助你更高效、灵活和准确地批量提取各类发票数据。

最后更新于

基于AI的数据提取软件。
立即开始使用Parseur。

自动提取电子邮件、PDF和电子表格中的文本。
节省数百小时的手动工作。
体验AI赋能的工作自动化。

Parseur rated 5/5 on Capterra
Parseur.com has the highest adoption on G2
Parseur.com has the happiest users badge on Crozdesk
Parseur rated 5/5 on GetApp
Parseur rated 4.5/5 on Trustpilot