使用 Python 中的 PDFMiner 从 PDF 中提取文本
用 Python 掌握 PDF 文本提取
PDFMiner.six 是一个强大的 Python 库,用于从 PDF 文档中提取文本、元数据和布局信息。
与简单的 PDF 读取器不同,它提供对 PDF 结构的深入分析,并能有效处理复杂的布局。

什么是 PDFMiner,为什么使用它?
PDFMiner 是一个纯 Python 库,专为从 PDF 文档中提取和分析文本而设计。.six 版本是目前积极维护的分支,支持 Python 3.x,而原始的 PDFMiner 项目已不再更新。
主要功能:
- 纯 Python 实现(无需外部依赖)
- 详细的布局分析和文本定位
- 字体和字符编码检测
- 支持加密的 PDF
- 包含命令行工具
- 可扩展架构,适用于自定义处理
当您需要对文本提取进行精确控制、需要保留布局信息或处理复杂的多栏文档时,PDFMiner 非常有用。尽管它可能比某些替代方案更慢,但其准确性和详细的分析能力使其成为文档处理流水线的首选。对于反向工作流程,您可能还对 使用 Python 生成 PDF 感兴趣。
安装和设置
使用 pip 安装 PDFMiner.six:
pip install pdfminer.six
对于虚拟环境(推荐):
python -m venv venv
source venv/bin/activate # 在 Windows 上:venv\Scripts\activate
pip install pdfminer.six
如果您是 Python 包管理的新手,请查看我们的 Python 快速参考,以了解 pip 和虚拟环境的更多细节。
验证安装:
pdf2txt.py --version
该库包含多个命令行工具:
pdf2txt.py- 从 PDF 中提取文本dumppdf.py- 转储 PDF 内部结构latin2ascii.py- 将拉丁字符转换为 ASCII
这些工具与 如 Poppler 的 PDF 操作工具 互补,这些工具提供额外的功能,如页面提取和格式转换。
基本文本提取
简单文本提取
从 PDF 中提取文本的最简单方法:
from pdfminer.high_level import extract_text
# 从 PDF 中提取所有文本
text = extract_text('document.pdf')
print(text)
这个高级 API 处理大多数常见用例,并将整个文档作为单个字符串返回。
从特定页面提取文本
要从特定页面提取文本:
from pdfminer.high_level import extract_text
# 从页面 2-5 提取文本(0 索引)
text = extract_text('document.pdf', page_numbers=[1, 2, 3, 4])
print(text)
这对于大型文档非常有用,当您只需要某些部分时,可以显著提高性能。
通过页面迭代提取文本
处理单个页面时:
from pdfminer.high_level import extract_pages
from pdfminer.layout import LTTextContainer
for page_layout in extract_pages('document.pdf'):
for element in page_layout:
if isinstance(element, LTTextContainer):
print(element.get_text())
这种方法为您提供了更多控制如何处理每一页,适用于页面结构各不相同的文档。
高级布局分析
了解 LAParams
LAParams(布局分析参数)控制 PDFMiner 如何解释文档布局。理解 PDFMiner 与简单库之间的区别在这里非常重要 - PDFMiner 实际上分析文本元素之间的空间关系。
from pdfminer.high_level import extract_text
from pdfminer.layout import LAParams
# 创建自定义 LAParams
laparams = LAParams(
line_overlap=0.5, # 文本行的最小重叠
char_margin=2.0, # 字符边距
line_margin=0.5, # 行边距
word_margin=0.1, # 单词间距
boxes_flow=0.5, # 文本框流阈值
detect_vertical=True, # 检测垂直文本
all_texts=False # 仅提取文本框中的文本
)
text = extract_text('document.pdf', laparams=laparams)
参数解释:
line_overlap:行必须垂直重叠多少才能被视为同一行(0.0-1.0)char_margin:同一单词中字符之间的最大间距(字符宽度的倍数)line_margin:同一段落中行之间的最大间距word_margin:分隔单词的间距阈值boxes_flow:文本框流方向的阈值detect_vertical:启用垂直文本检测(常见于亚洲语言)
提取布局信息
获取详细的定位和字体信息:
from pdfminer.high_level import extract_pages
from pdfminer.layout import LTTextBox, LTTextLine, LTChar
for page_layout in extract_pages('document.pdf'):
for element in page_layout:
if isinstance(element, LTTextBox):
# 获取边界框坐标
x0, y0, x1, y1 = element.bbox
print(f"文本位于 ({x0}, {y0}):{element.get_text()}")
# 遍历行
for text_line in element:
if isinstance(text_line, LTTextLine):
# 获取字符级别的细节
for char in text_line:
if isinstance(char, LTChar):
print(f"字符:{char.get_text()}, "
f"字体:{char.fontname}, "
f"大小:{char.height}")
这种级别的详细信息对于文档分析、表单提取或需要了解文档结构的编程处理非常有价值。
处理不同的 PDF 类型
加密的 PDF
PDFMiner 可以处理受密码保护的 PDF:
from pdfminer.high_level import extract_text
# 从受密码保护的 PDF 提取
text = extract_text('encrypted.pdf', password='your_password')
请注意,PDFMiner 只能从 PDF 中提取文本,不能绕过防止文本提取的 PDF 级别安全限制。
多栏文档
对于具有多栏的文档,调整 LAParams:
from pdfminer.high_level import extract_text
from pdfminer.layout import LAParams
# 优化多栏布局
laparams = LAParams(
detect_vertical=False,
line_margin=0.3,
word_margin=0.1,
boxes_flow=0.3 # 更低值有助于更好地检测列
)
text = extract_text('multi_column.pdf', laparams=laparams)
boxes_flow 参数对于多栏文档尤为重要 - 更低的值有助于 PDFMiner 区分不同的列。
非英语和 Unicode 文本
PDFMiner 处理 Unicode 很好,但请确保使用正确的编码:
from pdfminer.high_level import extract_text
# 使用 Unicode 支持提取文本
text = extract_text('multilingual.pdf', codec='utf-8')
# 以 UTF-8 编码保存到文件
with open('output.txt', 'w', encoding='utf-8') as f:
f.write(text)
处理扫描的 PDF
PDFMiner 无法直接从扫描的 PDF(图像)中提取文本。这些需要 OCR(光学字符识别)。不过,您可以将 PDFMiner 与 OCR 工具集成。
以下是检测 PDF 是否为扫描并需要 OCR 的方法:
from pdfminer.high_level import extract_text
from pdfminer.high_level import extract_pages
from pdfminer.layout import LTFigure, LTImage
def is_scanned_pdf(pdf_path):
"""检查 PDF 是否为扫描(主要是图像)"""
text_count = 0
image_count = 0
for page_layout in extract_pages(pdf_path):
for element in page_layout:
if isinstance(element, (LTFigure, LTImage)):
image_count += 1
elif hasattr(element, 'get_text'):
if element.get_text().strip():
text_count += 1
# 如果主要是图像且文本很少,可能是扫描的
return image_count > text_count * 2
if is_scanned_pdf('document.pdf'):
print("此 PDF 看起来是扫描的 - 使用 OCR")
else:
text = extract_text('document.pdf')
print(text)
对于扫描的 PDF,可以考虑与 Tesseract OCR 集成,或者先使用 工具从 PDF 中提取图像,然后对这些图像进行 OCR。
命令行使用
PDFMiner 包含强大的命令行工具:
使用命令行工具提取文本
# 提取文本到标准输出
pdf2txt.py document.pdf
# 保存到文件
pdf2txt.py -o output.txt document.pdf
# 提取特定页面
pdf2txt.py -p 1,2,3 document.pdf
# 以 HTML 格式提取
pdf2txt.py -t html -o output.html document.pdf
高级选项
# 自定义布局参数
pdf2txt.py -L 0.3 -W 0.1 document.pdf
# 提取详细布局(XML)
pdf2txt.py -t xml -o layout.xml document.pdf
# 设置加密 PDF 的密码
pdf2txt.py -P mypassword encrypted.pdf
这些命令行工具非常适合快速测试、shell 脚本和集成到自动化工作流程中。
性能优化
处理大型 PDF
对于大型文档,考虑以下优化策略:
from pdfminer.high_level import extract_pages
from pdfminer.layout import LAParams
# 仅处理所需页面
def extract_page_range(pdf_path, start_page, end_page):
text_content = []
for i, page_layout in enumerate(extract_pages(pdf_path)):
if i < start_page:
continue
if i >= end_page:
break
text_content.append(page_layout)
return text_content
# 禁用布局分析以提高速度
from pdfminer.high_level import extract_text
text = extract_text('large.pdf', laparams=None) # 快得多
批量处理
为了高效处理多个 PDF:
from multiprocessing import Pool
from pdfminer.high_level import extract_text
import os
def process_pdf(pdf_path):
"""处理单个 PDF 文件"""
try:
text = extract_text(pdf_path)
output_path = pdf_path.replace('.pdf', '.txt')
with open(output_path, 'w', encoding='utf-8') as f:
f.write(text)
return f"已处理: {pdf_path}"
except Exception as e:
return f"处理 {pdf_path} 时出错: {str(e)}"
# 并行处理 PDF
def batch_process_pdfs(pdf_directory, num_workers=4):
pdf_files = [os.path.join(pdf_directory, f)
for f in os.listdir(pdf_directory)
if f.endswith('.pdf')]
with Pool(num_workers) as pool:
results = pool.map(process_pdf, pdf_files)
for result in results:
print(result)
# 使用方法
batch_process_pdfs('/path/to/pdfs', num_workers=4)
常见问题和解决方案
问题:文本顺序错误
问题: 提取的文本混乱或顺序错误。
解决方案: 调整 LAParams,特别是 boxes_flow:
from pdfminer.layout import LAParams
laparams = LAParams(boxes_flow=0.3) # 尝试不同值
text = extract_text('document.pdf', laparams=laparams)
问题:单词之间缺少空格
问题: 单词连在一起,没有空格。
解决方案: 增加 word_margin:
laparams = LAParams(word_margin=0.2) # 从默认值 0.1 增加
text = extract_text('document.pdf', laparams=laparams)
问题:编码错误
问题: 出现奇怪字符或编码错误。
解决方案: 显式指定编码:
text = extract_text('document.pdf', codec='utf-8')
问题:处理大型 PDF 时出现内存错误
问题: 处理大型文件时出现内存错误。
解决方案: 按页面处理:
def extract_text_chunked(pdf_path, chunk_size=10):
"""分块提取文本以减少内存使用"""
all_text = []
page_count = 0
for page_layout in extract_pages(pdf_path):
page_text = []
for element in page_layout:
if hasattr(element, 'get_text'):
page_text.append(element.get_text())
all_text.append(''.join(page_text))
page_count += 1
# 分块处理
if page_count % chunk_size == 0:
yield ''.join(all_text)
all_text = []
# 输出剩余文本
if all_text:
yield ''.join(all_text)
与替代方案的比较
了解何时使用 PDFMiner 而不是其他库非常重要:
PDFMiner 与 PyPDF2
PyPDF2 更简单、更快,但准确性较低:
- 使用 PyPDF2 的情况:简单 PDF、快速提取、合并/拆分 PDF
- 使用 PDFMiner 的情况:复杂布局、精确文本定位、详细分析
PDFMiner 与 pdfplumber
pdfplumber 在 PDFMiner 的基础上构建,提供了更高级的 API:
- 使用 pdfplumber 的情况:表格提取、更简单的 API、快速原型设计
- 使用 PDFMiner 的情况:最大控制、自定义处理、生产系统
PDFMiner 与 PyMuPDF(fitz)
PyMuPDF 显著更快,但有 C 依赖项:
- 使用 PyMuPDF 的情况:对性能要求高的应用、大规模处理
- 使用 PDFMiner 的情况:纯 Python 要求、详细布局分析
实用示例:提取并分析文档
以下是一个完整示例,提取文本并提供文档统计信息:
from pdfminer.high_level import extract_pages, extract_text
from pdfminer.layout import LTTextBox, LTChar
from collections import Counter
import re
def analyze_pdf(pdf_path):
"""提取文本并提供文档分析"""
# 提取全文
full_text = extract_text(pdf_path)
# 统计信息
stats = {
'total_chars': len(full_text),
'total_words': len(full_text.split()),
'total_lines': full_text.count('\n'),
'fonts': Counter(),
'font_sizes': Counter(),
'pages': 0
}
# 详细分析
for page_layout in extract_pages(pdf_path):
stats['pages'] += 1
for element in page_layout:
if isinstance(element, LTTextBox):
for line in element:
for char in line:
if isinstance(char, LTChar):
stats['fonts'][char.fontname] += 1
stats['font_sizes'][round(char.height, 1)] += 1
return {
'text': full_text,
'stats': stats,
'most_common_font': stats['fonts'].most_common(1)[0] if stats['fonts'] else None,
'most_common_size': stats['font_sizes'].most_common(1)[0] if stats['font_sizes'] else None
}
# 使用方法
result = analyze_pdf('document.pdf')
print(f"页数: {result['stats']['pages']}")
print(f"单词数: {result['stats']['total_words']}")
print(f"主要字体: {result['most_common_font']}")
print(f"主要字号: {result['most_common_size']}")
与文档处理流水线的集成
PDFMiner 在较大的文档处理工作流程中表现良好。例如,在构建 RAG(检索增强生成)系统或文档管理解决方案时,可以将其与其他 Python 工具结合,形成完整的流水线。
一旦从 PDF 中提取了文本,通常需要将其转换为其他格式。您可以 使用 Python 库将 HTML 内容转换为 Markdown 或甚至利用 使用 Ollama 的 LLM 转换 进行智能文档转换。这些技术在 PDF 提取产生类似 HTML 的结构化文本并需要清理和重新格式化时特别有用。
对于全面的文档转换流水线,您可能还需要处理 Word 文档到 Markdown 的转换,创建一个统一的工作流程,处理多种文档格式并转换为通用输出格式。
最佳实践
-
复杂文档始终使用 LAParams - 默认设置适用于简单文档,但调整 LAParams 会显著改善复杂布局的结果。
-
先用示例页面进行测试 - 在处理大批量文件之前,先在代表性样本上测试您的提取设置。
-
优雅地处理异常 - PDF 文件可能损坏或格式错误。始终将提取代码包裹在 try-except 块中。
-
缓存提取的文本 - 对于重复处理,缓存提取的文本以避免重新处理。
-
验证提取的文本 - 实施检查以验证提取质量(例如,最小文本长度、预期关键词)。
-
针对特定用例考虑替代方案 - 虽然 PDFMiner 功能强大,但有时专门的工具(如用于表格的 tabula-py)更合适。
-
保持 PDFMiner 更新 -
.six分支正在积极维护。保持更新以获得错误修复和改进。 -
为代码编写良好的文档 - 当分享 PDF 提取脚本时,使用适当的 Markdown 代码块 与语法高亮,以提高可读性。
结论
PDFMiner.six 是 Python 开发人员处理 PDF 文档时必不可少的工具。其纯 Python 实现、详细的布局分析和可扩展架构使其非常适合生产文档处理系统。尽管它可能比简单库的学习曲线更陡峭,但它在复杂 PDF 提取任务中提供的精确性和控制力是无与伦比的。
无论您是在构建文档管理系统、分析科学论文还是从机器学习流水线中提取数据,PDFMiner 都为 Python 提供了可靠的 PDF 文本提取基础。
相关资源
本网站的相关文章
- Ubuntu 上的 PDF 操作工具 - Poppler - 有关命令行 PDF 工具(包括 pdftotext、pdfimages 和其他 poppler 工具)的全面指南,这些工具与 PDFMiner 在文档处理工作流程中协同工作
- 如何从 PDF 中提取图像 - 快速参考 - 学习如何使用 poppler 命令行工具从 PDF 中提取嵌入的图像,补充 PDFMiner 的文本提取功能
- Python 中生成 PDF 的库和示例 - 探索用于 PDF 生成的 Python 库,包括 ReportLab、PyPDF2 和 FPDF,以创建 PDF 文本提取的反向工作流程
- Python 快速参考 - 有关文件处理、字符串操作和编写干净 PDF 处理脚本的最佳实践的 Python 语法参考
- 使用 Python 将 HTML 转换为 Markdown 的全面指南 - 在构建文档转换流水线时,学习如何使用 Python 库将 HTML(从 PDF 或网页中提取)转换为 Markdown 格式
- 使用 LLM 和 Ollama 将 HTML 内容转换为 Markdown - 使用本地 LLM 智能地将 HTML 内容转换为 Markdown 的高级技术,用于清理提取的 PDF 文本
- 使用 Markdown 代码块 - 掌握 Markdown 语法,以正确格式化和突出显示您的 PDF 提取代码
- 将 Word 文档转换为 Markdown:完整指南 - 包括 Word、PDF 和其他格式的完整文档转换指南,用于跨平台文档处理流水线