LangChain实战:Document Loaders全解析——多格式文件加载与文档分割指南

3 阅读12分钟

在LangChain构建LLM应用的流程中,数据输入是一切的基础。无论是RAG检索增强生成,还是文档问答、文本分析,第一步都需要将不同来源、不同格式的数据,转化为LangChain可统一处理的标准格式。而Document Loaders(文档加载器),正是承担这一核心职责的组件,它像一座“桥梁”,连接起原始数据与LangChain生态,让多格式数据的处理变得高效、统一。

本文将从Document Loaders的核心设计出发,详解其统一接口规范,手把手教你使用常用加载器处理CSV、JSON、PDF、TXT等常见文件,再结合文档分割器解决大文件处理难题,所有代码均为实战原创,可直接复制运行,帮你快速掌握数据输入的核心技巧。

一、核心基础:Document Loaders的设计逻辑

LangChain的Document Loaders之所以能适配多种文件格式,核心在于其统一的接口设计——所有加载器(无论内置还是自定义)都必须实现BaseLoader抽象接口,这保证了无论处理哪种数据,都能遵循相同的调用逻辑,降低开发成本。

1. 统一载体:Document类

所有文档加载器最终返回的结果,都是Document类的实例。这个类是LangChain中所有文档的统一载体,核心包含两个属性,足以满足绝大多数开发场景的需求:

  • page_content:存储文档的核心文本内容,是后续LLM处理的主要对象;
  • metadata:存储文档的元数据,以字典形式存在,可自定义添加信息(如文件来源、创建时间、页码等),方便后续检索和管理。

下面是一个贴合实际开发的Document实例创建示例,模拟加载一篇技术文档的片段:

from langchain_core.documents import Document

# 模拟加载一篇LangChain相关的技术文档片段
doc = Document(
    page_content="LangChain的Document Loaders组件提供了统一接口,支持加载CSV、PDF、JSON等多种格式文件,"
                 "所有加载器均继承自BaseLoader,确保数据处理的一致性。",
    metadata={
        "source": "./langchain_doc.md",
        "create_time": "2026-04-10",
        "file_type": "markdown",
        "author": "LangChain实战者"
    }
)

# 打印文档内容和元数据
print("文档内容:", doc.page_content)
print("文档元数据:", doc.metadata)

2. 统一接口:load()与lazy_load()

无论哪种文档加载器,都实现了两个核心方法,这是统一调用逻辑的关键,也是应对不同数据量的核心技巧:

  • load():一次性加载全部文档,返回一个包含多个Document实例的列表。适用于小文件场景,调用简单,但如果文件过大,可能会出现内存溢出问题;
  • lazy_load():延迟流式加载文档,返回一个生成器对象。每次只加载一个Document实例,可通过for循环依次获取,完美解决大文件加载的内存压力,适合大型数据集场景。

这两个方法的使用逻辑非常简单,后续所有加载器的调用都将遵循这一规范,无需额外学习新的调用方式。

二、常用文档加载器实战:覆盖4种高频文件格式

LangChain内置了上百种文档加载器,覆盖网页、云存储、社交平台、本地文件等多种场景。其中,本地文件加载是最常用的场景,下面重点讲解4种高频本地文件加载器的实操技巧,代码均为原创,贴合实际开发需求。

1. CSVLoader:CSV文件加载神器

CSV是数据处理中最常用的格式之一,CSVLoader专门用于加载CSV文件,支持自定义分隔符、引号包裹符,以及无表头文件的字段名指定,灵活性极高。

实战场景:加载员工信息CSV文件,分别处理有表头和无表头两种情况,提取员工核心信息并封装为Document对象。

from langchain_community.document_loaders.csv_loader import CSVLoader

# 示例1:加载有表头的员工信息CSV(文件内容:name,department,salary,hire_date)
# 假设文件路径为./employees.csv,内容包含3条员工数据
loader_with_header = CSVLoader(
    file_path="./employees.csv",
    encoding="utf-8",  # 指定编码,避免中文乱码
    csv_args={
        "delimiter": ",",  # 指定CSV分隔符,默认是逗号
        "quotechar": '"'   # 指定字符串包裹符,默认是双引号
    }
)
# 一次性加载全部文档
docs_with_header = loader_with_header.load()
print("有表头CSV加载结果(共{}条):".format(len(docs_with_header)))
for i, doc in enumerate(docs_with_header, 1):
    print(f"第{i}条员工信息:")
    print("内容:", doc.page_content)
    print("元数据:", doc.metadata)
    print("-" * 60)

# 示例2:加载无表头的员工信息CSV,手动指定字段名
loader_no_header = CSVLoader(
    file_path="./employees_no_header.csv",
    encoding="utf-8",
    csv_args={
        "delimiter": ",",
        "quotechar": '"',
        "fieldnames": ["name", "department", "salary", "hire_date"]  # 手动指定字段名
    }
)
docs_no_header = loader_no_header.load()
print("\n无表头CSV加载结果(共{}条):".format(len(docs_no_header)))
for doc in docs_no_header:
    print("内容:", doc.page_content)

关键注意点:无表头文件必须通过csv_argsfieldnames参数指定字段名,否则会将第一行数据当作字段名,导致数据读取错误;如果CSV文件使用特殊分隔符(如分号、制表符),需在csv_args中指定delimiter

2. JSONLoader:JSON数据精准提取

JSON是前后端数据交互、配置文件的常用格式,JSONLoader可将JSON文件中的数据精准提取出来,封装为Document对象。与CSVLoader不同,JSONLoader依赖jq工具(一款跨平台JSON解析工具),使用前需先安装:pip install jq

JSONLoader的核心是jq_schema参数,通过简单的jq语法,可精准提取JSON中的任意字段、数组、嵌套对象,满足复杂JSON数据的提取需求。

实战场景:加载一个包含产品信息的JSON文件(包含嵌套结构和数组),分别提取产品列表、单个产品详情、嵌套的供应商信息。

from langchain_community.document_loaders import JSONLoader

# 假设JSON文件(./products.json)内容如下:
# {
#   "product_list": [
#     {"id": 1001, "name": "LangChain实战教程", "price": 99, "supplier": {"name": "技术出版社", "addr": "北京"}},
#     {"id": 1002, "name": "LLM应用开发指南", "price": 129, "supplier": {"name": "编程出版社", "addr": "上海"}}
#   ],
#   "update_time": "2026-04-10"
# }

# 示例1:提取所有产品信息(数组中的每个产品)
loader1 = JSONLoader(
    file_path="./products.json",
    jq_schema=".product_list[]",  # jq语法:提取product_list数组中的每个元素
    text_content=False,  # 不将结果转为字符串,保留原始字典格式
    encoding="utf-8"
)
docs1 = loader1.load()
print("所有产品信息:")
for doc in docs1:
    print(doc.page_content)

# 示例2:提取所有产品名称(数组中每个产品的name字段)
loader2 = JSONLoader(
    file_path="./products.json",
    jq_schema=".product_list[].name",  # jq语法:提取每个产品的name字段
    text_content=True,  # 将结果转为字符串
    encoding="utf-8"
)
docs2 = loader2.load()
print("\n所有产品名称:")
for doc in docs2:
    print(doc.page_content)

# 示例3:提取第一个产品的供应商地址(嵌套对象提取)
loader3 = JSONLoader(
    file_path="./products.json",
    jq_schema=".product_list[0].supplier.addr",  # jq语法:提取嵌套的addr字段
    text_content=True,
    encoding="utf-8"
)
docs3 = loader3.load()
print("\n第一个产品的供应商地址:", docs3[0].page_content)

# 示例4:加载JsonLines文件(每一行是一个独立JSON对象)
# 假设文件./product_lines.jsonl内容:
# {"id": 1003, "name": "Python基础教程", "price": 69}
# {"id": 1004, "name": "MySQL实战", "price": 89}
loader4 = JSONLoader(
    file_path="./product_lines.jsonl",
    jq_schema=".name",
    json_lines=True,  # 开启JsonLines模式,每一行解析为一个Document
    encoding="utf-8"
)
docs4 = loader4.load()
print("\nJsonLines文件中的产品名称:")
for doc in docs4:
    print(doc.page_content)

常用jq语法总结:.表示JSON根对象,[]表示数组,.key提取指定字段,[]"提取数组中所有元素,.key1.key2提取嵌套字段,掌握这几个基础语法,就能应对绝大多数JSON提取场景。

3. PyPDFLoader:PDF文件高效加载

PDF是文档存储的常用格式,LangChain支持多种PDF加载器,其中PyPDFLoader是最常用、最轻便的一款,依赖pypdf库,使用前需安装:pip install pypdf

PyPDFLoader支持按页面加载(默认)和合并加载两种模式,还能处理带密码的PDF文件,满足不同PDF处理需求。

from langchain_community.document_loaders import PyPDFLoader

# 示例1:加载无密码PDF,按页面分割(每一页一个Document对象)
# 假设PDF文件./langchain_tutorial.pdf有5页内容
loader1 = PyPDFLoader(
    file_path="./langchain_tutorial.pdf",
    mode="page",  # 按页面分割,默认模式
    encoding="utf-8"
)
docs1 = loader1.load()
print("按页面加载PDF,共{}页({}个Document对象)".format(len(docs1), len(docs1)))
for i, doc in enumerate(docs1, 1):
    print(f"第{i}页内容(前100字符):", doc.page_content[:100] + "...")

# 示例2:加载无密码PDF,合并为单个Document对象
loader2 = PyPDFLoader(
    file_path="./langchain_tutorial.pdf",
    mode="single",  # 合并所有页面为一个Document
    encoding="utf-8"
)
docs2 = loader2.load()
print("\n合并页面加载PDF,共{}个Document对象".format(len(docs2)))
print("PDF完整内容(前200字符):", docs2[0].page_content[:200] + "...")

# 示例3:加载带密码的PDF文件
loader3 = PyPDFLoader(
    file_path="./encrypted_pdf.pdf",
    mode="page",
    password="123456",  # 指定PDF密码
    encoding="utf-8"
)
docs3 = loader3.load()
print("\n带密码PDF加载结果,共{}页".format(len(docs3)))

关键注意点:如果PDF文件有密码,必须通过password参数指定,否则会加载失败;mode="page"适合需要按页面检索的场景(如RAG),mode="single"适合需要完整文档内容的场景。

4. TextLoader:TXT文本文件加载

TextLoader是最基础、最简单的文档加载器,专门用于加载TXT文本文件,将整个文本文件的内容封装为一个Document对象,适合加载小型文本文件(如配置文件、日志片段等)。

from langchain_community.document_loaders import TextLoader

# 加载一个TXT文本文件(./notes.txt),内容为开发笔记
loader = TextLoader(
    file_path="./notes.txt",
    encoding="utf-8",  # 必须指定编码,避免中文乱码
)
docs = loader.load()
print("TextLoader加载结果,共{}个Document对象".format(len(docs)))
print("文档内容:", docs[0].page_content)
print("文档元数据:", docs[0].metadata)

注意:TextLoader加载的文档始终只有一个Document对象,无论文本内容多长。如果文本文件过大(如几万字),直接加载会导致后续LLM处理时token溢出,此时就需要用到文档分割器。

三、大文件处理:RecursiveCharacterTextSplitter文档分割器

当加载的文档过大(如长文本、厚PDF),将其作为一个Document对象处理会面临两个问题:一是token数量超标,导致LLM调用失败;二是上下文过于冗长,影响模型理解核心信息。此时,就需要使用文档分割器,将大文档分割为多个小的Document对象,平衡上下文完整性和token成本。

LangChain官方推荐的默认分割器是RecursiveCharacterTextSplitter(递归字符文本分割器),它能根据文本的自然段落进行分割,在保持上下文连贯的同时,精准控制每个分段的大小,开箱即用效果极佳。

实战:分割大文本文件

场景:加载一个大型Python教程TXT文件(约5000字符),使用RecursiveCharacterTextSplitter分割为多个小分段,控制每个分段最大300字符,重叠50字符(保证上下文连贯)。

from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter

# 1. 加载大文本文件
loader = TextLoader(
    file_path="./python_tutorial.txt",
    encoding="utf-8"
)
docs = loader.load()
print("原始文档长度:{}字符".format(len(docs[0].page_content)))
print("原始Document数量:{}个".format(len(docs)))

# 2. 初始化文档分割器
splitter = RecursiveCharacterTextSplitter(
    chunk_size=300,  # 每个分段的最大字符数
    chunk_overlap=50,  # 分段之间的重叠字符数,保证上下文连贯
    # 分割依据:优先按段落(\n\n)分割,其次按换行(\n),再按中文标点,最后按英文标点
    separators=["\n\n", "\n", "。", "!", "?", ".", "!", "?", " ", ""],
    length_function=len  # 字符统计函数,默认是len(按字符数统计)
)

# 3. 分割文档
split_docs = splitter.split_documents(docs)
print("\n分割后Document数量:{}个".format(len(split_docs)))

# 4. 打印分割结果
for i, doc in enumerate(split_docs, 1):
    print(f"\n第{i}个分段({len(doc.page_content)}字符):")
    print(doc.page_content)

分割器核心参数解析

  • chunk_size:每个分段的最大字符数,根据LLM的token限制设置(如GPT-3.5-turbo的上下文token限制为4096,可设置chunk_size为300-500);
  • chunk_overlap:分段之间的重叠字符数,设置为10%-20%的chunk_size即可,保证相邻分段的上下文连贯,避免分割导致的信息断裂;
  • separators:分割依据,按优先级排序,优先按自然段落分割(\n\n),这样能最大程度保留文本的逻辑结构;
  • length_function:字符统计函数,默认是len,也可自定义(如按token数统计)。

四、总结:Document Loaders使用最佳实践

Document Loaders是LangChain数据输入的核心组件,掌握其使用技巧,能让你在处理多格式数据时事半功倍。结合实战经验,总结以下最佳实践:

  1. 统一接口优先:所有加载器都遵循load()lazy_load()接口,小文件用load(),大文件用lazy_load(),避免内存溢出;
  2. 加载器选型:CSV用CSVLoader,JSON用JSONLoader(需安装jq),PDF用PyPDFLoader(需安装pypdf),TXT用TextLoader,按需选择,避免过度复杂;
  3. 大文件必分割:超过1000字符的文档,一定要用RecursiveCharacterTextSplitter分割,平衡token成本和上下文完整性;
  4. 元数据规范:加载文档时,尽量完善metadata信息(如来源、创建时间),方便后续检索和管理;
  5. 编码与异常:加载中文文件时,务必指定encoding="utf-8",避免乱码;处理带密码的PDF、无表头的CSV时,注意参数配置,避免加载失败。

除了本文介绍的4种加载器,LangChain还内置了网页、Excel、Word、云存储等多种场景的加载器,更多详细用法可参考LangChain官方文档

掌握Document Loaders的使用,就能轻松搞定LangChain应用的数据输入环节,为后续的RAG、文档问答等功能打下坚实基础。动手实践起来,你会发现多格式数据的处理原来可以如此简单!