5.3.3 HTML标题文本分割器
在LangChain中,HTMLHeaderTextSplitter是一个文本分割器,能够在 HTML 文档中根据元素级别进行分割,并为每个相关的标题块添加元数据。这个分割器的目标是保持相关文本的语义分组,并且保留文档结构中编码的丰富上下文信息。HTMLHeaderTextSplitter可以根据提供的标题标签来分割文本,并且可以与其他文本分割器一起使用,作为文本处理管道的一部分。
请看下面的实例,使用 HTMLHeaderTextSplitter 来处理一个 HTML 格式的字符串,并根据其中的标题标签(<h1>、<h2>、<h3> 等)将文档分割成多个部分。每个部分都会保留与其相关的标题元数据。这样做的目的是为了在分割文档的同时,保持相关文本的语义分组,并保留文档结构中编码的丰富上下文信息。
实例5-1:使用HTMLHeaderTextSplitter分割HTML文本(源码路径:codes\5\fen03.py)
实例文件fen03.py的具体实现代码如下所示。
from langchain_text_splitters import HTMLHeaderTextSplitter
html_string = """
<!DOCTYPE html>
<html>
<body>
<div>
<h1>标题1</h1>
<p>关于标题1的一些介绍文本。</p>
<div>
<h2>子标题1</h2>
<p>关于子标题1的一些介绍文本。</p>
<h3>子子标题1</h3>
<p>关于子子标题1的一些文本。</p>
<h3>子子标题2</h3>
<p>关于子子标题2的一些文本。</p>
</div>
<div>
<h2>子标题2</h2>
<p>关于子标题2的一些文本。</p>
</div>
<br>
<p>关于标题1的一些结束文本。</p>
</div>
</body>
</html>
"""
headers_to_split_on = [
("h1", "一级标题"),
("h2", "二级标题"),
("h3", "三级标题"),
]
html_splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
html_header_splits = html_splitter.split_text(html_string)
print(html_header_splits)
上述代码的实现流程如下所示:
(1)定义 HTML 字符串:首先定义了一个包含多个标题级别的 HTML 字符串。这个字符串模拟了一个典型的网页结构,其中包含一级标题、二级标题、三级标题以及段落文本。
(2)设置标题分割规则:定义了一个 headers_to_split_on 列表,分别设置了一级标题(<h1>)、二级标题(<h2>)和三级标题(<h3>)标签作为分割点。
(3)创建分割器实例:使用 HTMLHeaderTextSplitter创建了一个分割器实例,并将 headers_to_split_on 列表传递给它,告诉分割器根据这些标题标签来分割文本。
(4)执行分割操作:调用 split_text 方法传入 HTML 字符串,分割器会根据标题标签将 HTML 内容分割成多个文档对象,并为每个对象添加元数据。
(5)输出结果:打印输出分割后的文档对象列表,每个文档对象包含分割后的文本内容以及与之相关的标题元数据。执行后会输出:
[Document(page_content='标题1'),
Document(page_content='关于标题1的一些介绍文本。 \n子标题1 子子标题1 子子标题2', metadata={'一级标题': '标题1'}),
Document(page_content='关于子标题1的一些介绍文本。', metadata={'一级标题': '标题1', '二级标题': '子标题1'}),
Document(page_content='关于子子标题1的一些文本。', metadata={'一级标题': '标题1', '二级标题': '子标题1', '三级标题': '子子标题1'}),
Document(page_content='关于子子标题2的一些文本。', metadata={'一级标题': '标题1', '二级标题': '子标题1', '三级标题': '子子标题2'}),
Document(page_content='子标题2', metadata={'一级标题': '标题1'}), Document(page_content='关于子标题2的一些文本。', metadata={'一级标题': '标题1', '二级标题': '子标题2'}),
Document(page_content='关于标题1的一些结束文本。', metadata={'一级标题': '标题1'})]
在实际应用中,HTMLHeaderTextSplitter联合RecursiveCharacterTextSplitter可以分割网络中的HTML文件,例如下面的实例演示了这一用法。
实例5-4:分割网络中的HTML文本(源码路径:codes\5\fen04.py)
实例文件fen04.py的具体实现代码如下所示。
from langchain_text_splitters import RecursiveCharacterTextSplitter, HTMLHeaderTextSplitter
url = "https://plato.stanford.edu/entries/goedel/"
headers_to_split_on = [
("h1", "Header 1"),
("h2", "Header 2"),
("h3", "Header 3"),
("h4", "Header 4"),
]
html_splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
html_header_splits = html_splitter.split_text_from_url(url)
chunk_size = 500
chunk_overlap = 30
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size, chunk_overlap=chunk_overlap
)
splits = text_splitter.split_documents(html_header_splits)
print(splits)
上述代码的目的是使用 LangChain 中的文本分割器来处理一个网页(在这个例子中是斯坦福哲学百科中的哥德尔条目页面),将其内容分割成更小的、易于处理的块,并保留文档结构中的上下文信息。执行后会输出分割网页https://plato.stanford.edu/entries/goedel/后的结果。
5.3.4 CharacterTextSplitter文本分割器
在LangChain中,CharacterTextSplitter是一个简单的文本分割器,它基于单个字符进行分割,并根据字符数来衡量文本块的大小。这种方法适用于那些不需要根据语义或结构进行分割的场景,而是需要将长文本分割成较小的部分以便处理。在默认情况下,CharacterTextSplitter 会逐个字符进行分割,块的大小通过字符数量来衡量。请看下面的实例,功能是使用 CharacterTextSplitter 分割文本文件 example1.txt。
实例5-1:使用CharacterTextSplitter分割文本(源码路径:codes\5\fen05.py)
实例文件fen05.py的具体实现代码如下所示。
from langchain_text_splitters import CharacterTextSplitter
# 读取长文本文件
with open("example1.txt") as f:
state_of_the_union = f.read()
# 创建 CharacterTextSplitter 实例
text_splitter = CharacterTextSplitter(
separator="\n\n", # 分隔符,这里使用两个连续的换行符
chunk_size=1000, # 每个块的大小为1000个字符
chunk_overlap=200, # 块之间的重叠为200个字符
length_function=len, # 使用 len 函数来计算字符长度
is_separator_regex=False, # 不将分隔符作为正则表达式处理
)
# 使用分割器创建文档列表
texts = text_splitter.create_documents([state_of_the_union])
print(texts[0]) # 打印第一个分割后的文本块
上述代码的实现流程如下所示:
(1)读取文本文件:使用 with open 语句打开 example1.txt 文件,并将其内容读取到变量 state_of_the_union 中。
(2)创建分割器实例:实例化 CharacterTextSplitter 类,设置以下参数:
- separator="\n\n":使用两个连续的换行符作为文本分割的分隔符。
- chunk_size=1000:每个文本块的大小为1000个字符。
- chunk_overlap=200:相邻文本块之间有200个字符的重叠部分。
- length_function=len:使用Python内置的 len 函数来计算字符长度。
- is_separator_regex=False:指定分隔符不应作为正则表达式处理。
(3)分割文本:调用 create_documents 方法,传入读取的文本内容 state_of_the_union,分割器会根据设置的参数将其分割成多个文本块。
(4)打印输出结果:打印输出分割后的第一个文本块,由于 create_documents 方法返回的是一个文档列表,所以print(texts[0]) 将输出第一个文档的 page_content。
文件 example1.txt的内容如下所示:
Hello, world!
This is the second line.
And this is the third line.
执行后会输出:
page_content='Hello, world!\nThis is the second line.\nAnd this is the third line.'
1. 传递元数据
在处理文本中,有时候不仅需要处理文本本身,而且还需要与文本相关的其他信息,比如文档的标识、作者、时间戳等。传递元数据就是在处理文本时,同时传递一些额外的信息,这些信息可以是任何你想要与文本相关联的数据。请看下面的代码,使用了字典article_metadata_dict将每篇文章的内容和对应的元数据组合在一起。然后,通过迭代字典的键值对可以访问每篇文章的内容以及与之相关的元数据。
# 假设我们有三篇文章
articles = [
"这是第一篇文章的内容。",
"这是第二篇文章的内容。",
"这是第三篇文章的内容。"
]
# 假设每篇文章的元数据
metadata = [
{"author": "张三", "date": "2024-04-12", "topic": "科技"},
{"author": "李四", "date": "2024-04-13", "topic": "政治"},
{"author": "王五", "date": "2024-04-14", "topic": "文学"}
]
# 将文章内容和元数据组合成字典
article_metadata_dict = {
"article1": {"content": articles[0], "metadata": metadata[0]},
"article2": {"content": articles[1], "metadata": metadata[1]},
"article3": {"content": articles[2], "metadata": metadata[2]}
}
# 处理文本并访问元数据
for article_id, data in article_metadata_dict.items():
print("文章ID:", article_id)
print("文章内容:", data["content"])
print("作者:", data["metadata"]["author"])
print("发布日期:", data["metadata"]["date"])
print("主题:", data["metadata"]["topic"])
print("\n")
执行后会输出:
文章ID: article1
文章内容: 这是第一篇文章的内容。
作者: 张三
发布日期: 2024-04-12
主题: 科技
文章ID: article2
文章内容: 这是第二篇文章的内容。
作者: 李四
发布日期: 2024-04-13
主题: 政治
文章ID: article3
文章内容: 这是第三篇文章的内容。
作者: 王五
发布日期: 2024-04-14
主题: 文学
2. 直接分割文本
在处理文本的过程中,有时候只是想简单地将文本分割成块,而不需要关注文档的其他信息。直接分割文本就是在不使用元数据的情况下,直接将文本传递给文本分割器进行分割,例如下面是一个使用CharacterTextSplitter直接分割文本的例子。
实例5-1:使用CharacterTextSplitter直接分割文本(源码路径:codes\5\fen06.py)
实例文件fen06.py的具体实现代码如下所示。
from langchain_text_splitters import CharacterTextSplitter
# 创建 CharacterTextSplitter 实例
text_splitter = CharacterTextSplitter(
separator="\n\n",
chunk_size=1000,
chunk_overlap=200,
length_function=len,
is_separator_regex=False,
)
# 定义要分割的文本
state_of_the_union = "This is a long document we can split up."
# 使用 CharacterTextSplitter 对文本进行分割
texts = text_splitter.create_documents([state_of_the_union])
# 打印分割后的文本
print(texts[0]) # 打印第一个分割后的文本块
在上述代码中,创建了一个CharacterTextSplitter实例,然后使用create_documents方法直接对文本进行分割。 LangChain会根据指定的分割参数将文本切分成块,并将结果存储在texts列表中。执行后会输出:
page_content='This is a long document we can split up.'