1. Word文档格式简介 #
1.1 什么是.docx格式 #
.docx是Microsoft Office 2007及以后版本使用的Word文档格式。它基于Office Open XML标准,本质上是一个压缩的XML文件集合。
简单理解:
- 将
.docx文件重命名为.zip,然后解压,可以看到内部的XML文件 - 主要的文档内容在
word/document.xml文件中 - 图片、样式等资源存储在单独的文件夹中
1.2 Word文档的内部结构 #
Word文档主要由以下部分组成:
document.xml(主文档内容)
├── 段落(<w:p>):文本内容
├── 表格(<w:tbl>):表格数据
├── 图片(<pic:pic>):嵌入的图片
└── 其他元素:目录、页眉页脚等重要说明:作为使用者,你不需要深入了解XML结构,只需要知道Word文档是结构化的即可。RAGFlow已经帮我们处理了这些底层细节。
1.3 为什么需要了解格式 #
了解Word文档格式有助于:
- 理解解析过程:知道解析器在做什么
- 处理特殊元素:知道如何提取图片、表格等
- 调试问题:遇到解析问题时知道可能的原因
2. Doc格式转换为Docx #
2.1 为什么需要转换 #
RAGFlow使用的python-docx库只能处理.docx格式,不支持旧的.doc格式。如果你的文档是.doc格式,需要先转换为.docx。
2.2 转换方法 #
2.2.1 Windows系统转换 #
方法一:使用LibreOffice(推荐)
# 1. 下载并安装LibreOffice
# 从官网下载:https://www.libreoffice.org/
# 2. 打开命令提示符,进入LibreOffice安装目录
cd "C:\Program Files\LibreOffice\program"
# 3. 转换单个文件
soffice.exe --headless --convert-to docx "D:\YourFolder\document.doc"
# 4. 批量转换(使用PowerShell)
Get-ChildItem -Path "D:\YourFolder" -Filter "*.doc" -Recurse | ForEach-Object {
& "C:\Program Files\LibreOffice\program\soffice.exe" --headless --convert-to docx $_.FullName
}方法二:使用Microsoft Word(需要安装Office)
# 创建PowerShell脚本 convert-doc-to-docx.ps1
$word = New-Object -ComObject Word.Application
$word.Visible = $false
Get-ChildItem -Recurse -Filter *.doc | ForEach-Object {
$doc = $word.Documents.Open($_.FullName)
$newName = $_.FullName -replace '\.doc$', '.docx'
$doc.SaveAs([ref]$newName, [ref]12) # 12是docx的格式代码
$doc.Close()
}
$word.Quit()2.2.2 Linux系统转换 #
# 1. 安装LibreOffice(如果未安装)
sudo apt-get install libreoffice # Debian/Ubuntu
sudo dnf install libreoffice # Fedora
# 2. 转换单个文件
libreoffice --headless --convert-to docx document.doc
# 3. 批量转换当前目录及子目录所有.doc文件
find . -name "*.doc" -exec libreoffice --headless --convert-to docx {} \;注意事项:
- 转换后的文件默认保存在原目录
- 确保转换时文件没有被其他程序占用
- 旧版
.doc文件可能需要Word 2003以上兼容模式
3. RAGFlow Word文档解析基础 #
3.1 什么是RAGFlowDocxParser #
RAGFlowDocxParser是RAGFlow提供的Word文档解析器基础类,它使用python-docx库来解析Word文档。
主要功能:
- 提取文档中的段落文本
- 提取文档中的表格
- 获取文档的样式信息
3.2 简单使用示例 #
下面是一个完整的使用示例:
# 导入必要的库
import os
import sys
# 添加项目根目录到Python路径
sys.path.insert(0, os.path.abspath('.'))
# 导入RAGFlow的Word解析器
from deepdoc.parser.docx_parser import RAGFlowDocxParser
# 定义Word文件路径(请替换为你的Word文件路径)
docx_file = './data/docx_fangan-2.docx'
# 创建Word解析器实例
print("正在初始化Word解析器...")
parser = RAGFlowDocxParser()
print("Word解析器初始化完成!")
# 解析Word文件
# from_page和to_page参数可以指定解析的页码范围(Word文档按段落计算)
print(f"正在解析Word文件: {docx_file}")
sections, tables = parser(docx_file, from_page=0, to_page=100000)
# 处理段落内容
# sections是一个列表,每个元素是(段落文本, 样式名称)的元组
print("\n========== 段落内容 ==========")
for idx, (text, style) in enumerate(sections, 1):
if text.strip(): # 只显示非空段落
print(f"\n 段落 {idx} ")
print(f"样式: {style}")
print(f"内容: {text[:200]}...") # 只显示前200个字符
# 处理表格内容
# tables是一个列表,每个元素是表格转换后的文本行列表
print("\n========== 表格内容 ==========")
for idx, table_lines in enumerate(tables, 1):
print(f"\n 表格 {idx} ")
for line in table_lines:
print(line)
print("\n解析完成!")代码说明:
- 导入模块:导入RAGFlowDocxParser类
- 初始化解析器:创建解析器实例
- 解析Word文档:调用解析器,返回段落和表格
- 处理结果:
sections:段落列表,每个元素包含文本和样式tables:表格列表,每个表格转换为文本行
3.3 使用应用解析器 #
RAGFlow在基础解析器之上,提供了应用解析器(位于rag/app/naive.py),它提供了更便捷的使用方式:
# 导入必要的库
import os
import sys
# 添加项目根目录到Python路径
sys.path.insert(0, os.path.abspath('.'))
# 导入naive应用解析器
from rag.app.naive import Docx, chunk
# 定义回调函数(用于显示进度,可选)
def dummy(prog=None, msg=""):
if msg:
print(f"进度: {msg}")
# 定义Word文件路径
file_name = './data/docx_fangan-2.docx'
# 方法一:直接解析(不进行分块)
print("方法一:直接解析")
docx_parse = Docx()
docx_content = docx_parse(file_name)
print(f"解析结果: {len(docx_content)} 个元素")
# 方法二:解析并自动分块
print("\n方法二:解析并分块")
data_chunk = chunk(file_name, callback=dummy)
# 查看分块结果
print(f"\n共生成 {len(data_chunk)} 个chunk")
for idx, data in enumerate(data_chunk[:5], 1): # 只显示前5个
print(f"\n Chunk {idx} ")
print(data['content_with_weight'][:200] + "...") # 只显示前200个字符代码说明:
- 直接解析:使用
Docx类直接解析,返回原始元素列表 - 解析并分块:使用
chunk函数,自动进行分块处理 - 结果格式:每个chunk包含
content_with_weight(内容)和metadata(元数据)
4. 自定义Word文档解析器 #
4.1 为什么需要自定义解析器 #
虽然RAGFlow提供了基础解析器,但在实际应用中,你可能需要:
- 保持顺序性:确保文本、表格、图片按文档中的顺序处理
- 添加上下文:为表格和图片添加前后文本作为上下文
- 图片描述:使用视觉大模型将图片转换为文本描述
- 表格总结:对表格进行总结,支持分析型问题
4.2 自定义解析器的实现思路 #
我们的自定义解析器将实现以下功能:
- 按顺序提取:按照文档中的顺序提取段落、表格、图片
- 表格上下文:为表格添加前一段文本作为上下文
- 图片描述:使用视觉大模型将图片转换为文本描述
- 图片上下文:为图片添加前后文本作为上下文
- 表格总结:使用LLM对表格进行总结
4.3 完整实现示例 #
下面是一个完整的自定义Word解析器实现:
# 导入必要的库
import os
import sys
import re
import uuid
# 添加项目根目录到Python路径
sys.path.insert(0, os.path.abspath('.'))
# 导入RAGFlow的基础解析器
from deepdoc.parser import DocxParser
# 导入python-docx相关
from docx import Document
from docx.text.paragraph import Paragraph
from docx.table import Table
from docx.image.exceptions import UnrecognizedImageError
from io import BytesIO
# 导入图像处理
from PIL import Image
# 导入LangChain相关
from langchain_core.documents import Document as LDocument
from langchain_text_splitters import RecursiveCharacterTextSplitter
# 导入工具函数(需要自己实现或从utils导入)
from model import RagLLM
from utils import table_to_summary, image_to_txt
class MyDocx(DocxParser):
"""
自定义Word文档解析器
功能:
1. 按顺序提取文本、表格、图片
2. 为表格和图片添加上下文
3. 支持图片描述和表格总结
"""
def __init__(self):
# 调用父类初始化
super().__init__()
def __clean(self, line):
"""
清理文本:移除全角空格等特殊字符
参数:
line: 原始文本行
返回:
str: 清理后的文本
"""
# 将全角空格替换为普通空格,并去除首尾空白
line = re.sub(r"\u3000", " ", line).strip()
return line
def get_picture(self, document, paragraph):
"""
从段落中提取图片
参数:
document: Word文档对象
paragraph: 段落对象
返回:
list: PIL Image对象列表,如果没有图片则返回None
"""
# 使用XPath查找段落中的图片元素
# .//pic:pic 表示查找所有pic:pic元素(无论嵌套层级)
imgs = paragraph._element.xpath('.//pic:pic')
# 如果没有找到图片,返回None
if not imgs:
return None
ret_imgs = [] # 存储提取的图片列表
# 遍历找到的所有图片
for img in imgs:
try:
# 获取图片的嵌入ID
# a:blip/@r:embed 表示获取blip元素的embed属性
embed = img.xpath('.//a:blip/@r:embed')[0]
# 根据ID获取图片的相关部分
related_part = document.part.related_parts[embed]
# 获取图片的二进制数据
image_blob = related_part.image.blob
# 将二进制数据转换为PIL Image对象
image = Image.open(BytesIO(image_blob)).convert('RGB')
ret_imgs.append(image)
except UnrecognizedImageError:
# 如果图片格式不被识别,跳过
print("Unrecognized image format. Skipping image.")
except Exception as e:
# 其他错误也跳过
print(f"Error extracting image: {e}")
# 返回图片列表,如果没有图片则返回None
return ret_imgs if ret_imgs else None
def get_table(self, tb):
"""
将表格转换为HTML格式
参数:
tb: docx表格对象
返回:
str: HTML格式的表格字符串
"""
html = "<table>"
# 遍历表格的每一行
for r in tb.rows:
html += "<tr>"
i = 0
# 处理合并单元格
# 如果相邻单元格内容相同,认为是合并单元格
while i < len(r.cells):
span = 1 # 合并的单元格数量
c = r.cells[i]
# 检查后续单元格是否与当前单元格内容相同
for j in range(i+1, len(r.cells)):
if c.text == r.cells[j].text:
span += 1
i = j
else:
break
i += 1
# 根据是否合并添加不同的HTML标签
if span == 1:
html += f"<td>{c.text}</td>"
else:
html += f"<td colspan='{span}'>{c.text}</td>"
html += "</tr>"
html += "</table>"
return html
def convert_table_to_lines(self, tb):
"""
将表格转换为文本行列表(用于明细检索)
参数:
tb: docx表格对象
返回:
list: 表格转换后的文本行列表
"""
lines = []
# 如果表格为空或只有一行(表头),返回空列表
if not tb.rows or len(tb.rows) <= 1:
return lines
# 获取表头(第一行)
headers = [cell.text.strip() for cell in tb.rows[0].cells]
# 处理数据行(从第二行开始)
for row in tb.rows[1:]:
# 获取当前行的所有单元格文本
row_values = [cell.text.strip() for cell in row.cells]
# 拼接表头和数据值,格式:【表头:值】
row_text = []
for i, value in enumerate(row_values):
if i < len(headers): # 确保有对应的表头
header = headers[i]
row_text.append(f"【{header}:{value}】")
else:
row_text.append(f"【{value}】")
# 将当前行的所有单元格拼接结果添加到结果列表
lines.append("".join(row_text))
return lines
def __call__(self, filename, binary=None, from_page=0, to_page=100000):
"""
解析Word文档,按顺序提取所有元素
参数:
filename: Word文件路径
binary: Word二进制数据(可选)
from_page: 起始页(段落计数)
to_page: 结束页(段落计数)
返回:
list: 元素列表,每个元素是(文本/表格HTML, 图片对象, 类型, 额外信息)的元组
"""
# 打开Word文档
doc = Document(filename) if not binary else Document(BytesIO(binary))
lines = [] # 存储所有提取的元素
# 遍历文档主体中的每个子元素
# doc.element.body.iterchildren() 返回文档主体中的所有子元素
for element in doc.element.body.iterchildren():
# 处理段落(paragraph)
if element.tag.endswith('p'):
# 创建段落对象
paragraph = Paragraph(element, doc.element)
# 尝试从段落中提取图片
image_list = self.get_picture(doc, paragraph)
# 清理段落文本
clean_text = self.__clean(paragraph.text)
# 如果有图片,标记为图片类型
if image_list:
# 格式:(文本, 图片列表, "image", None)
lines.append((clean_text, image_list, "image", None))
else:
# 格式:(文本, None, "text", None)
lines.append((clean_text, None, "text", None))
# 处理表格(table)
elif element.tag.endswith('tbl'):
# 创建表格对象
table = Table(element, doc.element)
# 将表格转换为HTML
table_html = self.get_table(table)
# 将表格转换为文本行(用于明细检索)
table_lines = self.convert_table_to_lines(table)
# 格式:(表格HTML, None, "table", 表格文本行列表)
lines.append((table_html, None, "table", table_lines))
# 处理结构化文档标签(sdt),如目录等
elif element.tag.endswith('sdt'):
sdt_content = []
# 遍历sdt元素的所有子元素
for child in element.iterchildren():
if child.tag.endswith('sdtContent'):
# 在sdtContent中查找段落
for achild in child.iterchildren():
if achild.tag.endswith('p'):
# 提取段落文本并清理
para = Paragraph(achild, doc.element)
sdt_content.append(self.__clean(para.text) + "\n")
# 格式:(目录内容, None, "sdt", None)
lines.append(("".join(sdt_content), None, "sdt", None))
return lines
def _len_wo_blank(self, text):
"""
计算文本长度(不包括空格)
参数:
text: 文本字符串
返回:
int: 去除空格后的文本长度
"""
return len(text.replace(" ", ""))
def pre_chunk(self, filename, binary=None, from_page=0, to_page=100000):
"""
预处理:提取文本、表格、图片,并添加上下文
参数:
filename: Word文件路径
binary: Word二进制数据
from_page: 起始页
to_page: 结束页
返回:
tuple: (文本chunks列表, 表格chunks列表, 图片chunks列表)
"""
# 调用__call__方法解析文档
lines = self(filename, binary, from_page, to_page)
# 去掉空白行
# 保留图片和有效的文本/表格/目录
lines = [
(text, img, atype, externel_info)
for text, img, atype, externel_info in lines
if atype == "image" or (atype in ["text", "sdt", "table"] and text.strip())
]
# 初始化结果列表
chunks = [] # 文本chunks
images = [] # 图片chunks(包含上下文)
tables = [] # 表格chunks(包含上下文)
# 配置参数
get_text_num = 2 # 获取前几行文本作为上下文
skip_table_images_text_cnt = 30 # 如果文本长度小于此值,可能只是标题,不单独作为chunk
# 遍历所有元素
i = 0
while i < len(lines):
# 获取前几行文本作为上下文
prev_texts = []
prev_index = i - 1
# 向前查找文本元素
while prev_index >= 0 and len(prev_texts) < get_text_num:
if lines[prev_index][2] == "text":
prev_texts.append(lines[prev_index][0])
prev_index -= 1
# 合并前几行文本(注意要反转顺序,因为是从后往前查找的)
prev_text = " ".join(reversed(prev_texts))
# 处理表格
if lines[i][2] == "table":
# 合并表格和前文文本
html_combined_text = f"{prev_text} \n {lines[i][0]}"
# 保存表格信息:格式为(合并后的文本, 表格文本行列表, 前文文本)
tables.append((html_combined_text, lines[i][3], prev_text))
# 如果前文本很短且已经在chunks中,从chunks中移除(避免重复)
for text in prev_texts:
if self._len_wo_blank(text) < skip_table_images_text_cnt and text in chunks:
chunks.remove(text)
# 处理图片
elif lines[i][2] == "image":
# 向后查找文本元素(图片说明通常在图片下方)
next_index = i + 1
while next_index < len(lines) and lines[next_index][2] != "text":
next_index += 1
# 获取后文文本
next_text = lines[next_index][0] if next_index < len(lines) else ""
# 保存图片信息:格式为(前文文本, 图片段落文本, 图片对象, 后文文本)
images.append((prev_text, lines[i][0], lines[i][1], next_text))
# 如果前后文本很短,从chunks中移除(避免重复)
for text in prev_texts:
if self._len_wo_blank(text) < skip_table_images_text_cnt and text in chunks:
chunks.remove(text)
# 如果后文本很短,标记为skip,后续不添加到chunks
if next_index < len(lines) and self._len_wo_blank(next_text) < skip_table_images_text_cnt:
lines[next_index] = (lines[next_index][0], lines[next_index][1], lines[next_index][2], "skip")
# 如果图片段落有文本,添加到chunks
if lines[i][0] != "":
chunks.append(lines[i][0])
# 处理普通文本
else:
# 普通文本直接添加到chunks,除非被标记为skip
if lines[i][3] != "skip":
chunks.append(lines[i][0])
i += 1
return chunks, tables, images
def chunk(self, filename, binary=None, from_page=0, to_page=100000, llm=None,
chunk_size=512, chunk_overlap=30):
"""
最终分块函数:生成LangChain Document列表
参数:
filename: Word文件路径
binary: Word二进制数据
from_page: 起始页
to_page: 结束页
llm: 语言模型实例(用于表格总结和图片描述)
chunk_size: 文本chunk大小
chunk_overlap: chunk重叠大小
返回:
list: LangChain Document列表
"""
# 步骤1: 预处理,获取文本、表格、图片
txt_chunks, table_chunks, image_chunks = self.pre_chunk(
filename, binary, from_page, to_page
)
# 如果没有提供LLM,创建一个
if llm is None:
llm = RagLLM()
docs = []
# 步骤2: 处理表格(总结)
print("正在处理表格...")
for table_info in table_chunks:
# table_info格式:(合并后的文本, 表格文本行列表, 前文文本)
table_chunk, table_txts, prev_text = table_info
# 使用LLM对表格进行总结
table_summary = table_to_summary(table_chunk, llm)
# 创建表格总结Document
doc = LDocument(
page_content=table_summary, # 总结作为主要内容
metadata={
"source": "table",
"content": table_chunk # 原始表格内容保存在metadata中
}
)
docs.append(doc)
# 为表格的每一行创建明细Document(用于精确检索)
for row in table_txts:
doc = LDocument(
page_content=f" 明细项:{row}",
metadata={
"source": "table-row",
"content": ""
}
)
docs.append(doc)
# 步骤3: 处理图片(描述)
print("正在处理图片...")
img_path = "./doc_images"
os.makedirs(img_path, exist_ok=True)
for image_chunk in image_chunks:
# image_chunk格式:(前文文本, 图片段落文本, 图片对象, 后文文本)
pre_text, txt, img, post_text = image_chunk
# 构建图片描述提示词
prompt = f"""
请根据图片的相关上下文和信息,用中文简要总结下图中所描述的内容,如果有数据请提取出数据,并做分析,否则只要简要描述。
上下文:
{pre_text}
{post_text}
图表信息:{txt}
"""
# 处理图片(可能是单个图片或图片列表)
imgs = img
if not isinstance(img, list):
imgs = [img]
filenames = [] # 存储保存的图片路径
image_descs = [] # 存储图片描述
# 遍历所有图片
for im in imgs:
# 使用视觉大模型描述图片
image_desc = image_to_txt(im, prompt, 300)
# 保存图片到本地
img_file_name = f"{img_path}/image_{uuid.uuid4()}.png"
im.save(img_file_name)
filenames.append(img_file_name)
image_descs.append(image_desc[0] if isinstance(image_desc, list) else image_desc)
# 合并所有图片的描述
image_desc = "\n".join(image_descs)
# 创建图片Document
# 内容包含上下文和图片描述,metadata中保存图片路径
doc = LDocument(
page_content=f"{pre_text} {post_text} 具体信息:{image_desc}",
metadata={
"source": "image",
"content": "##".join(filenames) # 多个图片路径用##分隔
}
)
docs.append(doc)
# 步骤4: 处理文本(分块)
print("正在处理文本...")
all_texts = "\n".join(txt_chunks)
# 使用递归字符分割器进行分块
r_spliter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
separators=[
"\n\n", # 双换行(段落分隔)
"\n", # 单换行
".", # 英文句号
"\uff0e", # 全角句号
"\u3002", # 中文句号
",", # 英文逗号
"\uff0c", # 全角逗号
"\u3001", # 中文顿号
]
)
# 分割文本
chunks = r_spliter.split_text(all_texts)
# 创建文本Document
for chunk_text in chunks:
doc = LDocument(
page_content=chunk_text,
metadata={
"source": "text",
"content": ""
}
)
docs.append(doc)
print(f"处理完成!共生成 {len(docs)} 个Document")
return docs
# 使用示例
if __name__ == "__main__":
# 定义Word文件路径
docx_file = './data/docx_fangan-2.docx'
# 创建自定义解析器实例
parser = MyDocx()
# 解析并分块
documents = parser.chunk(
docx_file,
chunk_size=512,
chunk_overlap=30
)
# 查看结果统计
text_count = sum(1 for doc in documents if doc.metadata['source'] == 'text')
table_count = sum(1 for doc in documents if doc.metadata['source'] == 'table')
table_row_count = sum(1 for doc in documents if doc.metadata['source'] == 'table-row')
image_count = sum(1 for doc in documents if doc.metadata['source'] == 'image')
print(f"\n解析完成!")
print(f"文本chunks: {text_count} 个")
print(f"表格chunks: {table_count} 个")
print(f"表格行chunks: {table_row_count} 个")
print(f"图片chunks: {image_count} 个")
print(f"总计: {len(documents)} 个Document")
# 查看示例结果
print("\n========== 示例结果 ==========")
for idx, doc in enumerate(documents[:3], 1):
print(f"\n Document {idx} ({doc.metadata['source']}) ")
content_preview = doc.page_content[:200] + "..." if len(doc.page_content) > 200 else doc.page_content
print(f"内容: {content_preview}")
print(f"元数据: {doc.metadata}")4.4 关键功能说明 #
4.4.1 图片提取 #
图片提取是Word解析中的一个难点,因为图片嵌入在文档的XML结构中。我们的实现:
- 使用XPath查找:
paragraph._element.xpath('.//pic:pic')查找段落中的图片元素 - 获取图片数据:通过嵌入ID获取图片的二进制数据
- 转换为PIL Image:将二进制数据转换为PIL Image对象,方便后续处理
4.4.2 表格处理 #
表格处理包括:
- 转换为HTML:保持表格的结构,方便显示
- 转换为文本行:将表格转换为"表头:值"的格式,方便精确检索
- 处理合并单元格:识别合并的单元格,正确生成HTML
4.4.3 上下文添加 #
为表格和图片添加上下文:
- 表格上下文:向前查找最近的文本段落(通常是表格标题)
- 图片上下文:向前和向后查找文本段落(图片说明可能在前后)
5. 完整使用示例 #
下面是一个完整的示例,演示如何使用自定义解析器:
# 导入必要的库
import os
import sys
# 添加项目根目录到Python路径
sys.path.insert(0, os.path.abspath('.'))
# 导入自定义解析器(假设已经保存为my_docx_parser.py)
from my_docx_parser import MyDocx
from model import RagLLM
# 定义回调函数(可选)
def progress_callback(prog=None, msg=""):
"""进度回调函数"""
if msg:
print(f"[进度] {msg}")
# 定义Word文件路径
docx_file = './data/docx_fangan-2.docx'
# 创建LLM实例(用于表格总结和图片描述)
llm = RagLLM()
# 创建自定义解析器实例
parser = MyDocx()
# 解析Word文档并生成Document列表
print("开始解析Word文档...")
documents = parser.chunk(
docx_file,
llm=llm,
chunk_size=512,
chunk_overlap=30
)
# 查看结果统计
text_count = sum(1 for doc in documents if doc.metadata['source'] == 'text')
table_count = sum(1 for doc in documents if doc.metadata['source'] == 'table')
table_row_count = sum(1 for doc in documents if doc.metadata['source'] == 'table-row')
image_count = sum(1 for doc in documents if doc.metadata['source'] == 'image')
print(f"\n解析完成!")
print(f"文本chunks: {text_count} 个")
print(f"表格chunks: {table_count} 个")
print(f"表格行chunks: {table_row_count} 个")
print(f"图片chunks: {image_count} 个")
print(f"总计: {len(documents)} 个Document")
# 查看示例结果
print("\n========== 示例结果 ==========")
for idx, doc in enumerate(documents[:5], 1):
print(f"\n Document {idx} ({doc.metadata['source']}) ")
content_preview = doc.page_content[:200] + "..." if len(doc.page_content) > 200 else doc.page_content
print(f"内容: {content_preview}")
if doc.metadata.get('content'):
print(f"额外内容: {doc.metadata['content'][:100]}...")
# 可以保存到向量数据库
# from langchain_chroma import Chroma
# from model import RagEmbedding
#
# embedding_model = RagEmbedding()
# vector_db = Chroma.from_documents(
# documents,
# embedding_model.get_embedding_fun(),
# collection_name="my_docx_collection"
# )6. 总结与最佳实践 #
6.1 Word解析 vs PDF解析 #
| 特性 | Word解析 | PDF解析 | |||| | 数据格式 | 结构化(XML) | 渲染后的图片 | | OCR需求 | 不需要 | 需要 | | 图片提取 | 相对简单 | 需要OCR识别 | | 表格处理 | 直接读取 | 需要表格识别 | | 格式信息 | 丰富(样式等) | 有限 |
6.2 常见问题处理 #
问题1:图片提取失败
- 原因:图片格式不支持或文档损坏
- 解决方案:添加异常处理,跳过无法提取的图片
问题2:表格结构复杂
- 原因:合并单元格、嵌套表格等
- 解决方案:使用更复杂的表格解析逻辑,或转换为Markdown格式
问题3:文档格式不标准
- 原因:文档可能包含特殊元素或格式
- 解决方案:添加更多的元素类型处理,或使用try-except跳过未知元素
6.3 性能优化建议 #
- 批量处理:对于大量文档,考虑并行处理
- 缓存结果:解析结果可以缓存,避免重复解析
- 选择性处理:如果不需要图片,可以跳过图片提取步骤