python
import fitz # PyMuPDF
import re
from typing import List, Dict, Any
import json
def process_handout_pdf(pdf_path: str, chapter_num: int) -> List[Dict[str, Any]]:
"""
处理单个handout PDF,提取文字、图像、图表等内容并给它们打标签。
此函数只负责数据提取和标签生成,不生成幻灯片。
"""
data = [] # 存储提取的所有数据和标签
doc = None
try:
doc = fitz.open(pdf_path)
except Exception as e:
print(f"无法打开PDF: {e}")
return []
total_pages = len(doc)
for page_num in range(total_pages):
page = doc.load_page(page_num) # 加载页
# 1. 提取文字
text_data = extract_text(page)
if text_data:
data.append({
"type": "text",
"content": text_data,
"source": {
"pdf_name": pdf_path.split('/')[-1], # 只取文件名
"page": page_num + 1 # 页面编号从1开始
},
"content_labels": get_content_labels(text_data)
})
# 2. 提取图像
image_data = extract_images(page)
for img in image_data:
data.append({
"type": "image",
"content": img,
"source": {
"pdf_name": pdf_path.split('/')[-1],
"page": page_num + 1
},
"content_labels": get_content_labels(img)
})
# 3. 提取图表(如果需要,可以根据图表的特征进行提取,这里假设它是图片)
chart_data = extract_charts(page) # 如果有特定方法提取图表,加入此函数
for chart in chart_data:
data.append({
"type": "chart",
"content": chart,
"source": {
"pdf_name": pdf_path.split('/')[-1],
"page": page_num + 1
},
"content_labels": get_content_labels(chart)
})
# 4. 提取其他元素(例如,表格、公式等)
other_data = extract_other_elements(page)
for element in other_data:
data.append({
"type": "other",
"content": element,
"source": {
"pdf_name": pdf_path.split('/')[-1],
"page": page_num + 1
},
"content_labels": get_content_labels(element)
})
# 关闭PDF
doc.close()
return data
# ==========================
# 提取具体元素的辅助函数
# ==========================
def extract_text(page: fitz.Page) -> str:
"""
提取页面上的纯文字内容
"""
return page.get_text("text")
def extract_images(page: fitz.Page) -> List[str]:
"""
提取页面上的所有图像,返回图片的URL或数据(在这里模拟为简单描述)
"""
image_data = []
images = page.get_images(full=True)
for img_index, img in enumerate(images):
# 简单示例:返回每个图片的描述
image_data.append(f"Image {img_index + 1}")
return image_data
def extract_charts(page: fitz.Page) -> List[str]:
"""
提取页面上的图表。此处假设图表以图像形式存储。
若有其他方式来提取图表,可以在此函数扩展。
"""
# 这里模拟图表提取,实际可能需要更复杂的图像分析方法
return extract_images(page)
def extract_other_elements(page: fitz.Page) -> List[str]:
"""
提取页面上的其他元素,例如表格、公式等。这里假设为文本或图像内容。
"""
return extract_text(page).split("\n") # 只是一个简单的拆分作为示例
# ==========================
# 内容标签生成辅助函数
# ==========================
def get_content_labels(content: str) -> List[str]:
"""
利用LLM或规则,为内容打上3-5个标签(知识点、专有名词等)。
这里只是模拟示例,实际可以调用LLM来进行识别。
"""
# 模拟通过 LLM 生成标签:此部分可以通过真正的 LLM 接口来生成
labels = []
# 规则判断:例如识别学科、人物、专有名词等(模拟)
if re.search(r'\bAI\b|\b机器学习\b', content): # 识别AI或机器学习
labels.append("机器学习")
if re.search(r'\b数据科学\b', content): # 识别数据科学
labels.append("数据科学")
if re.search(r'\b图像处理\b', content): # 识别图像处理
labels.append("图像处理")
# 添加人物/名词(例如,课程中的人物、模型等)
if re.search(r'\b(图灵\b|Alan Turing\b)', content): # 识别图灵
labels.append("Alan Turing")
# 模拟一个标签列表返回(根据内容生成标签,具体可根据需求优化)
if not labels:
labels = ["通用内容"] # 没有识别到特定标签时,加个默认标签
# 返回最多5个标签
return labels[:5]
# ==========================
# 数据结构存储
# ==========================
def get_structured_data(data: List[Dict[str, Any]]) -> Dict[str, Any]:
"""
将提取的数据整理成合适的结构,以便后续操作(例如:根据标签对内容进行整合)
"""
structured_data = {
"texts": [],
"images": [],
"charts": [],
"others": []
}
# 根据类型将数据分配到不同类别
for item in data:
if item["type"] == "text":
structured_data["texts"].append(item)
elif item["type"] == "image":
structured_data["images"].append(item)
elif item["type"] == "chart":
structured_data["charts"].append(item)
else:
structured_data["others"].append(item)
return structured_data
Dialogue 1
代码解释
提取元素:
process_handout_pdf 会提取 PDF 中的文字、图像、图表以及其他元素。每种提取内容都通过不同的辅助函数处理,如 extract_text 提取文字,extract_images 提取图像,extract_charts 提取图表(在此用图像模拟),extract_other_elements 提取其他内容(如表格、公式等)。
标签生成:
为每个提取的内容(无论是文字、图像还是其他元素)生成两个类型的标签:
来源标签:包括来源的 PDF 文件名和页码。
内容标签:使用简单的正则表达式模拟通过 LLM 提取知识点、人物等标签,实际应用中你可以接入 LLM API 来实现更复杂的标签生成。
每个提取的内容会关联多个标签(最多 5 个),并存储在 content_labels 字段中。
数据结构:
最终生成的数据将被组织成一个字典(structured_data),该字典将不同类型的提取内容(文字、图像、图表、其他元素)分类存储,便于后续处理和分析。
存储和整合:
get_structured_data 函数将数据根据类型整理,并返回最终结构。这将为后续的分析、展示或聚类等任务提供数据支持。
下一步
LLM 接入:你可以根据需要替换 get_content_labels 函数中的标签生成部分,调用实际的 LLM API 来识别更准确的内容标签。
图像和图表的进一步分析:如果图表有结构化数据(如图表表格数据),可以使用 OCR 或图表解析方法提取这些信息,而不仅仅是图片描述。
这样,你就完成了数据提取、标签生成和结构化存储的处理部分。
Dialogue 2
确实,之前的 extract_charts 和 extract_other_elements 函数中有些内容只是伪代码或者假设代码。图表、公式、表格等内容的提取是一个复杂的问题,因为它们通常以不同的方式呈现,尤其是图表可能是图片形式,表格通常是结构化文本或表格对象。
下面是针对 提取图表 和 提取表格 的具体代码实现示例:
1. 提取图表
对于图表,很多情况下,它们是作为图片嵌入在PDF中,因此我们可以通过图像提取来获取图表。下面的代码假设图表以图像形式存在,并使用 fitz(PyMuPDF)提取图像。
python
def extract_charts(page: fitz.Page) -> List[str]:
"""
提取页面上的图表,假设图表是图像格式。
"""
charts = []
images = page.get_images(full=True) # 获取页面上的所有图像
for img_index, img in enumerate(images):
xref = img[0] # 图片的xref
base_image = fitz.Pixmap(page.doc, xref) # 使用xref提取图片
# 可以在这里进行进一步的图像处理,例如将其转换为PNG、JPG等格式并保存
# 或者你可以选择对图像进行OCR分析,提取图表的结构化数据
# 将图像作为图表添加到结果列表中
charts.append(f"Chart {img_index + 1} (image {xref})")
return charts
2. 提取表格
表格一般是由多行和多列组成的结构化数据,但在 PDF 中,表格通常是通过 Text 内容或者嵌入的表格对象来表示的。通过 fitz 可以提取文本,并通过一定的规则来判断哪些内容属于表格。
这里是一个基本的 提取表格数据 的示例,我们将所有文本提取出来,然后尝试识别以表格形式呈现的内容:
python
def extract_table(page: fitz.Page) -> List[Dict[str, Any]]:
"""
提取页面上的表格数据。假设表格是以文本形式呈现,按照行列对齐。
使用fitz的get_text方法提取文本。
"""
table_data = []
text = page.get_text("text") # 提取页面的纯文本
lines = text.split("\n") # 按行分割文本
# 设定一个简单规则来判断表格的列数和行数(假设表格有固定的列宽)
# 这里假设表格是通过空格或制表符分隔的
for line in lines:
columns = line.split() # 简单以空格分割列
if len(columns) > 1: # 如果列数多于1,认为是表格行
table_data.append({"columns": columns})
return table_data
3. 提取公式
公式提取比较复杂,尤其是PDF中的公式有时是嵌入的图片,有时是文本形式。下面是一个简化的例子:
python
def extract_formulae(page: fitz.Page) -> List[str]:
"""
提取页面上的公式。假设公式是以图片形式存在,使用get_images()方法提取。
"""
formulas = []
images = page.get_images(full=True)
for img_index, img in enumerate(images):
xref = img[0]
base_image = fitz.Pixmap(page.doc, xref)
# 假设公式是通过图片存储的,可以选择OCR处理这些图片
# 或者直接将其标记为公式
formulas.append(f"Formula {img_index + 1} (image {xref})")
return formulas
4. 综合方案
你可以将图表、表格、公式的提取代码整合到一个 extract_other_elements 方法中,根据实际情况返回这些提取结果。比如,提取图表、表格和公式,并将它们存储在一个统一的数据结构中:
python
def extract_other_elements(page: fitz.Page) -> List[Dict[str, Any]]:
"""
提取页面上的其他元素,如图表、公式和表格。
"""
other_elements = []
# 提取图表
charts = extract_charts(page)
for chart in charts:
other_elements.append({
"type": "chart",
"content": chart
})
# 提取表格
tables = extract_table(page)
for table in tables:
other_elements.append({
"type": "table",
"content": table
})
# 提取公式
formulae = extract_formulae(page)
for formula in formulae:
other_elements.append({
"type": "formula",
"content": formula
})
return other_elements
总结
图表提取:假设图表是以图片形式嵌入 PDF 中,使用 get_images 提取图片,并将其作为图表处理。
表格提取:提取文本并根据简单的规则分割表格数据。如果有其他复杂的表格格式,可以考虑使用 fitz 提供的 get_text("dict") 方法来提取表格数据。
公式提取:假设公式是图片(这在学术论文中较为常见),同样通过提取图像来获取公式。
这种方法对 图像、文本表格 和 公式 进行了较为简单的处理,但实际应用中,你可能需要更多的优化。例如,可以结合 OCR 技术(如 Tesseract)来对图像中的文本进行识别,以提取更丰富的图表和公式信息。
如果你需要进一步细化图像或表格的处理,或者希望将公式从文本中提取出来,你可以考虑结合图像识别和数学公式解析库。
Dialogue 3
python
import os
import fitz # PyMuPDF
from typing import List, Dict, Any
import re
# 设置存储图像的目录
IMAGE_DIR = "extracted_images"
def extract_text(page: fitz.Page) -> str:
"""
提取页面上的纯文字内容
"""
return page.get_text("text") # 获取纯文本
def extract_images(page: fitz.Page) -> List[str]:
"""
提取页面上的所有图像,返回图片的描述。
"""
image_data = []
images = page.get_images(full=True)
for img_index, img in enumerate(images):
xref = img[0] # 图像的xref
base_image = fitz.Pixmap(page.doc, xref) # 使用xref提取图片
# 检查图像是否为灰度图,如果是,转换为RGB
if base_image.n < 4: # 如果是灰度图或调色板图像
base_image = fitz.Pixmap(fitz.csRGB, base_image) # 转换为RGB模式
# 确保图像目录存在
if not os.path.exists(IMAGE_DIR):
os.makedirs(IMAGE_DIR)
# 定义图像保存路径
image_path = os.path.join(IMAGE_DIR, f"image_{xref}.png")
# 将图像保存为PNG格式
base_image.save(image_path) # 保存为PNG文件
# 将图像路径作为图像记录添加到列表
image_data.append({
"type": "image",
"content": {
"image_path": image_path, # 图像文件路径
"xref": xref # 图像的xref
}
})
# 清理内存中的图像对象
base_image = None
return image_data
def extract_charts(page: fitz.Page, chapter_num: int) -> List[Dict[str, Any]]:
"""
提取页面上的图表,假设图表是图像格式。
将图像转换为PNG并保存到文件系统中,同时返回图像路径。
"""
charts = []
images = page.get_images(full=True) # 获取页面上的所有图像
for img_index, img in enumerate(images):
xref = img[0] # 图像的xref
base_image = fitz.Pixmap(page.doc, xref) # 使用xref提取图片
# 检查图像是否为灰度图,如果是,转换为RGB
if base_image.n < 4: # 如果是灰度图或调色板图像
base_image = fitz.Pixmap(fitz.csRGB, base_image) # 转换为RGB模式
# 确保图像目录存在
if not os.path.exists(IMAGE_DIR):
os.makedirs(IMAGE_DIR)
# 定义图像保存路径
image_path = os.path.join(IMAGE_DIR, f"chart_{chapter_num}_{img_index + 1}.png")
# 将图像保存为PNG格式
base_image.save(image_path) # 保存为PNG文件
# 将图像路径作为图表记录添加到结果列表
charts.append({
"type": "chart",
"content": {
"image_path": image_path, # 图像文件路径
"xref": xref # 图像的xref
},
"source": {
"chapter": chapter_num,
"image_index": img_index + 1
}
})
# 清理内存中的图像对象
base_image = None
return charts
def process_handout_pdf(pdf_path: str, chapter_num: int) -> List[Dict[str, Any]]:
"""
处理handout PDF,提取文字、图像、图表,并保存图像到文件系统。
"""
data = [] # 存储提取的所有数据和标签
doc = None
try:
doc = fitz.open(pdf_path)
except Exception as e:
print(f"无法打开PDF: {e}")
return []
total_pages = len(doc)
for page_num in range(total_pages):
page = doc.load_page(page_num) # 加载页
# 1. 提取文字
text_data = extract_text(page)
if text_data:
data.append({
"type": "text",
"content": text_data,
"source": {
"pdf_name": pdf_path.split('/')[-1], # 只取文件名
"page": page_num + 1 # 页面编号从1开始
}
})
# 2. 提取图像
image_data = extract_images(page)
for img in image_data:
data.append(img)
# 3. 提取图表
chart_data = extract_charts(page, chapter_num)
for chart in chart_data:
data.append(chart)
# 关闭PDF
doc.close()
return data
# 示例:调用提取图表、图像、文字的函数
def main():
pdf_path = "path_to_your_pdf.pdf" # 请替换成你的 PDF 文件路径
chapter_num = 1 # 章节号
extracted_data = process_handout_pdf(pdf_path, chapter_num)
# 打印提取的数据
for item in extracted_data:
if item["type"] == "text":
print(f"文字内容:\n{item['content']}")
elif item["type"] == "image":
print(f"图像保存路径: {item['content']['image_path']}")
elif item["type"] == "chart":
print(f"图表图像保存路径: {item['content']['image_path']}")
if __name__ == "__main__":
main()
代码说明
提取文字:
使用 extract_text(page) 提取页面上的纯文本内容。
使用 page.get_text("text") 提取每页的文字信息,返回一个文本字符串。
提取图像:
使用 extract_images(page) 提取页面上的所有图像。每个图像都转换为 PNG 格式并保存在本地文件系统中。
图像路径和 xref(图片标识符)被存储在返回的数据结构中,以便后续使用。
提取图表:
使用 extract_charts(page, chapter_num) 提取页面上的图表,并假设它们是图像。图像转换为 PNG 格式并保存到文件系统中。
图表也被保存为图像路径,并返回给调用者。
数据结构:
提取的每个元素(文字、图像、图表)都以字典的形式存储,包含 type(内容类型)、content(具体内容,如文字或图像路径)以及 source(来源,包含文件名、页码或章节号)。
图像和图表存储:
图像和图表被保存在 extracted_images 目录中,文件名根据图像的 xref 或章节号来生成(例如 image_1.png 或 chart_1_1.png)。
图像的路径被添加到返回的字典中,之后可以进一步使用或将其存储到数据库中(如果需要)。
主函数 main():
示例中,我们调用 process_handout_pdf 来提取 PDF 中的文字、图像和图表,并打印结果。
运行方式
替换 pdf_path 为你需要提取数据的 PDF 文件路径。
调用 process_handout_pdf 函数,提取 PDF 中的文字、图像和图表。
结果将被打印出来,包括每个提取内容的类型(文字、图像、图表)和相应的保存路径或内容。
改进思路
图像压缩与优化:为了节省存储空间,你可以考虑将图像保存时进行压缩。可以使用 PIL 库来调整图像质量或大小。
OCR:如果图表包含文本内容,且你希望从图像中提取文字,可以集成 OCR 库(如 Tesseract)来进行图像文字识别。
更多提取内容类型:如果 PDF 中包含公式、表格或其他元素,你可以在 extract_other_elements 函数中扩展这些提取方法。
这个版本的代码应该能够满足你提取文字、图像、图表,并将它们保存到本地文件系统中的需求。
Dialogue 4
page.get_text("text") # 获取纯文本////一行代码就能搞定吗?
是的,page.get_text("text") 这行代码实际上已经是非常高效的文本提取方式,它会提取 PDF 页面的纯文本内容(包括所有段落、行、单词等)。这个方法会将 PDF 页面的文本按行解析并返回一个字符串。
但是,要注意以下几点:
page.get_text("text") 的工作原理
get_text("text")方法通过解析 PDF 文件中的文本对象(如字体、大小、位置等信息)来提取文本。- 它返回的是一个包含页面所有文本内容的单个字符串,其中包括按行分割的文本内容。
简单的示例
python
import fitz # PyMuPDF
def extract_text(page):
text = page.get_text("text")
return text
# 示例调用
doc = fitz.open("path_to_pdf.pdf")
page = doc.load_page(0) # 加载第一页
text = extract_text(page)
print(text) # 打印提取的文本内容
为什么 get_text("text") 是足够的:
-
快速提取文本:
- 这是最常用且高效的提取方法,适用于大部分需要提取页面内容的场景。它能够返回 PDF 页面中的所有可见文本。
-
简单易用:
- 只需一行代码
page.get_text("text")就能获取整个页面的文本内容,避免了手动逐行提取或计算布局的问题。
- 只需一行代码
可能的局限性
-
格式丢失:
- 虽然该方法提取了文本内容,但它会丢失原始 PDF 中的布局、字体、颜色等信息。所有的文本都被视作简单的行内容,无法保留表格、列或页面的排版样式。
-
复杂 PDF 文件:
- 对于一些复杂的 PDF 文件,特别是那些有非常复杂的排版、图像或自定义字体的文件,
get_text("text")可能无法完美提取所有内容。比如,如果文本被嵌入在图像或特殊对象中,get_text("text")无法识别这些内容。
- 对于一些复杂的 PDF 文件,特别是那些有非常复杂的排版、图像或自定义字体的文件,
-
文本提取精度:
- 有时如果文本在 PDF 中按非标准方式编码(例如使用图像嵌入或某些字体压缩技术),
get_text提取的文本可能会存在乱码或错误的情况。
- 有时如果文本在 PDF 中按非标准方式编码(例如使用图像嵌入或某些字体压缩技术),
其他选项
如果你需要更精确的控制文本提取,或者希望保留更多的排版信息,可以使用其他参数或方法:
get_text("dict"):返回一个包含详细信息的字典,其中包括文本块、单词、字体、颜色等。这对于处理需要保留排版或结构的情况非常有用。
python
# 获取更详细的文本信息(包括字体、位置等)
text_dict = page.get_text("dict")
print(text_dict)
get_text("html"):返回 HTML 格式的文本内容,包括标签和其他排版信息,适合将 PDF 转换为网页格式。
总结
对于大多数场景,page.get_text("text") 这行代码确实已经足够处理大部分文本提取任务,并且是非常简洁有效的方式。只有在需要保留更复杂的格式或信息时,才可能需要使用其他更高级的选项(如 "dict" 或 "html")。