You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多页不规则PDF发票数据提取至Excel的Python技术求助

解决不规则PDF发票数据提取问题

核心问题分析

原代码的主要问题包括:

  • 正则表达式分组错位,导致提取字段混乱
  • 正则规则过于严格,无法适配格式波动的发票行
  • 缺失值未按要求填充为0
  • 输出列名与目标要求不匹配

修正后的代码实现

import re
import pdfplumber
import pandas as pd
from collections import namedtuple

# 匹配目标列名的namedtuple,与需求完全对应
Inv = namedtuple('Inv', [
    'area_name', 'Date', 'Tran', 'Inv #', 'Product', 
    'Description', 'Packing', 'Rate', 'Qty', 'Bonus', 
    'Gross', 'Discount', 'Sales', 'Tax', 'Net Amt'
])

# 预定义区域名称正则,保留精确匹配
area_pattern = re.compile(
    r'\b(Chichawatni|Harrapa|Kasowal|Mianchannu|Malka Hans|Gogera|Qabula|Arif Wala|Sahiwal|90 Morh|S1 High Street|S2 Mission Chowk|S3 DHQ Hospital|S4 Farid Town|S5 Jahaz Ground|S7 Karbala Road|S7 Mazdoor Puli|S8 TBZ Colony)\b',
    re.IGNORECASE
)

# 优化后的发票行正则:适配可选字段、多空格、格式波动
line_pattern = re.compile(
    r'(\d{2}-[A-Za-z]{3}-\d{4})'  # Date
    r'\s+(Sale|Return)?'          # Tran(可选)
    r'\s*(\S+)'                   # Inv #
    r'\s+([A-Za-z0-9\-\s]+?)'     # Product(非贪婪匹配避免溢出)
    r'\s+([A-Za-z0-9\-\s]+?)'     # Description
    r'\s+(\d+[A-Za-z]+)'          # Packing
    r'\s+([\d\.]+)'               # Rate
    r'\s+(\d+)'                   # Qty
    r'\s+([\d\.]+)'               # Bonus
    r'\s+([\d\.]+)'               # Gross
    r'\s+([\d\.]+)'               # Discount
    r'\s+([\d\.]+)'               # Sales
    r'\s+([\d\.]+)'               # Tax
    r'\s+([\d\.]+)'               # Net Amt
    , re.IGNORECASE
)

lines = []

with pdfplumber.open('AK PHARMA  (1).pdf') as pdf:
    for page in pdf.pages:
        text = page.extract_text()
        if not text:
            continue
        current_area = None
        
        for line in text.split('\n'):
            line = line.strip()
            if not line:
                continue
            
            # 匹配区域名称
            area_match = area_pattern.search(line)
            if area_match:
                current_area = area_match.group(0).strip()
                continue
            
            # 匹配发票数据行
            line_match = line_pattern.match(line)
            if line_match and current_area:
                # 提取所有分组,缺失字段填充为0
                groups = list(line_match.groups())
                # 处理可选的Tran字段
                if groups[1] is None:
                    groups[1] = '0'
                # 数值字段兜底处理
                for i in range(3, len(groups)):
                    if groups[i] is None:
                        groups[i] = '0'
                
                # 构造Inv对象
                inv_entry = Inv(current_area, *groups)
                lines.append(inv_entry)

# 转换为DataFrame并处理缺失值
df = pd.DataFrame(lines)
# 数值列转换为float并填充缺失值
numeric_cols = ['Rate', 'Qty', 'Bonus', 'Gross', 'Discount', 'Sales', 'Tax', 'Net Amt']
df[numeric_cols] = df[numeric_cols].apply(pd.to_numeric, errors='coerce').fillna(0)
# Tran字段缺失填充0
df['Tran'] = df['Tran'].fillna('0')

# 输出为Excel文件
df.to_excel('AK_Pharma_Invoices.xlsx', index=False)
print("数据提取完成,已保存为AK_Pharma_Invoices.xlsx")

关键优化点说明

  • 正则修正:调整分组顺序,确保每个字段对应正确位置;使用非贪婪匹配解决字段溢出问题;标记Tran为可选字段,适配无交易类型的行。
  • 缺失值处理:通过fillna(0)统一填充所有缺失的数值和文本字段,符合需求。
  • 列名对齐:完全匹配目标列名,避免后续映射麻烦。
  • 鲁棒性提升:增加空文本、空行的跳过逻辑,避免无效循环。

内容的提问来源于stack exchange,提问作者Hannan Wali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 12:29:51