3、文档加载器实战:使用LangChain加载PDF、Word、TXT、HTML等不同格式文档
文档加载,说白了就是RAG系统的「入口关」。你想想看,如果连文档都读不进来,后面的检索和生成全是空谈。我在做第一个RAG项目时,就栽在这个环节上——PDF里的表格全乱了,Word文档的样式丢失,HTML里的图片链接全断了……嗯,今天咱们就把这些坑一个个填上。
3.1 为什么需要文档加载器?
LangChain的文档加载器,本质上是一个「格式适配器」。它把不同格式的文件,统一转换成LangChain内部的Document对象。这个对象包含两个核心部分:
- page_content:文档的文本内容
- metadata:文档的元数据(来源、页码、创建时间等)
我个人习惯把加载器理解为「翻译官」——它把PDF、Word、TXT、HTML这些「方言」,翻译成RAG系统能听懂的「普通话」。
3.2 PDF加载器实战
PDF是最常见的文档格式,但也是最难处理的。为什么?因为PDF本质上是「打印描述语言」,它记录的是「在哪里画什么」,而不是「文本是什么」。
3.2.1 PyMuPDFLoader(推荐)
我个人最常用的是PyMuPDFLoader。它速度快,对中文支持好,还能保留基本的排版信息。
from langchain_community.document_loaders import PyMuPDFLoader
# 加载PDF文件
loader = PyMuPDFLoader("data/产品手册.pdf")
documents = loader.load()
# 查看加载结果
print(f"加载了 {len(documents)} 页")
print(f"第一页内容(前200字): {documents[0].page_content[:200]}")
print(f"元数据: {documents[0].metadata}")
mode="page"参数。我在处理技术文档时,经常用这个模式。
3.2.2 PDFPlumberLoader(表格友好)
如果你的PDF里有大量表格,PDFPlumberLoader是更好的选择。我曾经处理过一份财务报表PDF,里面全是表格,PyMuPDFLoader读出来全是乱码,换成PDFPlumberLoader就完美解决了。
from langchain_community.document_loaders import PDFPlumberLoader
loader = PDFPlumberLoader("data/财务报表.pdf")
documents = loader.load()
# PDFPlumberLoader会保留表格结构
print(documents[0].page_content)
3.3 Word文档加载器
Word文档(.docx)的处理相对简单,因为它是结构化的XML格式。LangChain提供了UnstructuredWordDocumentLoader和Docx2txtLoader两种方式。
3.3.1 UnstructuredWordDocumentLoader
这个加载器基于unstructured库,能保留标题、列表、表格等结构信息。
from langchain_community.document_loaders import UnstructuredWordDocumentLoader
loader = UnstructuredWordDocumentLoader("data/项目报告.docx")
documents = loader.load()
print(f"文档长度: {len(documents[0].page_content)} 字符")
print(f"元数据: {documents[0].metadata}")
3.3.2 Docx2txtLoader(轻量级)
如果你只需要纯文本,Docx2txtLoader更轻量,速度也更快。
from langchain_community.document_loaders import Docx2txtLoader
loader = Docx2txtLoader("data/项目报告.docx")
documents = loader.load()
# 纯文本输出,没有格式信息
print(documents[0].page_content[:500])
3.4 TXT文本加载器
TXT是最简单的格式,但也是最容易出问题的——编码问题。我曾经被一个GBK编码的TXT文件坑了整整一下午。
from langchain_community.document_loaders import TextLoader
# 默认使用UTF-8编码
loader = TextLoader("data/笔记.txt")
documents = loader.load()
# 如果遇到编码问题,指定编码
loader = TextLoader("data/笔记.txt", encoding="gbk")
documents = loader.load()
print(documents[0].page_content[:200])
chardet库检测编码:import chardetwith open("file.txt", "rb") as f: result = chardet.detect(f.read()) print(result['encoding'])
3.5 HTML加载器
HTML文档的难点在于「去噪」——你需要提取正文内容,去掉导航栏、广告、页脚等无关信息。
3.5.1 UnstructuredHTMLLoader
这个加载器基于unstructured库,能自动识别并提取正文内容。
from langchain_community.document_loaders import UnstructuredHTMLLoader
loader = UnstructuredHTMLLoader("data/文章.html")
documents = loader.load()
# 自动去除了HTML标签和无关元素
print(documents[0].page_content[:500])
3.5.2 BSHTMLLoader(自定义提取)
如果你需要更精细的控制,可以用BSHTMLLoader,它基于BeautifulSoup,你可以自定义提取规则。
from langchain_community.document_loaders import BSHTMLLoader
loader = BSHTMLLoader("data/文章.html")
documents = loader.load()
# 可以访问BeautifulSoup对象进行自定义处理
soup = loader.soup
# 提取所有h2标题
h2_tags = soup.find_all('h2')
for h2 in h2_tags:
print(h2.get_text())
<script>、<style>标签,再提取<article>或<main>区域的内容。这样准确率最高。
3.6 多格式批量加载器
实际项目中,你往往需要同时处理多种格式。LangChain提供了DirectoryLoader,可以批量加载整个目录下的文件。
from langchain_community.document_loaders import DirectoryLoader
from langchain_community.document_loaders import PyMuPDFLoader
from langchain_community.document_loaders import TextLoader
from langchain_community.document_loaders import UnstructuredWordDocumentLoader
# 批量加载PDF文件
pdf_loader = DirectoryLoader(
"data/",
glob="**/*.pdf",
loader_cls=PyMuPDFLoader
)
pdf_docs = pdf_loader.load()
# 批量加载TXT文件
txt_loader = DirectoryLoader(
"data/",
glob="**/*.txt",
loader_cls=TextLoader,
loader_kwargs={"encoding": "utf-8"}
)
txt_docs = txt_loader.load()
# 合并所有文档
all_docs = pdf_docs + txt_docs
print(f"总共加载了 {len(all_docs)} 个文档")
3.7 加载器对比总结
| 格式 | 推荐加载器 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| PyMuPDFLoader | 速度快,中文支持好 | 表格处理一般 | 通用PDF文档 | |
| PDF(表格) | PDFPlumberLoader | 表格提取准确 | 扫描件不支持 | 财务报表、数据报表 |
| Word | UnstructuredWordDocumentLoader | 保留结构信息 | 依赖unstructured库 | 需要保留标题层级 |
| Word(纯文本) | Docx2txtLoader | 轻量快速 | 丢失格式信息 | 只需提取文本 |
| TXT | TextLoader | 简单直接 | 需处理编码 | 纯文本文件 |
| HTML | UnstructuredHTMLLoader | 自动去噪 | 可能误删内容 | 通用网页 |
| HTML(自定义) | BSHTMLLoader | 灵活可控 | 需要手动处理 | 需要精确提取 |
3.8 实战:构建一个通用文档加载器
最后,我分享一个我在项目中实际使用的通用加载器。它能根据文件扩展名自动选择加载器,并统一处理异常。
import os
from langchain_community.document_loaders import (
PyMuPDFLoader,
TextLoader,
UnstructuredWordDocumentLoader,
UnstructuredHTMLLoader
)
def load_document(file_path):
"""通用文档加载器"""
ext = os.path.splitext(file_path)[1].lower()
try:
if ext == '.pdf':
loader = PyMuPDFLoader(file_path)
elif ext == '.docx':
loader = UnstructuredWordDocumentLoader(file_path)
elif ext == '.txt':
# 自动检测编码
import chardet
with open(file_path, 'rb') as f:
encoding = chardet.detect(f.read())['encoding']
loader = TextLoader(file_path, encoding=encoding)
elif ext in ['.html', '.htm']:
loader = UnstructuredHTMLLoader(file_path)
else:
raise ValueError(f"不支持的格式: {ext}")
documents = loader.load()
# 统一添加来源信息
for doc in documents:
doc.metadata['source'] = file_path
doc.metadata['format'] = ext
return documents
except Exception as e:
print(f"加载失败: {file_path}, 错误: {e}")
return []
# 使用示例
docs = load_document("data/产品手册.pdf")
if docs:
print(f"成功加载: {docs[0].metadata['source']}")
print(f"内容长度: {len(docs[0].page_content)}")
好了,文档加载器就讲到这里。下一章咱们聊聊文档分割——把长文档切成适合检索的小块。嗯,那才是真正考验技术的地方。