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]} 列)从那以后我每次跑完批量抽取都强制走一遍这个校验确认表数量和行数量级对得上再交付。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

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:20 阅读更多 →
强化学习智能体 vs 大模型智能体:选型、落地与避坑指南

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

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

2026/10/18 14:08:20 阅读更多 →
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:20 阅读更多 →

最新新闻

无代码编辑器:拖拽搭建小程序、H5与网站,企业级应用快速生成

无代码编辑器:拖拽搭建小程序、H5与网站,企业级应用快速生成

简介:这份资源是一套面向无代码开发场景的完整编辑器项目包,适合希望快速搭建小程序、H5页面与网站的企业团队及个人开发者,尤其对缺乏编程基础、又想低成本验证业务想法的人群友好。压缩包共677个文件,约5.67MB,以tsx…

2026/10/18 14:51:49 阅读更多 →
diagram-design:从草图到系统,用信息设计思维画好每一张图

diagram-design:从草图到系统,用信息设计思维画好每一张图

1. 从一张草图到一套系统:diagram-design 到底在解决什么问题第一次看到 “diagram-design” 这个标题,很多人会以为它只是教你怎么把图画得好看一点。但真正做过系统设计、写过技术方案、跟产品经理吵过架构图的人都知道,画图这件事从来不只…

2026/10/18 14:51:49 阅读更多 →
图表设计方法论:从信息分层到可维护资产的工程实践

图表设计方法论:从信息分层到可维护资产的工程实践

1. 从"diagram-design"这个标题里能读出什么第一次看到"diagram-design"这个标题,我脑子里蹦出来的第一个判断是:这大概率不是一个单纯的"画图工具推荐",而是一个关于图表设计方法论的项目。为什么这么说&…

2026/10/18 14:51:49 阅读更多 →
Artcraft:意图锚定、过程可见与材料敬畏的创作新范式

Artcraft:意图锚定、过程可见与材料敬畏的创作新范式

1. “Artcraft”不是新词,而是被低估的创作范式重构信号最近在多个设计社区、开源项目讨论区和创意工作坊里,“artcraft”这个词频繁跳出来,但没人给它下过准确定义。它不像“generative design”或“creative coding”那样有清晰的技术边界&…

2026/10/18 14:51:49 阅读更多 →
C++ TCP服务端从零实现:核心API、粘包处理与高并发优化

C++ TCP服务端从零实现:核心API、粘包处理与高并发优化

1. 从零拆解一个TCP服务端到底要写哪些东西很多人第一次接触网络编程,脑子里冒出来的第一个问题不是"怎么写",而是"我到底要写几行代码才能让两个程序说上话"。我当初也是这么想的。后来真正把C的socket API撸了一遍才发现&#xff…

2026/10/18 14:51:49 阅读更多 →
接口安全设计:从Cookie到参数签名防篡改的核心机制

接口安全设计:从Cookie到参数签名防篡改的核心机制

1. 接口安全设计思路拆解:Cookie、签名与“加密函数”到底在防谁先说一个很多人容易混淆的点:我们平时说“参数加密”“签名函数”,其实绝大多数场景下并不是在做那种密文不可见的加密,而是做“篡改可识别”。换句话说&#xff0c…

2026/10/18 14:50:48 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* 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 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* 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 12:16:16 阅读更多 →