🔒 You must be logged in as an Administrator or Editor to listen to this audio.
Python 高级 PDF 解析工具逐行讲解
我会逐行、逐段为你详细讲解这段 Python 代码,用最通俗的方式解释每一部分的作用、逻辑和用途,让你完全看懂这段高级 PDF 解析工具代码。
整体功能一句话总结
这是一个类的方法,功能是:高级读取 PDF 文件,同时提取 3 种内容:
- 纯文本
- 表格(转成 Markdown 格式)
- 图片(保存成本地文件,并记录路径) 最后返回:拼接好的完整文本内容 + 图片附件列表。
逐行逐段完整讲解
1. 方法定义(函数签名)
def _read_pdf_advanced(self, filepath: str, doc_id: str) -> (str, List[Dict]):
def:定义函数/方法_read_pdf_advanced:方法名,下划线开头表示这是类内部使用的私有方法self:类方法必须有的参数,代表实例本身filepath: str:参数注解,PDF 文件路径,字符串类型doc_id: str:文档唯一 ID,用于给图片命名,避免重名-> (str, List[Dict]):返回值注解- 第一个返回值:字符串(完整文本)
- 第二个返回值:字典列表(所有图片附件信息)
2. 文档字符串(说明)
"""
高级 PDF 读取:按页提取文本、表格(HTML)和图片(保存为文件)
"""
- 说明这个方法的功能:按页提取文本、表格、图片
- 注释里写的 HTML 是笔误,实际代码里转成了 Markdown 表格
3. 初始化两个空列表
content_parts = [] # 存放所有文本、表格、图片标记
attachments = [] # 存放所有图片的信息(路径、页码等)
content_parts:最后会把所有内容拼接成一个大字符串attachments:专门存图片信息,方便外部使用
4. 创建附件文件夹(保存图片)
# 创建附件目录
if not os.path.exists(self.attachments_dir):
os.makedirs(self.attachments_dir)
- 检查是否有
attachments目录 - 没有就自动创建,所有提取的图片都会存在这里
5. 降级处理:依赖缺失时用最简单方式读取
if not self._pdfplumber_available or not self._fitz_available:
# 降级处理
from pypdf import PdfReader
reader = PdfReader(filepath)
for i, page in enumerate(reader.pages):
content_parts.append(page.extract_text() or "")
return "\n\n".join(content_parts), []
作用:兼容模式
- 如果系统没安装高级库(pdfplumber / PyMuPDF)
- 就自动降级,只用轻量库
pypdf只提取纯文本 - 不提取表格、不提取图片
- 最后返回:文本 + 空附件列表
6. 导入高级 PDF 处理库
import pdfplumber
import fitz
pdfplumber:专门提取文本和表格,准确率极高fitz(PyMuPDF):专门提取图片,速度快、效果好
7. 打开 PDF 文件(两个库同时打开)
pdf_plumber_obj = pdfplumber.open(filepath)
pdf_fitz_obj = fitz.open(filepath)
- 用两个库分别打开同一个 PDF
- 各司其职:
pdfplumber处理文字、表格fitz处理图片
8. try-finally 结构(保证文件一定会关闭)
try:
# 所有解析逻辑
finally:
pdf_plumber_obj.close()
pdf_fitz_obj.close()
- finally 最重要:无论代码是否报错,一定会关闭 PDF 文件
- 避免文件占用、内存泄漏
9. 逐页循环处理 PDF
for page_idx in range(len(pdf_plumber_obj.pages)):
page_num = page_idx + 1
content_parts.append(f"--- Page {page_num} ---")
- 一页一页处理
page_idx从 0 开始,page_num从 1 开始(符合人类阅读习惯)- 每页开头加标记:
--- Page 1 ---
10. 提取当前页的文本(pdfplumber)
# 1. 提取文本和表格 (pdfplumber)
plumber_page = pdf_plumber_obj.pages[page_idx]
text = plumber_page.extract_text() or ""
if text:
content_parts.append(text)
- 获取当前页对象
- 提取文本
- 有文本就加入内容列表
11. 提取当前页的表格 → 转成 Markdown
tables = plumber_page.extract_tables()
if tables:
for i, table in enumerate(tables):
md_table = self._list_to_md_table(table)
content_parts.append(f"\n[Table {i+1} on Page {page_num}]\n{md_table}")
extract_tables():把表格转成二维列表- 循环每一个表格
- 调用私有方法
_list_to_md_table()转成 Markdown 格式 - 加入内容列表,并标注:第几页、第几个表
示例效果:
[Table 1 on Page 2]
| 姓名 | 年龄 |
|------|------|
| 张三 | 20 |
12. 提取当前页的图片(fitz)
# 2. 提取图片 (fitz)
fitz_page = pdf_fitz_obj[page_idx]
image_list = fitz_page.get_images()
获取当前页
get_images():获取本页所有图片
13. 循环处理每一张图片
for img_index, img in enumerate(image_list, 1):
xref = img[0] # 图片唯一标识
pix = fitz.Pixmap(pdf_fitz_obj, xref)
处理 CMYK 色彩模式图片(避免报错)
if pix.n - pix.alpha > 3:
pix = fitz.Pixmap(fitz.csRGB, pix)
- 有些 PDF 图片是 CMYK 印刷模式,无法直接保存 PNG
- 这行代码自动转成 RGB
14. 给图片命名并保存
img_name = f"img_{doc_id}_p{page_num}_i{img_index}.png"
img_path = os.path.join(self.attachments_dir, img_name)
pix.save(img_path)
pix = None # 释放内存
- 命名规则:
img_文档ID_页码_图片序号.png例:img_20260407_p3_i2.png - 保存到
attachments/目录 pix = None释放图片内存
15. 记录图片信息到附件列表
attachments.append({
"type": "image", "name": img_name, "path": img_path, "page": page_num
})
content_parts.append(f"\n\n")
- 附件字典包含:类型、文件名、路径、页码
- 文本内容里加入 Markdown 图片标记:

16. 每页结束加分隔符
content_parts.append("\n") # 页面间距
17. 关闭文件(finally)
finally:
pdf_plumber_obj.close()
pdf_fitz_obj.close()
18. 最终返回结果
return "\n\n".join(content_parts), attachments
- 把所有内容用换行拼接成一个大字符串
- 返回:
(完整文本, 附件列表)
核心技术总结(超清晰)
| 库 | 作用 |
|---|---|
| pdfplumber | 提取文本、表格 |
| PyMuPDF(fitz) | 提取图片 |
| pypdf | 依赖缺失时降级只读文本 |
代码亮点
- 私有方法:
_开头,规范 - 类型注解:参数、返回值清晰
- 降级兼容:依赖缺失不崩溃
- try-finally:保证文件关闭
- 图片防重名:用 doc_id + 页码 + 序号命名
- 色彩兼容:CMYK → RGB
- 结构化输出:文本 + 附件分离
最终输出示例(文本部分)
--- Page 1 ---
这是第一页的文本内容
--- Page 2 ---
这是第二页文本
[Table 1 on Page 2]
| 姓名 | 年龄 |
|------|------|
| 小明 | 18 |

总结
- 这是一个健壮、完整、生产可用的 PDF 解析工具方法
- 同时提取文本 + 表格 + 图片
- 自带降级机制、异常保护、文件管理
- 输出格式友好,适合直接用于文档解析、知识库、RAG 系统
如果你需要,我还可以帮你:
- 补全缺失的
_list_to_md_table方法 - 写一个完整可运行的 demo
- 优化代码、增加日志、增加异常处理