尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Python批量提取PDF表格到Excel:pdfplumber与pandas实战
简介这份资源面向需要批量处理PDF表格的Python学习者与办公自动化开发者提供从PDF中一键提取表格并导出Excel的完整实例。包内共14个文件以png截图、xlsx结果表、pdf样本、ipynb笔记本和py脚本为主压缩包约18.34MB涵盖年报类PDF与对应Excel输出便于对照验证提取效果。已有117人学习下载。读者可参考脚本与Notebook理解pdfplumber、pandas、openpyxl等库的配合方式掌握读取PDF、识别表格、构建DataFrame、写入Excel的完整链路并借助截图与结果文件排查格式不规范、数据缺失等常见问题提升数据处理效率。1. 批量提取 PDF 表格到 Excel一份能直接跑的 Python 实例拆解财务对账单、供应商报价单、检测报告这些 PDF 里的表格要一条条复制到 Excel手动干一下午眼睛都花了。这份 Python 实例解决的就是这个场景用 pdfplumber 定位表格线用 pandas 清洗数据最后批量写入 Excel。它适合经常处理报表的运营、财务、数据分析师也适合刚学完 Python 基础想找个真实项目练手的开发者。整个脚本不依赖付费接口本地跑数据不出机器。下面按「环境怎么搭、表格怎么抽、坑怎么绕」的顺序拆开讲每一步都能直接抄。2. 环境搭建与依赖选型为什么是 pdfplumber 而不是 PyPDF22.1 三个库的分工与安装处理 PDF 表格这件事Python 生态里能选的库不少但真正能打的就三个pdfplumber 负责抽取表格结构pandas 负责数据清洗和格式转换openpyxl 负责写 Excel。PyPDF2 只能读文本流遇到有线框的表格直接抓瞎camelot 依赖 Ghostscript装起来容易翻车。pdfplumber 基于 pdfminer.six能识别表格的横竖线对规则表格的还原度最高。安装命令一行搞定pip install pdfplumber pandas openpyxl这里有个版本兼容的坑pdfplumber 0.10.x 和 pdfminer.six 20221105 之后的版本配合最稳。如果装完 import 报cannot import name PDFObjRef大概率是 pdfminer.six 版本太老升级一下就行pip install --upgrade pdfminer.six参数说明--upgrade会拉取最新版但生产环境建议锁版本比如pdfminer.six20221105避免某天自动升级后接口变了。2.2 验证环境是否可用装完别急着写业务代码先跑一个最小验证确认库能正常加载import pdfplumber import pandas as pd import openpyxl print(pdfplumber:, pdfplumber.__version__) print(pandas:, pd.__version__) print(openpyxl:, openpyxl.__version__)逻辑说明这段代码只做一件事——确认三个库都能 import 且打印版本号。如果某一行报ModuleNotFoundError说明对应的库没装上回到 2.1 重新装。版本号打印出来是为了后面排查问题时有个依据比如 pdfplumber 0.5.x 和 0.10.x 的extract_tables返回结构有差异知道版本能少走弯路。常见做法是建一个requirements.txt把版本钉死pdfplumber0.10.3 pandas2.1.4 openpyxl3.1.2这样换机器或者给同事复现时pip install -r requirements.txt一把过不会出现「我这能跑你那报错」的玄学问题。3. 表格抽取核心逻辑extract_tables 的参数怎么调3.1 单页表格抽取与 DataFrame 转换pdfplumber 的核心方法是page.extract_tables()它返回一个嵌套列表每个元素是一张表表里每一行是一个列表。直接转 DataFrame 就能用import pdfplumber import pandas as pd def extract_tables_from_pdf(pdf_path): all_tables [] with pdfplumber.open(pdf_path) as pdf: for page_num, page in enumerate(pdf.pages, start1): # 抽取当前页所有表格 tables page.extract_tables() for table in tables: if not table or len(table) 2: continue # 跳过空表或只有表头的表 # 第一行作为列名其余作为数据 df pd.DataFrame(table[1:], columnstable[0]) df[来源页] page_num # 标记数据来自哪一页 all_tables.append(df) return all_tables tables extract_tables_from_pdf(report.pdf) for i, df in enumerate(tables): print(f第 {i1} 张表{df.shape[0]} 行 x {df.shape[1]} 列) print(df.head())逻辑说明pdfplumber.open以上下文管理器打开文件确保用完自动释放。enumerate(pdf.pages, start1)给每页编号方便追溯数据来源。extract_tables()不带参数时用默认的线框检测策略适合大多数有线表格。table[1:]跳过表头行columnstable[0]把第一行当列名。加一列「来源页」是个实用习惯后面核对数据时能快速定位。参数说明extract_tables()支持table_settings参数常用配置如下参数作用适用场景vertical_strategy竖线检测策略lines默认有线框、text按文字间距horizontal_strategy横线检测策略lines默认、textsnap_tolerance线条吸附容差表格线模糊时调大默认 3join_tolerance线段合并容差虚线表格调大默认 3min_words_vertical竖策略为 text 时最少词数默认 3如果表格没有线框只有文字对齐就得改成table_settings { vertical_strategy: text, horizontal_strategy: text, min_words_vertical: 2, min_words_horizontal: 1, } tables page.extract_tables(table_settings)3.2 多页批量处理与 Excel 写入单张表抽出来只是第一步实际场景往往是几十个 PDF 每个几十页。批量处理要解决两个问题文件遍历和 Excel 分 sheet 写入。import os import glob import pdfplumber import pandas as pd def batch_extract_to_excel(input_dir, output_path): pdf_files glob.glob(os.path.join(input_dir, *.pdf)) if not pdf_files: print(没找到 PDF 文件检查路径) return with pd.ExcelWriter(output_path, engineopenpyxl) as writer: for pdf_file in pdf_files: base_name os.path.splitext(os.path.basename(pdf_file))[0] with pdfplumber.open(pdf_file) as pdf: for page_num, page in enumerate(pdf.pages, start1): tables page.extract_tables() for t_idx, table in enumerate(tables): if not table or len(table) 2: continue df pd.DataFrame(table[1:], columnstable[0]) # sheet 名不能超过 31 字符且不能含特殊字符 sheet_name f{base_name}_p{page_num}_t{t_idx}[:31] df.to_excel(writer, sheet_namesheet_name, indexFalse) print(f完成输出到 {output_path}) batch_extract_to_excel(./pdfs, ./output.xlsx)逻辑说明glob.glob匹配目录下所有 PDFos.path.splitext去掉扩展名拿文件名。pd.ExcelWriter用 openpyxl 引擎支持在一个文件里写多个 sheet。sheet 名拼接了文件名、页码、表序号方便追溯。[:31]是硬性截断Excel 的 sheet 名上限就是 31 个字符超了直接报错。参数说明to_excel的indexFalse表示不写行索引否则 Excel 里会多一列 0、1、2。如果数据量大ExcelWriter可以加modea追加模式但要注意文件必须已存在。4. 避坑与排查表格抽取翻车的五个真实场景4.1 现象抽出来的表格全是空行或乱码原因PDF 是扫描件本质是图片没有文字层。pdfplumber 只能处理有文字层的 PDF扫描件需要先做 OCR。解决用 pdfplumber 的page.images检查是否有图片对象如果有且extract_text()返回空基本可以判定是扫描件。常见做法是先过一遍 OCR 工具转成文字层 PDF再走抽取流程。判断代码with pdfplumber.open(scan.pdf) as pdf: page pdf.pages[0] text page.extract_text() if not text or len(text.strip()) 10: print(疑似扫描件需要 OCR 预处理)4.2 现象表格列错位数字跑到别的列里原因PDF 里表格线不完整或者单元格内有换行导致 pdfplumber 把一行拆成两行。解决先调snap_tolerance和join_tolerance从默认 3 调到 5 或 8让断开的线能接上。如果还不行改用text策略按文字位置切分。另外可以在extract_tables之后加一步清洗把空字符串替换成None再用dropna去掉全空行df df.replace(, None).dropna(howall)4.3 现象中文列名变成乱码或问号原因PDF 内嵌字体编码问题pdfplumber 解码时拿不到正确的 Unicode 映射。解决这种情况比较棘手先确认 PDF 是不是标准字体。可以试page.extract_text(layoutTrue)看文本层是否正常。如果文本层正常但表格抽取乱码尝试升级 pdfplumber 到最新版新版本对 CJK 字体的处理有改进。实在不行用pdfplumber抽文本再自己按位置解析放弃extract_tables。4.4 现象Excel 写入报ValueError: Sheetname must be a string原因sheet 名拼接时用了非字符串类型或者包含 Excel 不允许的字符\ / * ? [ ] :。解决写一个清洗函数把非法字符替换掉import re def clean_sheet_name(name): name re.sub(r[\\/*?\[\]:], _, str(name)) return name[:31] sheet_name clean_sheet_name(f{base_name}_p{page_num})4.5 现象大文件处理到一半内存爆了原因pdfplumber会把页面对象缓存在内存里几百页的 PDF 加上多个 DataFrame 同时存在内存直接拉满。解决逐页处理处理完一页就把page对象释放DataFrame 及时写入 Excel 而不是全部攒在列表里。上面的批量代码已经是流式写入如果还爆可以加gc.collect()手动触发回收或者把 PDF 拆成小文件分批跑。注意pdfplumber 打开文件后如果不关闭Windows 下会锁文件后续想移动或删除会报「文件被占用」。务必用with语句。5. 进阶技巧合并多表与格式保留5.1 跨页表格合并很多报表的表格是跨页的第一页有表头第二页直接续数据。处理思路是检测后续页的第一行是否与首页表头一致如果一致就跳过表头直接拼数据。def merge_cross_page_tables(pdf_path): merged_rows [] header None with pdfplumber.open(pdf_path) as pdf: for page in pdf.pages: tables page.extract_tables() for table in tables: if not table: continue if header is None: header table[0] merged_rows.extend(table[1:]) else: # 如果当前表头和已存表头一致跳过表头行 if table[0] header: merged_rows.extend(table[1:]) else: merged_rows.extend(table) return pd.DataFrame(merged_rows, columnsheader)逻辑说明用一个header变量记住第一张表的表头后续表如果表头相同就只取数据行不同则视为新表全部保留。这个逻辑适合结构一致的连续报表如果 PDF 里混了多种表格需要加判断条件区分。5.2 写入 Excel 时保留数字格式pdfplumber 抽出来的全是字符串写入 Excel 后数字左对齐、无法求和。可以在to_excel之前做类型转换def convert_types(df): for col in df.columns: # 尝试转数字失败就保留字符串 df[col] pd.to_numeric(df[col], errorsignore) return df df convert_types(df) df.to_excel(writer, sheet_namesheet_name, indexFalse)参数说明errorsignore表示转换失败时保留原值不会报错。如果确定某列是数字可以用errorscoerce把失败值变成 NaN再统一填充。5.3 验证抽取结果是否完整抽完之后怎么确认没漏数据我一般用两个办法一是对比 PDF 页数和 Excel 里有数据的 sheet 数二是随机抽几页人工核对行数。还可以写个校验脚本统计每张表的行数和列数跟 PDF 里肉眼看到的做个粗略对比def validate_extraction(pdf_path, excel_path): with pdfplumber.open(pdf_path) as pdf: pdf_table_count sum(len(p.extract_tables()) for p in pdf.pages) xl pd.ExcelFile(excel_path) print(fPDF 中检测到 {pdf_table_count} 张表Excel 中有 {len(xl.sheet_names)} 个 sheet) for sheet in xl.sheet_names: df xl.parse(sheet) print(f {sheet}: {df.shape[0]} 行 x {df.shape[1]} 列)从那以后我每次跑完批量抽取都强制走一遍这个校验确认表数量和行数量级对得上再交付。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

Docker 常用命令速查

Docker 常用命令速查

Docker 常用命令速查 一、镜像(Image) # 搜索镜像 docker search nginx# 拉取镜像(不指定标签默认 latest) docker pull nginx docker pull nginx:1.25# 查看本地镜像 docker images docker image ls# 删除镜像 docker rmi nginx:…

📅 2026/10/18 14:08:13
强化学习智能体 vs 大模型智能体:选型、落地与避坑指南

强化学习智能体 vs 大模型智能体:选型、落地与避坑指南

简介:由快手科技快意大模型知识增强研发负责人毛航宇分享的《强化学习(多)智能体 和 大语言模型(多)智能体》PDF,系统梳理了近十年强化学习智能体与LLM智能体的研究脉络,并沿Deep RL、Transformer-based RL、LLM-based AI Agent的演化路径选讲…

📅 2026/10/18 14:08:13
Idea断点调试(debug)详解:从条件断点到远程调试的完整实践

Idea断点调试(debug)详解:从条件断点到远程调试的完整实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/18 14:08:13
MORE NEWS

更多资讯

📰

Java中javac和JIT编译到底有何区别?一文讲透两阶段编译机制

1. 别再把两件事混为一谈了:先分清“编译”的两个含义只要写过Java的人,基本都干过这两件事:一是执行javac把.java文件编译成.class文件,二是在IDE里点一下运行,然后看着程序跑起来。表面上看,这两个动作都…

📰

在 Python 编程中,字符串是最基础也最常用的数据类型之一

在 Python 编程中,字符串是最基础也最常用的数据类型之一。无论是数据处理、日志记录、用户交互还是报表生成,都离不开对字符串的格式化输出和清洗操作。str.format() 方法提供了强大的格式化能力,可以控制字段宽度、对齐方式、小数精度等&am…

📰

AI工具装了没人用?从接入成本到习惯养成的落地指南

“AI工具装好了,但团队没人用”——这句话我这两年听过太多次。几乎每个想推AI落地的开发团队,都会撞上同一个尴尬:工具选型报告写得漂漂亮亮,部署文档比需求文档还细,结果上线两周后后台一看,日活不到两位…

📰

归并排序递归与非递归实现:从分治原理到工程化优化

搞过一次大数据量排序之后,我才真正意识到递归版归并排序在教学上和工程上完全是两码事。理论课上讲归并排序,教科书永远是递归写法,简洁漂亮;但真到了处理大批量数据的时候,频繁的临时数组分配和函数调用开销会让人想…

📰

函数是Python编程的核心构建块,也是程序设计中实现代码复用、模块化和抽象化的重要手段

函数是Python编程的核心构建块,也是程序设计中实现代码复用、模块化和抽象化的重要手段。本报告将系统性地探讨Python函数的声明与调用机制、参数传递原理、变量作用域规则以及静态变量(static)的实现方式。通过丰富的代码示例和深入的原理解…

📰

切片级漏洞检测:用GNN实现函数内精准漏洞定位

简介:本资源是一套面向计算机及相关专业(如信息安全、人工智能、软件工程)在校学生与初阶研究者的毕业设计级项目,聚焦图神经网络在源代码切片层面的漏洞检测与可解释性分析。项目完整实现从C语言代码预处理、Joern生成PDG图与lin…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬