PDF 里的表格和 Excel 表格并不是同一种数据。PDF 文件通常不保存结构化表格,页面上的行与列只是文字、线条按坐标呈现出来的视觉效果。因此,用脚本读取 PDF 表格,本质上是通过库分析页面版式,再还原行列结构。
一、环境准备
安装 Spire.PDF:
如果只是学习或处理小文档,也可以使用免费版本:
- pip install Spire.Pdf.Free
复制代码
免费版在加载和处理 PDF 时有页数限制,最多处理前 10 页。超过 10 页的文档需要先拆分,或改用完整版 PyPI。
二、核心提取代码
下面代码演示加载 PDF、逐页提取表格,并按行输出单元格文本:
- from spire.pdf import PdfDocument, PdfTableExtractor
- # 加载 PDF 文档
- pdf = PdfDocument()
- pdf.LoadFromFile("input.pdf")
- # 创建表格提取器
- table_extractor = PdfTableExtractor(pdf)
- # 逐页提取表格
- for i in range(pdf.Pages.Count):
- tables = table_extractor.ExtractTable(i)
- for table_index, table in enumerate(tables):
- print(f"Table {table_index + 1} on page {i + 1}:")
- for row in range(table.GetRowCount()):
- row_data = []
- for col in range(table.GetColumnCount()):
- text = table.GetText(row, col).replace("\n", " ")
- row_data.append(text.strip())
- print("\t".join(row_data))
复制代码
三、关键步骤说明
1. 加载 PDF 文档
PdfDocument 是文档入口对象,LoadFromFile() 接收 PDF 路径,相对路径和绝对路径都可以。
2. 创建表格提取器
PdfTableExtractor 用来对文档逐页分析版式并检测表格结构,构造时需要传入已经加载的 PdfDocument 对象。
3. 逐页提取表格
pdf.Pages.Count 表示总页数,ExtractTable(i) 返回第 i 页检测到的所有表格。它返回的是列表,因为一页中可能有多个表格;如果该页没有表格,则返回空列表。
4. 遍历行列读取单元格
GetRowCount() 和 GetColumnCount() 分别获取行数、列数;GetText(row, col) 读取指定单元格文本。单元格内可能包含换行,代码先把 \n 替换为空格,再 strip() 去掉首尾空白,避免输出多余空行。最后用制表符 \t 拼接一行单元格,方便复制到 Excel 或作为 TSV 使用。
四、运行效果
对一份包含简单表格的 PDF 运行上述代码,控制台输出大致如下:
- Table 1 on page 1:
- 姓名 部门 薪资
- 张三 技术部 12000
- 李四 市场部 9800
- 王五 财务部 10500
复制代码
每行对应表格中的一行,单元格之间由制表符分隔,可以直接粘贴到表格软件。
五、进阶:保存为 CSV
如果不想只打印到控制台,可以用 Python 内置 csv 模块写入 CSV,不需要额外安装库:
- import csv
- from spire.pdf import PdfDocument, PdfTableExtractor
- pdf = PdfDocument()
- pdf.LoadFromFile("input.pdf")
- extractor = PdfTableExtractor(pdf)
- with open("output.csv", "w", newline="", encoding="utf-8") as f:
- writer = csv.writer(f)
- for i in range(pdf.Pages.Count):
- for table in extractor.ExtractTable(i):
- for row in range(table.GetRowCount()):
- row_data = [
- table.GetText(row, col).replace("\n", " ").strip()
- for col in range(table.GetColumnCount())
- ]
- writer.writerow(row_data)
- pdf.Close()
复制代码
csv.writer 会自动处理单元格中的逗号、引号等特殊字符,通常比手动拼接字符串更可靠。批量处理完成后,建议调用 pdf.Close() 释放资源。
六、注意事项与局限
1. 更适合有清晰边框的表格。PdfTableExtractor 依赖页面版式分析,对边框明确、结构规整的表格识别效果较好;无可见边框、多行单元格或表头不明确的复杂排版,可能出现检测不到或结构不完整。
2. 扫描件需要先 OCR。如果 PDF 是扫描图片、没有文本层,文本类工具无法直接读取内容,需要先用 OCR 把图片转成文字,Spire.PDF 本身不提供 OCR。
3. 免费版页数限制。免费版最多处理前 10 页,长文档可以在循环里使用 min(pdf.Pages.Count, 10) 限制,或先按页拆分。
4. 资源释放。批量处理大量文件时,最后调用 pdf.Close(),避免资源占用。
七、其他可选库
如果遇到 Spire.PDF 处理不了的复杂表格,可以尝试以下开源库作为补充:
pdfplumber:基于 pdfminer.six,提供较细粒度的页面对象访问,适合复杂排版。
camelot:通过检测表格线识别结构,对有线表格效果较好。
tabula-py:封装 Java 的 tabula,输出可直接兼容 pandas DataFrame。
实际项目中可以按表格类型组合使用:结构规整的表格任选一个库即可,复杂表格建议先小样本测试再选型。
八、总结
用 Spire.PDF for Python 提取 PDF 表格的流程是:加载文档,创建 PdfTableExtractor,逐页调用 ExtractTable(),再遍历行列读取单元格。它适合边框清晰、结构规整的表格,配合 csv 模块即可落盘保存。遇到无边框、扫描件等情况,则需要 OCR 或其他 PDF 解析库配合。 |