第一次接触PyMuPDF时,我正在处理一份200多页的产品手册。需要提取关键章节、保存所有图表,还要做个简易查看器给市场部用。试过好几个库之后,PyMuPDF的表现让我眼前一亮——它不仅处理速度快,还能精准定位到PDF里的每个元素。
这个基于MuPDF引擎的Python库,最厉害的地方在于直接操作PDF底层结构。不像某些库先把PDF转成中间格式,PyMuPDF能直接读取文字块坐标、提取矢量图形,甚至修改注释属性。我做过测试:用PyMuPDF提取100页PDF中的表格,速度比pdfminer快3倍,内存占用只有PyPDF2的一半。
安装只需要一行命令:
bash复制pip install pymupdf
但要注意它的导入名是fitz(历史原因):
python复制import fitz # 这就是PyMuPDF
典型应用场景包括:
最近帮客户做的投标文件解析系统,就用PyMuPDF实现了自动提取技术参数表格。相比人工核对,效率提升了20倍不止。
拿到PDF第一件事就是看它的"身份证信息"。PyMuPDF的metadata属性会返回一个字典,包含作者、标题等关键信息。有次审计项目就用这个功能发现了文档的真实创建时间(虽然文件名显示是2023年,但元数据暴露了实际是2018年修改的)。
python复制doc = fitz.open("report.pdf")
print(doc.metadata)
# 输出示例:
# {'title': '年度报告', 'author': '张伟', 'format': 'PDF 1.7'}
更实用的get_toc()方法能提取目录结构,返回值是嵌套列表。我常用这个功能实现文档自动导航:
python复制toc = doc.get_toc()
for level, title, page in toc:
print(f"第{page}页 {' '*(level-1)}└ {title}")
页面级操作才是重头戏。PyMuPDF的Page对象提供了多种内容提取方式,最常用的是get_text()。参数不同,提取效果天差地别:
python复制page = doc.load_page(0) # 第一页
# 纯文本模式(带换行)
print(page.get_text("text"))
# 保持段落结构
blocks = page.get_text("blocks")
for block in blocks:
print(block[4]) # 第5个元素是文本内容
# 精确到单词级
words = page.get_text("words")
for word in words:
print(f"{word[4]} (位置:{word[0:4]})")
处理扫描件时,可以先用get_pixmap()生成图像,再用OCR识别。我常用的参数组合:
python复制pix = page.get_pixmap(
matrix=fitz.Matrix(2, 2), # 200%缩放提高清晰度
colorspace="rgb",
alpha=False
)
pix.save("page.png")
search_for()方法比想象中强大。上次需要高亮显示所有"保密条款",用这个功能配合矩形绘制,10行代码就实现了:
python复制search_results = page.search_for("保密条款")
for rect in search_results:
highlight = page.add_highlight_annot(rect)
highlight.update()
更复杂的场景可以用正则表达式先提取文本位置,再反查坐标。比如提取所有金额数字:
python复制import re
text = page.get_text("dict")
for block in text["blocks"]:
for line in block["lines"]:
for span in line["spans"]:
amounts = re.findall(r"\d+\.\d{2}", span["text"])
if amounts:
print(f"找到金额:{amounts} 位置:{span['bbox']}")
PyMuPDF允许直接修改PDF内容,就像做外科手术。常见操作包括:
页面重组:
python复制# 删除第3页
doc.delete_page(2)
# 复制第1页到最后
doc.copy_page(0)
# 移动第5页到第2页位置
doc.move_page(4, 1)
内容插入:
python复制new_page = doc.new_page(width=595, height=842) # A4尺寸
new_page.insert_text(
point=fitz.Point(50, 50),
text="机密文件",
fontsize=24,
fontname="helv",
color=(1, 0, 0) # RGB红色
)
水印添加:
python复制for page in doc:
page.insert_image(
rect=page.bound(), # 全页面
filename="watermark.png",
overlay=True # 覆盖模式
)
将PyMuPDF与PyQt5结合,可以做出专业级的PDF工具。核心思路是:
基础查看器代码框架:
python复制from PyQt5.QtWidgets import QApplication, QLabel
from PyQt5.QtGui import QPixmap, QImage
import fitz
import sys
class PDFViewer(QLabel):
def __init__(self):
super().__init__()
self.setMinimumSize(800, 600)
def load_pdf(self, path):
doc = fitz.open(path)
page = doc.load_page(0)
pix = page.get_pixmap(dpi=150)
# 转换为Qt能显示的格式
img = QImage(
pix.samples,
pix.width,
pix.height,
pix.stride,
QImage.Format_RGB888
)
self.setPixmap(QPixmap.fromImage(img))
app = QApplication(sys.argv)
viewer = PDFViewer()
viewer.load_pdf("demo.pdf")
viewer.show()
sys.exit(app.exec_())
缩略图导航:
python复制from PyQt5.QtWidgets import QListWidget, QListWidgetItem
class ThumbnailList(QListWidget):
def __init__(self):
super().__init__()
self.setViewMode(1) # 图标模式
self.setIconSize(QSize(100, 150))
def add_thumbnails(self, doc):
for i in range(doc.page_count):
page = doc.load_page(i)
pix = page.get_pixmap(matrix=fitz.Matrix(0.2, 0.2))
img = QImage(pix.samples, pix.width, pix.height,
pix.stride, QImage.Format_RGB888)
item = QListWidgetItem(QPixmap.fromImage(img))
item.setText(f"第{i+1}页")
self.addItem(item)
文本选择功能:
python复制def mousePressEvent(self, event):
pos = event.pos()
text = self.page.get_text("words")
for word in text:
if (word[0] <= pos.x() <= word[2] and
word[1] <= pos.y() <= word[3]):
print(f"选中文本:{word[4]}")
实际项目中,我还添加了这些实用功能:
处理大文件时,这些方法能显著提升性能:
python复制doc = fitz.open("large.pdf")
# 不立即加载所有页面
python复制clip = fitz.Rect(0, 0, 400, 600) # 可视区域
pix = page.get_pixmap(clip=clip)
python复制from functools import lru_cache
@lru_cache(maxsize=10)
def get_page_image(page_num):
return doc.load_page(page_num).get_pixmap()
中文乱码问题:
确保系统中有中文字体,指定正确字体名称:
python复制page.insert_text(
point,
"中文内容",
fontname="notosans-sc" # 思源黑体
)
内容重叠:
修改内容前先检查原有元素:
python复制existing = page.get_text("blocks")
for block in existing:
if block[4].find("保留区域") >= 0:
continue # 跳过特定区域
内存泄漏:
及时关闭不再使用的文档:
python复制with fitz.open("temp.pdf") as doc:
# 操作文档
# 自动关闭
最近在金融项目中发现一个典型用例:批量处理500份PDF财报,提取关键指标生成可视化报告。用PyMuPDF+PyQt5实现的方案,比商业软件快40%,准确率还更高。特别是在处理表格数据时,PyMuPDF的文本坐标定位能力发挥了关键作用。