Python提取PDF图片实战:PyMuPDF批量处理高清原图教程
2026/9/20 7:03:50 网站建设 项目流程

在日常办公和数据分析中,PDF 文件里不仅有文字,还经常包含大量插图、截图、产品图、图表和扫描件。如果只需要其中的某张图片,很多人的第一反应是把整个 PDF 转成图片后手动截图,或者使用在线“PDF 图片提取工具”。但这样不仅效率低,而且可能涉及文件上传隐私问题,图片质量也会被压缩。更重要的是,当需要批量处理几十、上百个 PDF 时,手工操作几乎不可行。

本文围绕“python提取PDF中的图片”这一主题,给出从方案选型、环境配置、代码实现到业务落地的完整教程。你将学会用 Python 实现“读取 PDF 页面 -> 定位内部图片对象 -> 保存为高质量图片文件”的自动化流程,也会了解不同 PDF 结构(文本型 PDF 与扫描版 PDF)的区别,以及为什么有些 PDF 无论如何都无法用简单方法提取出清晰的图片。

无论你是刚接触 Python 的初学者,还是已经在做文档处理、知识管理、自动化办公的开发者,这篇文章都可以作为一份可直接取用的工具教程。

1. 提取 PDF 图片的核心思路与概念

1.1 为什么直接“截图”不可取

PDF 本质上是一个以“页面”为单位的文档容器,页面上的文字、线条、图片都被组织成不同的内容对象。当我们用 PDF 阅读器查看时,看到的是渲染后的结果,而阅读器通常并不提供“导出原图”功能。很多人于是采取以下替代方案:

  1. 放大 PDF 后用系统截图工具截图,得到的图片分辨率受屏幕限制。
  2. 使用「打印 -> 另存为 PNG」等方式,把整个页面输出成图片,再二次裁剪。
  3. 使用在线 PDF 图片提取网站,需要上传文件,批量有限制。

这些方法都绕了一步弯路:图片对象本来就在 PDF 内部,只是没有暴露出来。我们需要用编程方式直接读取 PDF 内部结构,把“被嵌在页面里的图片”原样或者高质量地还原出来。

1.2 文本型 PDF 与扫描版 PDF 的本质区别

在开始写代码前,必须先区分两类 PDF:

  • 文本型/数字型 PDF:由 Word、LaTeX、排版软件或代码直接生成,内容由文字编码和图片对象组成。图片在 PDF 中往往以 JPEG、JPX、CCITT、FLATE 等编码方式存储,可以被解析并还原。
  • 扫描版/图像型 PDF:本质上是把每一页都保存成一张大照片,再套一个 PDF 外壳。页面里没有“文字对象”,整页内容就是一张图片。这类 PDF 用解析对象的方式提取图片,得到的往往是“整页图片”,因为原图就是整页扫描图。

因此,下面的两种策略分别适用于不同类型:

  1. 基于 PDF 对象结构解析,提取页面内的图片对象。
  2. 将页面渲染成高清位图,再保存为图片。

如果你想提取的是 PDF 中“包含的插图文件”,优先使用方法 1;如果 PDF 本身是扫描件,或者页面是矢量化排版,没有独立图片对象,就需要使用方法 2,把整页渲染成图。

1.3 常见第三方库选择

Python 生态中与 PDF 相关的库很多,但在“提取图片”这个需求上,最常用、踩坑最少的是下面几个:

库名主要用途优势局限
PyMuPDF(fitz)PDF 解析、渲染、提取文本与图片速度快,API 简单,图片提取质量可控安装包较大,依赖 MuPDF
pdfplumber提取文本、表格文本和表格解析方便官方图片提取能力较弱,不适合单独用来提图
PyPDF2 / pypdfPDF 合并、拆分、页面操作轻量图片提取不是其核心能力,且 PyPDF2 维护速度较慢
fitz + Pillow 组合图像处理和保存可把原生图像数据交 PIL 转换,统一格式需要额外安装 Pillow

综合性能和易用性,推荐以 PyMuPDF 为主。它的接口名fitz是历史遗留,实质就是封装了 MuPDF C 库。用它可以做到:

  1. 获取 PDF 页面对象。
  2. 获取页面中的图片引用列表。
  3. 读取原始图片二进制数据。
  4. 访问图片扩展名、宽高和色彩空间。
  5. 直接把图片保存为 PNG / JPEG 文件。

如果图片对象内部以加密的 DCTDecode(JPEG)格式存储,我们还可以想办法拿到接近原始质量的 JPEG 数据,避免二次压缩。

2. 环境准备与版本说明

2.1 安装 Python

如果你的电脑还没有安装 Python,请先安装 Python 3.8 及以上版本。更推荐 3.9 或 3.10、3.11,兼容性更好。

安装完成后,可以在命令行 / 终端中验证:

python --version

如果显示类似Python 3.11.2,说明环境正常。

2.2 安装关键依赖库

本文的核心依赖:

pip install PyMuPDF

安装完成后,你可能会看到库名标记为PyMuPDF,但在代码中导入时使用的是fitz

如果后续需要扩展功能,比如把图片转成 WebP、统一调整图片尺寸,建议同时安装 Pillow:

pip install Pillow

使用国内网络时,为了下载速度更快,可以指定镜像:

pip install PyMuPDF -i https://pypi.tuna.tsinghua.edu.cn/simple

但本文的示例是在常规 pip 环境下完成的,镜像配置不影响代码逻辑。

2.3 验证安装是否成功

在命令行输入python进入交互环境,执行:

python

然后输入:

import fitz print(fitz.__doc__)

如果能够输出 PyMuPDF 版本相关信息,而不是报ModuleNotFoundError,说明安装成功。

常见报错原因:

  • Python 版本过旧。
  • 使用虚拟环境时没有把库安装在当前项目中。
  • PyMuPDF 安装过程中网络中断,需要重新执行 pip install。

3. PyMuPDF 核心 API 拆解

3.1 打开 PDF 文档

使用fitz.open()

import fitz pdf_path = "sample.pdf" doc = fitz.open(pdf_path) print(f"页数:{doc.page_count}")

需要说明的是,fitz.open()默认以只读方式打开,返回一个Document对象。这个对象包含页面列表、元数据、书签等信息。

如果 PDF 有密码:

doc = fitz.open(pdf_path) if doc.needs_pass: doc.authenticate("your_password")

3.2 获取页面对象

通过doc.load_page(index)doc[index]获取第 index 页。

page = doc[0] # 第一页

page对象是理解整个提取流程的核心。后续获取图片都要通过它来调用get_images(full=True)方法。

3.3 页面图片枚举方法

images = page.get_images(full=True)

get_images()返回一个元组列表。每隔元组中的第一个元素是 PDF 文档级“xref”对象的编号。我们可以通过doc.extract_image(xref)把具体图片数据取出来。

3.4 原始图片数据提取

for img_index, img in enumerate(page.get_images(full=True)): xref = img[0] base_image = doc.extract_image(xref) image_bytes = base_image["image"] image_ext = base_image["ext"] image_width = base_image["width"] image_height = base_image["height"]

extract_image返回一个字典,常见键包括:

  • image:图片二进制数据。
  • ext:图片扩展名,例如jpegpngbmpjpx
  • width:图片宽度。
  • height:图片高度。
  • colorspace:颜色空间。
  • xref:对象编号。

3.5 页面渲染成图片

如果想另存页面为图片,可以使用:

pix = page.get_pixmap(dpi=300) pix.save("page_1.png")

dpi参数控制渲染清晰度,常用 150、300、600。该方法适用于扫描版 PDF,或者当 PDF 页面中的图片被矢量图嵌入、无法通过图片对象枚举时,作为兜底方案。

4. 实战:Python 批量提取 PDF 中的图片

为了让教程真正可用,下面提供一个完整工具脚本。这个脚本可以接收一个 PDF 文件,自动提取所有页面中出现的图片,并保存到指定目录。同时会过滤掉太小的“装饰性”图片,也可以按需求开启“扫描件整页另存”模式。

4.1 创建项目结构

建议创建以下目录结构:

pdf_image_extractor/ ├── pdfs/ # 存放待处理的 PDF ├── output_images/ # 提取出的图片保存目录 ├── extract_images.py # 主脚本

4.2 编写核心脚本

下面是完整的extract_images.py

# -*- coding: utf-8 -*- """ PDF 图片提取脚本 依赖: pip install PyMuPDF """ import os import fitz def extract_images_from_pdf(pdf_path, output_dir, min_width=20, min_height=20): """ 从 PDF 中提取所有页面的图片对象。 Args: pdf_path (str): PDF 文件路径 output_dir (str): 图片输出目录 min_width (int): 过滤掉小于该宽度的图片,避免输出大量小图标 min_height (int): 过滤掉小于该高度的图片 Returns: list: 保存成功的图片文件路径列表 """ if not os.path.exists(pdf_path): print(f"[错误] 文件不存在: {pdf_path}") return [] os.makedirs(output_dir, exist_ok=True) doc = fitz.open(pdf_path) saved_files = [] # 遍历每一页 for page_index in range(doc.page_count): page = doc.load_page(page_index) # 获取当前页面的图片引用 image_list = page.get_images(full=True) if not image_list: print(f"第 {page_index + 1} 页:未发现独立图片对象") continue print(f"第 {page_index + 1} 页:发现 {len(image_list)} 个图片对象") for img_index, img in enumerate(image_list): xref = img[0] try: base_image = doc.extract_image(xref) except Exception as e: print(f" 提取图片对象 XREF={xref} 失败: {e}") continue image_bytes = base_image["image"] image_ext = base_image["ext"] width = base_image["width"] height = base_image["height"] # 过滤小尺寸图片 if width < min_width or height < min_height: print(f" 跳过小图片: xref={xref}, {width}x{height}") continue # 生成不带后缀的图片文件名 file_name = f"page{page_index + 1}_img{img_index + 1}.{image_ext}" file_path = os.path.join(output_dir, file_name) with open(file_path, "wb") as f: f.write(image_bytes) print(f" 已保存: {file_path} ({width}x{height}, {len(image_bytes)} bytes)") saved_files.append(file_path) doc.close() return saved_files def extract_pdf_page_as_image(pdf_path, output_dir, page_indices=None, dpi=200): """ 把整个 PDF 页面渲染成一张图片。 适合扫描版 PDF、整页是图片的 PDF。 Args: pdf_path (str): PDF 文件路径 output_dir (str): 图片输出目录 page_indices (list[int]): 需要渲染的页码列表,从 0 开始;默认所有页 dpi (int): 渲染 DPI,越大越清晰,文件也越大 Returns: list: 保存成功的图片文件路径列表 """ if not os.path.exists(pdf_path): print(f"[错误] 文件不存在: {pdf_path}") return [] os.makedirs(output_dir, exist_ok=True) doc = fitz.open(pdf_path) saved_files = [] if page_indices is None: page_indices = list(range(doc.page_count)) for page_index in page_indices: if page_index >= doc.page_count: print(f"警告:页码 {page_index} 超出范围,跳过") continue page = doc.load_page(page_index) pix = page.get_pixmap(dpi=dpi) output_path = os.path.join(output_dir, f"page_{page_index + 1}.png") pix.save(output_path) saved_files.append(output_path) print(f"页面 {page_index + 1} 已渲染为 {output_path}") doc.close() return saved_files if __name__ == "__main__": # 待处理的 PDF pdf_file = "pdfs/sample.pdf" # 方式一:提取页面中的图片对象 print("=" * 40) print("开始提取 PDF 内嵌图片") print("=" * 40) save_dir = "output_images/embedded" extract_images_from_pdf(pdf_file, save_dir) # 方式二:如果 PDF 是扫描件,可以渲染整页 print("\n" + "=" * 40) print("开始将 PDF 页面渲染为图片") print("=" * 40) page_dir = "output_images/pages" extract_pdf_page_as_image(pdf_file, page_dir, dpi=200)

4.3 脚本逻辑说明

代码并不复杂,但有几点值得展开:

  1. get_images(full=True)full=True参数会返回每个图片对象的完整信息。如果写False,返回的元素会更少,其中仍包含 xref 信息,但部分场景下可能需要多次调用才能拿到全部数据。建议始终设为True

  2. doc.extract_image(xref)的调用位置是在文档对象上,而不是页面对象上。因为图片在 PDF 中是全局资源,xref 是它们在文档级对象表中的编号。同一张图片可能被多个页面引用,提取一次即可。

  3. 代码中的“过滤小图片”条件min_width=20min_height=20是经验值。PDF 中常有很多 1x1 像素的占位符图片,如果不做过滤,输出目录会混入大量无意义文件。

  4. 图片扩展名不一定是png。如果原 PDF 使用 JPEG 压缩,则image_extjpeg;如果使用 JBIG2,则可能是jb2。我们需要动态生成文件名。

4.4 运行脚本

在项目根目录执行:

python extract_images.py

假设pdfs/sample.pdf是一个 3 页文档,且每页有若干图片,运行效果大致如下:

============================================ 开始提取 PDF 内嵌图片 ============================================ 第 1 页:发现 3 个图片对象 已保存: output_images/embedded/page1_img1.jpeg (1200x800, 182340 bytes) 已保存: output_images/embedded/page1_img2.png (640x480, 51233 bytes) 已保存: output_images/embedded/page1_img3.jpeg (96x96, 2310 bytes) 第 2 页:发现 1 个图片对象 已保存: output_images/embedded/page2_img1.jpeg (800x600, 92012 bytes) 第 3 页:未发现独立图片对象

第 3 页没有图片对象,可能是文字页,也可能是内嵌矢量图,无法用图片对象方式提取;如果确认第 3 页有图形视觉内容,可以用extract_pdf_page_as_image渲染整页。

4.5 保存结果与质量讨论

提取出来的文件大小和原始图片非常接近。由于extract_image返回的是 PDF 内部存储的原始图像数据,我们只是把它以字节流形式重新写到磁盘,因此基本不存在“二次压缩损失”。

但需要注意两点:

  • 如果 PDF 中图片经过裁剪矩阵或旋转等显示处理,extract_image得到的是原始图片对象,不一定会应用裁剪效果。也就是说,提取出来的可能是 PDF 中嵌入的“完整图片”,而不是你在页面上看到的最小显示区域。
  • 如果图片是“蒙版叠加”形式,比如透明背景 PNG 使用了 SMask(软掩膜),用最简单的方式提取可能只得到原始图像,却丢失了透明度信息。遇到这种情况,PyMuPDF 的extract_image在部分版本中会返回包含 alpha 通道的图像,但为了保险,建议对输出图片做遮罩合并处理。不过,多数办公场景下,直接保存已经够用。

5. 完整案例:提取 PDF 中的流程图和产品截图

假设你有一个几十页的产品说明书 PDF,每一页都穿插了大量产品截图和架构流程图。你想快速把这些图整理成素材库。下面用一个具体的调用示例来说明如何“只提取符合要求的图片”。

5.1 需求假设

  • 图片最小宽度 200px,最小高度 200px。
  • 只处理第 2、3、5、10 页的图片。
  • 输出文件名包含原页码。
  • 忽略页面顶部 logo 等小图。

修改后的调用如下:

import fitz import os pdf_path = "产品说明书.pdf" output_dir = "extract_imgs" os.makedirs(output_dir, exist_ok=True) target_pages = [1, 2, 4, 9] # 页码索引从 0 开始,即第2、3、5、10页 doc = fitz.open(pdf_path) for page_idx in target_pages: if page_idx >= doc.page_count: continue page = doc.load_page(page_idx) images = page.get_images(full=True) for i, img in enumerate(images): xref = img[0] im = doc.extract_image(xref) ext = im["ext"] w = im["width"] h = im["height"] if w < 200 or h < 200: print(f"跳过小图: xref={xref}, 尺寸={w}x{h}") continue file_name = f"p{page_idx + 1}_{i + 1}.{ext}" file_path = os.path.join(output_dir, file_name) with open(file_path, "wb") as f: f.write(im["image"]) print(f"已保存: {file_path}") doc.close()

这段代码相当于上面脚本的一个场景化变体。当你只需要特定页面时,可以用循环页号代替全篇盲扫。

5.2 对输出图片质量不满意时怎么做

“质量不满意”通常有两种含义:

  1. 图片太小。因为原始 PDF 中嵌入的图片分辨率本身就不高,提取出来的原图当然无法超越原始设计。此时可以尝试使用get_pixmap()对区域做放大渲染,但效果提升有限,因为如果原图是模糊的,放大也不会有细节。
  2. 图片有浅色背景或格式需要统一。这时可以进一步使用 Pillow 做后处理,比如转换成 PNG、白底铺平、压缩体积、裁剪白边。

后处理示例:

from PIL import Image # 将提取出的 JPEG 重新保存为 PNG,并做简单白底合成 with Image.open("p1_1.jpeg") as img: rgba = img.convert("RGBA") background = Image.new("RGBA", rgba.size, (255, 255, 255, 255)) combined = Image.alpha_composite(background, rgba) combined.convert("RGB").save("p1_1_processed.png")

不过要看你的用途,如果只是想保存原图,直接保留原始格式即可,没必要做格式转换。

6. 常见问题与排查思路

6.1 ModuleNotFoundError: No module named 'fitz'

问题现象常见原因解决思路
导入 fitz 报错没有安装 PyMuPDF执行 pip install PyMuPDF
导入了 PyMuPDF 后仍然报错Python 环境混乱,虚拟环境和全局环境不一致检查当前解释器位置,在当前项目虚拟环境下重新安装依赖

6.2 代码运行后没有提取到任何图片

问题现象常见原因解决思路
page.get_images(full=True) 返回空列表PDF 页面中的“图片”其实是矢量绘图,没有独立图片对象使用 page.get_pixmap() 渲染页面
PDF 是扫描版,每页就是一张大图页面只有一个图片对象,而不是多个;若输出目录为空可能是 pymupdf 版本对图片对象获取异常直接将该页渲染成图片
图片被转换成内嵌对象流PDF 压缩型结构特殊尝试升级 PyMuPDF,或用 fitz.Tools() 辅助查看

6.3 提取出的图片顺序和 PDF 显示顺序不一样

get_images()返回的是页面资源对象列表,顺序并不严格等同于屏幕上图片的视觉位置。当页面中图片重叠、被打散时,按 xref 枚举的图片顺序可能与阅读顺序不一致,这是 PDF 正常现象。

如果需要按视觉位置排序,可以进一步获取图片在页面上的显示矩形区域。进阶代码如下:

for img_info in page.get_images(full=True): xref = img_info[0] rects = page.get_image_rects(xref) # rects 是图片在页面上的位置列表(可能多个位置引用同一对象) for r in rects: print("图片显示坐标:", r)

然后通过坐标排序,就可以按照从上到下、从左到右等方式保存提取文件。

6.4 提取图片报“cannot extract image with xref”错误

原因一般是图片在 PDF 中使用某些特殊编码,PyMuPDF 暂不支持直接抽取。另一种情况是 PDF 存在损坏或加密,虽然页面能打开,但对象读取失败。可尝试:

  1. 先执行doc.builder,使用doc.save("new.pdf")另存为新的 PDF,再做提取。
  2. 如果 PDF 有加密,确保已执行authenticate并且返回值为 1 或者非 0。

6.5 大 PDF 处理内存溢出或运行过慢

如果 PDF 页数非常多(超过几百页),且每页都有大量高清图片,一次性遍历所有图片可能导致内存占用高。可以把处理拆成按页流式方式,每次处理完一页即释放引用:

for page_index in range(doc.page_count): page = doc.load_page(page_index) images = page.get_images(full=True) for img in images: # 处理完以后,不要再持有 image_bytes pass

另外,图片数据会占用内存,保存文件后,可主动把局部变量重置为None,以便 GC 回收。

7. 进阶方案:区分 PDF 类型并自动选择提取策略

在实际业务中,我们往往无法保证所有 PDF 都是同一类型。如果是混合 PDF 库,可以使用下面一个判断思路:

  1. 统计整个 PDF 中每页平均图片对象数量。
  2. 如果页面数量较多但图片对象数量很少,同时页面尺寸较大,猜测量扫描 PDF。
  3. 如果图片对象数量正常(与页面内容一致),则优先走extract_image提取。

不过这个判断并不是绝对准确的,更严谨的做法是根据 PDF 页面是否包含文本层来判断。

判断一段 Page 是否包含文本:

page = doc.load_page(0) text = page.get_text("text") if text.strip(): print("该页包含文本,极可能是文本型 PDF") else: print("该页没有文本层,可能是扫描版 PDF")

需要注意,文本型 PDF 中的页面也可能只有图片标题没有正文,因此“没有文本”不一定是扫描件。更稳的方式是综合“文本量 + 图片数量”来判断。如果页面高度接近 A4,且页面是一张 2000x3000 的大图对象,基本可以确定为扫描版。

7.1 把提取逻辑封装成类

如果需要在多个项目中复用,可以把上面的逻辑封装成一个工具类:

import os import fitz from pathlib import Path class PDFImageExtractor: """基于 PyMuPDF 的 PDF 图片提取器""" def __init__(self, pdf_path): self.pdf_path = Path(pdf_path) self.doc = None def open(self, password=None): self.doc = fitz.open(str(self.pdf_path)) if password and self.doc.needs_pass: if not self.doc.authenticate(password): raise RuntimeError("PDF 密码认证失败") return self.doc def extract_embed_images(self, output_dir="output", min_width=20, min_height=20): output_path = Path(output_dir) output_path.mkdir(parents=True, exist_ok=True) self._ensure_open() saved_files = [] for page_idx in range(self.doc.page_count): page = self.doc.load_page(page_idx) image_list = page.get_images(full=True) for img_idx, img in enumerate(image_list): xref = img[0] try: base = self.doc.extract_image(xref) except Exception as e: print(f"Error extract xref {xref}: {e}") continue w, h = base["width"], base["height"] if w < min_width or h < min_height: continue ext = base["ext"] file_path = output_path / f"page{page_idx + 1}_{img_idx + 1}.{ext}" with open(file_path, "wb") as f: f.write(base["image"]) saved_files.append(str(file_path)) return saved_files def render_pages(self, output_dir="output_pages", dpi=150, page_indices=None): output_path = Path(output_dir) output_path.mkdir(parents=True, exist_ok=True) self._ensure_open() saved_files = [] if page_indices is None: page_indices = range(self.doc.page_count) for page_idx in page_indices: page = self.doc.load_page(page_idx) pix = page.get_pixmap(dpi=dpi) file_path = output_path / f"page_{page_idx + 1}.png" pix.save(file_path) saved_files.append(str(file_path)) return saved_files def _ensure_open(self): if self.doc is None: self.open() def close(self): if self.doc: self.doc.close() self.doc = None if __name__ == "__main__": extractor = PDFImageExtractor("pdfs/混合文档.pdf") extractor.open() embedded_files = extractor.extract_embed_images("output/embedded") page_files = extractor.render_pages("output/pages", dpi=150) print(f"提取内嵌图片: {len(embedded_files)} 张") print(f"渲染页面图片: {len(page_files)} 张") extractor.close()

这样的封装更接近工程化用法:实例化时只需传入 PDF 路径,打开后可以分别调用提取方法,最后关闭文档对象。

8. 最佳实践与工程建议

8.1 不要忽略 PDF 来源与权限问题

PDF 可能是他人拥有版权的文档。如果你只是用于个人学习或工作内容整理,完全没问题;但如果要把提取出的图片用于商业发布、公开传播,需要确认是否具有版权授权。在进行批量处理时,请只在拥有合法使用权、且允许提取内容的 PDF 上操作。涉及涉密文件时,选择离线脚本处理,不要上传到在线工具。

8.2 设定合理的过滤条件

提取图片前,先考虑清楚阈值。例如:

  • 过滤小于 50x50 的图标,避免目录混乱。
  • 过滤超大尺寸但颜色极少的纯色色块,避免存储浪费。
  • 可统一把图片转换为 RGB 或 PNG,便于后续入库。

但是“过滤”不能太激进,否则会漏掉重要截图。建议先默认只过滤1x12x2的占位图,等观察一轮输出后再调整条件。

8.3 处理加密 PDF 时的安全规范

代码支持密码认证,但生产环境中不应把明文密码硬编码在脚本里。建议:

  1. 使用环境变量获取密码。
  2. 只允许认证只读权限,不要用来尝试破解或绕过权限。
  3. 如果提示“密码错误”,不要暴力尝试。

8.4 路径与命名规范

推荐输出文件名包含:

  • 原 PDF 文件名(或标识)。
  • 页面序号。
  • 图片序号。
  • 原图扩展名。

避免只使用image1.png1.jpg这种难以追踪的名字。下面是一个更强的命名方案:

from pathlib import Path pdf_stem = Path(pdf_path).stem file_name = f"{pdf_stem}_p{page_idx + 1}_{img_idx + 1}.{ext}"

8.5 日志的重要性

如果要对几百个 PDF 批量操作,print()的输出不够用。使用logging记录每个文件的提取结果、失败原因、耗时:

import logging logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s" ) logger = logging.getLogger(__name__) logger.info("开始处理 %s", pdf_path)

这样出现问题后,可以根据日志回溯哪一步失败。

8.6 性能优化建议

  • 批量处理时,逐个打开 PDF,处理完立即close()
  • 若一个 PDF 中有大量重复图片(例如每一页都引用同一 logo),可以用集合保存已经保存过的 xref,跳过重复写入。
  • 如果对单页图片提取速度不满意,可以尝试减少extract_image调用次数。但通常瓶颈在磁盘写入,可以先统计总图片数量后才开始批量写。

跳过重复 xref 示例:

processed_xrefs = set() for page_idx in range(doc.page_count): page = doc.load_page(page_idx) for img in page.get_images(full=True): xref = img[0] if xref in processed_xrefs: continue processed_xrefs.add(xref) # 继续提取

8.7 让脚本支持文件夹批量处理

在实际工作里,可能一次需要提取多个 PDF。可以在主函数中遍历文件夹:

from pathlib import Path import fitz def batch_extract_from_folder(folder_path, output_root="extracted"): folder = Path(folder_path) for pdf_file in folder.glob("*.pdf"): print(f"正在处理: {pdf_file.name}") doc = fitz.open(pdf_file) out_subdir = Path(output_root) / pdf_file.stem out_subdir.mkdir(parents=True, exist_ok=True) for page_idx in range(doc.page_count): page = doc.load_page(page_idx) for img_idx, img in enumerate(page.get_images(full=True)): xref = img[0] base = doc.extract_image(xref) ext = base["ext"] file_path = out_subdir / f"p{page_idx + 1}_{img_idx + 1}.{ext}" with open(file_path, "wb") as f: f.write(base["image"]) doc.close() print("批量处理完成")

这个方法对文件夹下所有 PDF 进行同样处理,如果某些 PDF 是扫描版,仍然建议配合页面渲染模式使用。好一点的策略是先用判断逻辑识别扫描版,把扫描版单独丢到另一个目录执行get_pixmap()

9. 结语

掌握用 Python 提取 PDF 图片,本质上就是理解 PDF 文档对象模型和图片对象的关系。通过 PyMuPDF 的get_images()extract_image()方法,可以在毫秒级内抽取出页面内嵌图片,无需安装重型办公软件,也不需要逐页打开 PDF 手动另存。同时,针对扫描版 PDF 和无法提取图片对象的页面,学会用get_pixmap()渲染整页作为兜底方案,就能覆盖绝大多数文档图片提取需求。

我建议你找一个平时收藏的 PDF,亲手跑一遍上面的脚本,看看输出结果是否符合预期。可以先从单一 PDF 开始,观察提取的文件名和尺寸,再调整过滤条件,最后再扩展到批量文件夹。当你真的处理过一份包含几十张图片的复杂 PDF 后,你会意识到:“PDF 提取图片”这件事完全可以被脚本自动化,而且适合嵌入到文档管理系统、内容审核工具或自动化办公流程中。

如果本文对你有帮助,可以收藏备用。后面遇到多页扫描件时,建议重新阅读第 4 节和第 6 节的判断逻辑,用同样的思路处理即可。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询