当前位置: 代码网 > it编程>前端脚本>Python > Python如何提取PDF表格数据并写入SQLite数据库

Python如何提取PDF表格数据并写入SQLite数据库

2026年09月22日 Python 我要评论
处理 pdf 表格数据的场景很常见:季度报表、对账单、业务台账,业务方给一份 pdf 过来,需要结构化后进数据库做后续分析。本文分享一个完整的 python 实现,覆盖从表格提取、字段清洗、动态建表到

处理 pdf 表格数据的场景很常见:季度报表、对账单、业务台账,业务方给一份 pdf 过来,需要结构化后进数据库做后续分析。本文分享一个完整的 python 实现,覆盖从表格提取、字段清洗、动态建表到批量入库的全流程。代码依赖免费库 free spire.pdf 完成 pdf 解析,其余全部基于标准库,部署成本很低。

环境与库

pip install spire.pdf.free

导入方式:

from spire.pdf import *
from spire.pdf.common import *

需要提前说明两个能力边界,避免后面踩坑:

  • 免费版单文件最多处理前 10 页,超出部分不会返回。
  • 表格提取依赖 pdf 内的边框线结构,扫描件或无框线表格不适用,需走 ocr 方案。

整体流程

处理流程分三步:

  • 逐页提取所有表格的原始文本矩阵
  • 对表头做规范化、去重,生成合法列名
  • 每张表动态建表并批量插入

这样做的好处是,一份 pdf 里包含多张结构不同的表格时,不需要提前预知列结构,也能全部落入数据库。

文本清洗:先处理连字问题

pdf 里常见的一类坑是连字符(ligature)被替换成 unicode 私有区字符,例如 fiff 会变成 \ue005\ue000 之类的编码。直接入库就是一堆乱码方块,所以第一步要做归一化。

def normalize_text(text: str) -> str:
    if not text:
        return ""
    ligature_map = {
        '\ue000': 'ff', '\ue001': 'ft', '\ue002': 'ffi',
        '\ue003': 'ffl', '\ue004': 'ti', '\ue005': 'fi',
    }
    for k, v in ligature_map.items():
        text = text.replace(k, v)
    return text.strip()

映射表不需要一次穷举,实践中遇到新字符再补充即可。

列名规范化与去重

原始表头往往带空格、大小写混杂、甚至包含中文和符号,不能直接作为数据库列名。这里做两件事:

  • 用正则把非字母数字字符统一替换成下划线,空表头用 column_n 兜底
  • 处理重复列名(pdf 表格经常出现两个同名"金额"列)
def normalize_column_name(name: str, index: int) -> str:
    if not name:
        return f"column_{index}"
    name = name.lower()
    name = re.sub(r'[^a-z0-9]+', '_', name).strip('_')
    return name or f"column_{index}"
def deduplicate_columns(columns):

seen = set()

result = []

for col in columns:

base = col

count = 1

while col in seen:

col = f"{base}_{count}"

count += 1

seen.add(col)

result.append(col)

return result

去重逻辑采用后缀递增方式,amountamount_1amount_2,不会互相覆盖。

提取表格

pdftableextractor 是核心对象,按页调用 extracttable(page_index) 返回该页所有表格对象。每张表通过 getrowcount() / getcolumncount() 遍历,单元格文本用 gettext(row, col) 获取。

pdf = pdfdocument()
pdf.loadfromfile("quarterly sales.pdf")
extractor = pdftableextractor(pdf)
all_tables = []
for i in range(pdf.pages.count):
tables = extractor.extracttable(i)
if not tables:
continue
for table in tables:
table_rows = []
for row in range(table.getrowcount()):
row_data = [
normalize_text(table.gettext(row, col))
for col in range(table.getcolumncount())
]
table_rows.append(row_data)
if table_rows:
all_tables.append(table_rows)
pdf.close()
if not all_tables:
raise valueerror("no tables found in pdf.")

注意 pdf.close() 的位置——批量处理多份文件时这一步很关键,否则内存占用会持续累积。

动态建表 + 批量插入

不同表格的列结构不同,用 table_n 命名并动态生成 ddl 是最省事的做法。所有列统一声明为 text,把类型推断推迟到查询阶段处理。

conn = sqlite3.connect("sales_data.db")
cursor = conn.cursor()
for table_index, table in enumerate(all_tables):
if len(table) < 2:
continue  # 只有表头没有数据行的表直接跳过
raw_headers = table[0]
normalized_headers = [
normalize_column_name(h, i) for i, h in enumerate(raw_headers)
]
normalized_headers = deduplicate_columns(normalized_headers)
table_name = f"table_{table_index + 1}"
columns_def = ", ".join([f'"{col}" text' for col in normalized_headers])
cursor.execute(f"""
create table if not exists "{table_name}" (
id integer primary key autoincrement,
{columns_def}
)
""")
placeholders = ", ".join(["?" for _ in normalized_headers])
column_names = ", ".join([f'"{col}"' for col in normalized_headers])
insert_sql = f"""
insert into "{table_name}" ({column_names})
values ({placeholders})
"""
batch = []
for row in table[1:]:
if not any(row):
continue
values = [
row[i] if i < len(row) else ""
for i in range(len(normalized_headers))
]
batch.append(values)
if batch:
cursor.executemany(insert_sql, batch)
print(f"inserted {len(batch)} rows into {table_name}")
conn.commit()
conn.close()
print(f"processed {len(all_tables)} tables from pdf.")

几个实现细节值得展开:

if len(table) < 2 跳过空表。 pdf 中有时会提取出只有表头、甚至只有一行空字符串的伪表格,提前过滤掉能避免无意义建表。

列数对齐取最小值。 当某行的列数少于表头时,row[i] if i < len(row) else "" 补齐空字符串。反过来如果某行列数多于表头,多出的部分会被直接丢弃——这在合并单元格场景下会出现,实践中建议加一条日志记录列数异常的行号,方便后续人工核对。

executemany 批量插入。 单条 insert 在一张几百行的表上就会明显变慢,批量执行能显著降低 sqlite 事务开销。

验证结果

跑完后直接用 sqlite3 命令行看数据:

sqlite3 sales_data.db ".tables"
sqlite3 sales_data.db "select * from table_1 limit 5;"

可以继续优化的方向

类型转换。 目前全部以 text 入库,如果后续要按数值排序或聚合,可以在插入前做一次类型推断:

def try_parse(value: str):
    try:
        return float(value.replace(",", ""))
    except (valueerror, attributeerror):
        return value

values 列表在入 batch 前做一次映射,能让纯数字列以 real 类型存储,查询时就不需要 cast 了。

表名语义化。table_1table_2 只是最省事的方案。如果 pdf 中每张表上方有标题文本,可以提取后作为表名,读起来更直观。

多文件批处理。 把主流程封装成 process_pdf(path, conn) 函数,外层套一层文件遍历即可。注意每个文件都要 loadfromfile → 提取 → close(),不要复用 pdfdocument 实例。

整套方案除 pdf 解析库外没有任何第三方依赖,适合嵌入现有的数据处理脚本,也方便在容器里跑。如果你的场景是页数不多、结构相对规范的 pdf 报表,这份代码基本可以直接拿去改几个常量投入使用。

以上就是python如何提取pdf表格数据并写入sqlite数据库的详细内容,更多关于python提取pdf表格数据的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com