当前位置:   article > 正文

LangChain - 文档加载

LangChain - 文档加载


一、关于 检索

许多LLM应用程序需要用户特定数据,这些数据不是模型的训练集的一部分。
完成这一任务的主要方法是通过检索增强生成(RAG)。
在此过程中,检索外部数据,然后在生成步骤中将其传递给LLM。

LangChain为RAG应用程序提供了所有的构建模块 - 从简单到复杂。
本文档部分涵盖了与检索步骤相关的所有内容,例如数据的获取。
虽然听起来很简单,但可能有微妙的复杂性. 这涵盖了几个关键模块。


在这里插入图片描述


1、文档加载器

从许多不同来源加载文档. LangChain提供了100多种不同的文档加载器,并与空间中的其他主要提供商(如AirByte和Unstructured)集成。
我们提供了加载各种类型文档(HTML、PDF、代码)的集成,从各种位置(私人S3存储桶、公共网站)加载。


2、文档转换器

检索的一个关键部分是仅获取文档的相关部分,为了最好地准备文档以进行检索,这涉及几个转换步骤,其中一个主要步骤是 将大型文档分割(或分块)为较小的块
LangChain提供了几种不同的算法来完成此操作,以及针对特定文档类型(代码、markdown等)进行优化的逻辑.


3、文本嵌入模型

检索的另一个关键部分是 为文档创建嵌入。
嵌入捕捉文本的语义含义,使您能够 快速高效地查找其他相似的文本。
LangChain与25多个不同的嵌入提供商和方法进行集成, 从开源到专有API, 使您能够选择最适合您需求的一种。
LangChain提供了标准接口,使您可以轻松切换模型。


4、向量存储

随着嵌入的兴起,出现了对支持这些嵌入的数据库的需求。
LangChain与50多个不同的向量存储进行集成,从开源本地存储到云托管专有存储, 使您能够选择最适合您需求的一种。
LangChain公开了标准接口,使您可以轻松切换向量存储。


5、检索器

一旦数据在数据库中,您仍然需要检索它。
LangChain支持许多不同的检索算法,并且是我们增加最多价值的地方之一。
我们支持 易于入门的基本方法 - 即简单的语义搜索。
但是,我们还添加了一系列算法以提高性能,这些算法包括:

  • 父文档检索器: 允许您为每个父文档 创建 多个嵌入,允许您查找较小的块 但返回较大的上下文。
  • 自查询检索器: 用户的问题通常包含 对不仅仅是语义的东西的引用,而是表达一些 最好用元数据过滤器 表示的逻辑。
    自查询允许您从查询中解析出语义 部分和查询中存在的其他元数据过滤器
  • 集合检索器: 有时您可能希望 从 多个不同的来源 或 使用多个不同的算法 检索文档。
    集合检索器使您可以轻松实现此目的。


二、文档加载器

前往Integrations 以获取与第三方工具 集成的内置文档 加载器 的文档。

使用文档加载器从源加载数据作为Document
Document是一段文本和相关元数据。
例如,有用于加载简单的.txt文件的文档加载器,用于加载任何网页的文本内容,甚至用于加载YouTube视频的转录稿。

文档加载器提供了一个“load”方法,用于从配置的源加载数据作为文档。
它们还可以选择实现“延迟加载”,以便将数据惰性加载到内存中。


入门指南

最简单的加载器将文件作为文本读入,并将其全部放入一个文档中。

from langchain.document_loaders import TextLoader

loader = TextLoader("./index.md")
loader.load()
  • 1
  • 2
  • 3
  • 4

[
    Document(
      page_content='---\nsidebar_position: 0\n---\n# Document loaders\n\nUse document loaders to load data from a source as `Document`\'s. ... They optionally implement:\n\n3. "Lazy load": load documents into memory lazily\n', 
      metadata={'source': '../docs/docs_skeleton/docs/modules/data_connection/document_loaders/index.md'})
]
  • 1
  • 2
  • 3
  • 4
  • 5


三、CSV

逗号分隔值(CSV) 文件是一种使用逗号分隔值的定界文本文件。
文件的每一行是一个数据记录;每个记录由一个或多个字段组成,字段之间用逗号分隔。


1、使用每个文档一行的 CSV 数据加载 CSVLoader

from langchain.document_loaders.csv_loader import CSVLoader

loader = CSVLoader(file_path='./example_data/mlb_teams_2012.csv')
data = loader.load()
  • 1
  • 2
  • 3
  • 4

data

    [Document(page_content='Team: Nationals\n"Payroll (millions)": 81.34\n"Wins": 98', lookup_str='', metadata={'source': './example_data/mlb_teams_2012.csv', 'row': 0}, lookup_index=0), 
    ...
    Document(page_content='Team: Astros\n"Payroll (millions)": 60.65\n"Wins": 55', lookup_str='', metadata={'source': './example_data/mlb_teams_2012.csv', 'row': 29}, lookup_index=0)]
  • 1
  • 2
  • 3

2、自定义 csv 解析和加载 (csv_args

参见 csv 模块 文档,了解支持的 csv 参数的更多信息。

loader = CSVLoader(file_path='./mlb_teams_2012.csv', 
					csv_args={
					    'delimiter': ',',
					    'quotechar': '"',
					    'fieldnames': ['MLB Team', 'Payroll in millions', 'Wins']
					})

data = loader.load()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

data

    [Document(
      page_content='MLB Team: Team\nPayroll in millions: "Payroll (millions)"\nWins: "Wins"', 
      lookup_str='', 
      metadata={'source': './example_data/mlb_teams_2012.csv', 'row': 0}, 
      lookup_index=0), 
    ...
    Document(
    		page_content='MLB Team: Astros\nPayroll in millions: 60.65\nWins: 55', 
    		lookup_str='', 
    		metadata={'source': './example_data/mlb_teams_2012.csv', 'row': 30}, 
    		lookup_index=0)]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3、指定用于 标识文档来源的 列(source_column

使用 source_column 参数指定从每一行创建的文档的来源。
否则,file_path 将作为从 CSV 文件创建的所有文档的来源。

在使用基于来源回答问题的链时,这非常有用。

loader = CSVLoader(file_path='./example_data/mlb_teams_2012.csv',
				   source_column="Team")

data = loader.load()
  • 1
  • 2
  • 3
  • 4

data :

    [Document(page_content='Team: Nationals\n"Payroll (millions)": 81.34\n"Wins": 98', lookup_str='', metadata={'source': 'Nationals', 'row': 0}, lookup_index=0), 
    ...
    Document(page_content='Team: Astros\n"Payroll (millions)": 60.65\n"Wins": 55', lookup_str='', metadata={'source': 'Astros', 'row': 29}, lookup_index=0)]
  • 1
  • 2
  • 3

四、文件目录 file_directory

这里介绍了如何加载目录中的所有文档。

在底层,默认情况下使用 UnstructuredLoader

我们可以使用 glob 参数来控制要加载的文件。

请注意,这里不加载 .rst 文件或 .html文件。


1、加载文件目录数据(DirectoryLoader

from langchain.document_loaders import DirectoryLoader
loader = DirectoryLoader('../', glob="**/*.md")
docs = loader.load()
len(docs) # -> 1
  • 1
  • 2
  • 3
  • 4

2、显示进度条 (tqdm,show_progress

默认情况下,不会显示进度条。
要显示进度条,请安装 tqdm 库(如 pip install tqdm),并将 show_progress 参数设置为 True

loader = DirectoryLoader('../', 
						  glob="**/*.md", 
						  show_progress=True)
docs = loader.load()
  • 1
  • 2
  • 3
  • 4

    Requirement already satisfied: tqdm in /Users/jon/.pyenv/versions/3.9.16/envs/microbiome-app/lib/python3.9/site-packages (4.65.0)

    0it [00:00, ?it/s]
  • 1
  • 2
  • 3

3、使用多线程 (use_multithreading

默认情况下,加载操作在 一个线程 中进行。
为了利用多个线程,将 use_multithreading标志设置为 True。

loader = DirectoryLoader('../', 
						glob="**/*.md", 
						use_multithreading=True)
docs = loader.load() 
  • 1
  • 2
  • 3
  • 4

4、更改加载器类(loader_cls

默认情况下使用 UnstructuredLoader 类。但是,您可以相当容易地 更改加载器的类型。

from langchain.document_loaders import TextLoader

loader = DirectoryLoader('../', 
						  glob="**/*.md", 
						  loader_cls=TextLoader)

docs = loader.load()
len(docs)
# -> 1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

如果需要加载 Python 源代码文件,请使用 PythonLoader

from langchain.document_loaders import PythonLoader
loader = DirectoryLoader('../../../../../', 
						glob="**/*.py", 
						loader_cls=PythonLoader)

docs = loader.load()
len(docs)
# ->  691
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

5、使用 TextLoader 自动检测文件编码

在这个示例中,我们将看到一些在使用 TextLoader 类从目录加载一系列任意文件时可能有用的策略。

首先为了说明问题,让我们尝试加载多个具有任意编码的文本。

path = '../../../../../tests/integration_tests/examples'
loader = DirectoryLoader(path, glob="**/*.txt", loader_cls=TextLoader)
  • 1
  • 2

A. 默认行为
loader.load()
  • 1

<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="color: #800000; text-decoration-color: #800000">╭─────────────────────────────── </span><span style="color: #800000; text-decoration-color: #800000; font-weight: bold">Traceback </span><span style="color: #bf7f7f; text-decoration-color: #bf7f7f; font-weight: bold">(most recent call last)</span><span style="color: #800000; text-decoration-color: #800000"> ────────────────────────────────╮</span>
<span style="color: #800000; text-decoration-color: #800000"></span>  

...
<span style="color: #800000; text-decoration-color: #800000">╰──────────────────────────────────────────────────────────────────────────────────────────────────╯</span>
<span style="color: #ff0000; text-decoration-color: #ff0000; font-weight: bold">RuntimeError: </span>Error loading ..<span style="color: #800080; text-decoration-color: #800080">/../../../../tests/integration_tests/examples/</span><span style="color: #ff00ff; text-decoration-color: #ff00ff">example-non-utf8.txt</span>
</pre>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

文件 example-non-utf8.txt 使用了不同的编码,load() 函数会失败,并显示一条有用的消息指示哪个文件解码失败。

使用 TextLoader 的默认行为,任何一个文档加载失败,整个加载过程都将失败,并且不会加载任何文档。


B. 静默失败 (silent_errors

我们可以将参数 silent_errors 传递给 DirectoryLoader,以跳过 无法加载的文件 并继续加载过程。

loader = DirectoryLoader(path, 
						  glob="**/*.txt", 
						  loader_cls=TextLoader, 
						  silent_errors=True)
docs = loader.load()
  • 1
  • 2
  • 3
  • 4
  • 5

    Error loading ../../../../../tests/integration_tests/examples/example-non-utf8.txt
  • 1

doc_sources = [doc.metadata['source']  for doc in docs]
doc_sources
  • 1
  • 2

    ['../../../../../tests/integration_tests/examples/whatsapp_chat.txt',
     '../../../../../tests/integration_tests/examples/example-utf8.txt']
  • 1
  • 2

C. 自动检测编码 (loader_kwargs, autodetect_encoding

我们也可以 在失败前,使用 TextLoader 来自动检测文件编码。
传递 autodetect_encoding 来加载这个类。

text_loader_kwargs={'autodetect_encoding': True}

loader = DirectoryLoader(path, 
							glob="**/*.txt", 
							loader_cls=TextLoader, 
							loader_kwargs=text_loader_kwargs)
				
docs = loader.load()
doc_sources = [doc.metadata['source']  for doc in docs]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

doc_sources :

    ['../../../../../tests/integration_tests/examples/example-non-utf8.txt',
     '../../../../../tests/integration_tests/examples/whatsapp_chat.txt',
     '../../../../../tests/integration_tests/examples/example-utf8.txt']
  • 1
  • 2
  • 3

五、HTML

超文本标记语言或 HTML 是用于在 Web 浏览器中显示的文档的标准标记语言。


1、加载 html (UnstructuredHTMLLoader

这部分介绍如何将 HTML 文档加载到 我们可以在下游使用的文档格式中。

from langchain.document_loaders import UnstructuredHTMLLoader
loader = UnstructuredHTMLLoader("example_data/fake-content.html")
data = loader.load()
  • 1
  • 2
  • 3

data :

    [Document(page_content='My First Heading\n\nMy first paragraph.', lookup_str='', metadata={'source': 'example_data/fake-content.html'}, lookup_index=0)]
  • 1

2、使用 BeautifulSoup4 加载 HTML (BSHTMLLoader

我们还可以使用 BeautifulSoup4 使用 BSHTMLLoader 加载 HTML 文档。
这将提取 HTML 中的文本到 page_content,并将页面标题作为 metadatatitle

from langchain.document_loaders import BSHTMLLoader
loader = BSHTMLLoader("example_data/fake-content.html")
data = loader.load()
  • 1
  • 2
  • 3

data :

    [Document(page_content='\n\nTest Title\n\n\nMy First Heading\nMy first paragraph.\n\n\n', metadata={'source': 'example_data/fake-content.html', 'title': 'Test Title'})]
  • 1

六、JSON

JSON (JavaScript Object Notation) 是一种开放标准的文件格式和数据交换格式,存储和传输方便,且可读。

JSON 对象由属性 key - 值 value 对和数组(或其他可序列化值)组成的数据对象。


1、使用 json 加载数据


import json
from pathlib import Path
from pprint import pprint

from langchain.document_loaders import JSONLoader

file_path='./facebook_chat.json'
data = json.loads(Path(file_path).read_text())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

data :

    {'image': 
    		{'creation_timestamp': 1675549016, 'uri': 'image_of_the_chat.jpg'},
     'is_still_participant': True,
     'joinable_mode': {'link': '', 'mode': 1},
     'magic_words': [],
     'messages': [{'content': 'Bye!',
                   'sender_name': 'User 2',
                   'timestamp_ms': 1675597571851},
                  ...
                  {'content': 'Hi! Im interested in your bag. Im offering $50. Let me know if you are interested. Thanks!',
                   'sender_name': 'User 1',
                   'timestamp_ms': 1675549022673}],
     'participants': [{'name': 'User 1'}, {'name': 'User 2'}],
     'thread_path': 'inbox/User 1 and User 2 chat',
     'title': 'User 1 and User 2 chat'}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

2、使用 JSONLoader

JSONLoader 使用指定的 jq schema 来解析 JSON 文件。

它使用 jq python 包。 查看这个 手册 来详细了解 jq 语法。

pip install jq
  • 1

假设我们有兴趣提取 JSON 数据中 messages 键下的 content 字段的值。
通过以下示例可以轻松完成这项工作。

loader = JSONLoader(
		    file_path='./facebook_chat.json',
		    jq_schema='.messages[].content')

data = loader.load()
  • 1
  • 2
  • 3
  • 4
  • 5

data :

    [Document(page_content='Bye!', 
    					metadata={'source': '/Users/xx/facebook_chat.json', 
    					'seq_num': 1}),
    ...
     Document(page_content='Hi! Thanks!', 
     					metadata={'source': '/Users/xx/facebook_chat.json', 'seq_num': 11})]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

3、提取元数据 (Extracting metadata)

通常,我们希望将 JSON 文件中的元数据包含到从内容创建的文档中。

下面演示了如何使用 JSONLoader 提取元数据。

需要注意一些关键的更改。在前一个示例中,我们没有收集元数据,在模式中直接指定了 page_content 的值的提取位置。

.messages[].content
  • 1

在当前示例中,我们必须告诉加载器迭代 messages 字段中的记录。
因此,jq_schema 必须是以下形式:

.messages[]
  • 1

这样我们就可以将记录(字典)传递给必须实现的 metadata_func
metadata_func负责确定哪些记录中的信息应该 包含在最终 Document 对象中的元数据中。

此外,现在我们必须通过加载器 显式指定 content_key 参数,用于从记录中提取 page_content 的值的键。

def metadata_func(record: dict, metadata: dict) -> dict:

    metadata["sender_name"] = record.get("sender_name")
    metadata["timestamp_ms"] = record.get("timestamp_ms")

    return metadata

loader = JSONLoader(
    file_path='./example_data/facebook_chat.json',
    jq_schema='.messages[]',
    content_key="content",
    metadata_func=metadata_func
)

data = loader.load()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

data :

    [Document(page_content='Bye!', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 1, 'sender_name': 'User 2', 'timestamp_ms': 1675597571851}),
    ...
     Document(page_content='Hi! Im interested in your bag. Im offering $50. Let me know if you are interested. Thanks!', metadata={'source': '/Users/avsolatorio/WBG/langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 11, 'sender_name': 'User 1', 'timestamp_ms': 1675549022673})]
  • 1
  • 2
  • 3

现在,您将看到文档包含与我们提取的内容相关联的元数据。


4、metadata_func

如上所示,metadata_func 接受 JSONLoader 生成的默认元数据。这允许用户完全控制元数据的格式。

例如,默认元数据包含 sourceseq_num 键。
然而,JSON 数据中可能也包含这些键。
用户可以利用 metadata_func 重命名默认键 并使用 JSON 数据中的键。


下面的示例展示了如何修改 source,只包含与 langchain 目录相关的文件来源信息。

Define the metadata extraction function.
def metadata_func(record: dict, metadata: dict) -> dict:

    metadata["sender_name"] = record.get("sender_name")
    metadata["timestamp_ms"] = record.get("timestamp_ms")

    if "source" in metadata:
        source = metadata["source"].split("/")
        source = source[source.index("langchain"):]
        metadata["source"] = "/".join(source)

    return metadata

loader = JSONLoader(
    file_path='./example_data/facebook_chat.json',
    jq_schema='.messages[]',
    content_key="content",
    metadata_func=metadata_func
)

data = loader.load()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

data :

    [Document(page_content='Bye!', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 1, 'sender_name': 'User 2', 'timestamp_ms': 1675597571851}),
    ...
     Document(page_content='Hi! Im interested in your bag. Im offering $50. Let me know if you are interested. Thanks!', metadata={'source': 'langchain/docs/modules/indexes/document_loaders/examples/example_data/facebook_chat.json', 'seq_num': 11, 'sender_name': 'User 1', 'timestamp_ms': 1675549022673})]
  • 1
  • 2
  • 3

5、jq schema 中常见的 JSON 结构

下面的列表提供了 用户可以根据结构 从 JSON 数据中 提取内容时 使用的可能的 jq_schema 的参考。

JSON        -> [{"text": ...}, {"text": ...}, {"text": ...}]
jq_schema   -> ".[].text"

JSON        -> {"key": [{"text": ...}, {"text": ...}, {"text": ...}]}
jq_schema   -> ".key[].text"

JSON        -> ["...", "...", "..."]
jq_schema   -> ".[]"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

七、Markdown ( UnstructuredMarkdownLoader

Markdown 是一种轻量级标记语言,用于使用纯文本编辑器创建格式化文本。

这部分内容介绍了如何将 Markdown 文档 加载到我们可以在应用程序中 要使用的文档格式中。

! pip install unstructured > /dev/null
  • 1

from langchain.document_loaders import UnstructuredMarkdownLoader
markdown_path = "../../../../../README.md"
loader = UnstructuredMarkdownLoader(markdown_path)
data = loader.load()
  • 1
  • 2
  • 3
  • 4

data :

    [Document(page_content=\x9f¦\x9cï¸\x8fð\x9f\x97 LangChain\n\nâ\x9a¡ Building applications with LLMs through composability â\x9a¡\n\nLooking for the JS/TS version? Check out LangChain.js.\n\nProduction Support: As you move your LangChains into production, we'd love to offer more comprehensive support.
    ...
    Evaluation:\n\n[BETA] Generative models are notoriously hard to evaluate with traditional metrics.  ... For detailed information on how to contribute, see here.", metadata={'source': '../../../../../README.md'})]
  • 1
  • 2
  • 3

保留元素(Retain Elements)

在底层,Unstructured 为不同的文本块创建不同的“元素”。
默认情况下,我们将它们组合在一起,但通过指定 mode="elements" 可以轻松保留该分离。

loader = UnstructuredMarkdownLoader(markdown_path, 
									mode="elements")
data = loader.load()
  • 1
  • 2
  • 3

data[0]

    Document(page_content='ð\x9f¦\x9cï¸\x8fð\x9f”\x97 LangChain', metadata={'source': '../../../../../README.md', 'page_number': 1, 'category': 'Title'})
  • 1

八、PDF

便携式文档格式(PDF),标准化为 ISO 32000,是 Adobe 于 1992 年开发的一种文件格式,用于以与应用软件、硬件和操作系统无关的方式呈现文档,包括文本格式和图像。

这涵盖了如何将 PDF 文档加载到 我们在下游使用的 Document 格式中。


1、使用 PyPDF

使用 pypdf 将 PDF 加载到文档数组中,其中每个文档包含页面内容和元数据,以及 page 号码。

pip install pypdf
  • 1

from langchain.document_loaders import PyPDFLoader

loader = PyPDFLoader("example_data/layout-parser-paper.pdf")
pages = loader.load_and_split()
  • 1
  • 2
  • 3
  • 4

pages[0]

    Document(page_content='LayoutParser : A Uni\x0ced Toolkit for Deep\nLearning Based Document Image Analysis
    ...
    Introduction\nDeep Learning(DL)-based approaches are the state-of-the-art for a wide range of\ndocument image analysis (DIA) tasks including document image classi\x0ccation [ 11,arXiv:2103.15348v2  [cs.CV]  21 Jun 2021', metadata={'source': 'example_data/layout-parser-paper.pdf', 'page': 0})
  • 1
  • 2
  • 3

这种方法的优点是可以通过页面号码检索文档。

我们想使用 OpenAIEmbeddings,所以我们需要获取 OpenAI API 密钥。

import os
import getpass

os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key:')
  • 1
  • 2
  • 3
  • 4

from langchain.vectorstores import FAISS
from langchain.embeddings.openai import OpenAIEmbeddings

faiss_index = FAISS.from_documents(pages, OpenAIEmbeddings())
docs = faiss_index.similarity_search("How will the community be engaged?", k=2)

for doc in docs:
    print(str(doc.metadata["page"]) + ":", doc.page_content[:300])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

    9: 10 Z. Shen et al.
    Fig. 4: Illustration of (a) the original historical Japanese document with layout
    detection results and (b) a recreated version of the document image that achieves
    much better character recognition recall. The reorganization algorithm rearranges
    the tokens based on the their detect
    3: 4 Z. Shen et al.
    Efficient Data AnnotationC u s t o m i z e d  M o d e l  T r a i n i n gModel Cust omizationDI A Model HubDI A Pipeline SharingCommunity PlatformLa y out Detection ModelsDocument Images 
    T h e  C o r e  L a y o u t P a r s e r  L i b r a r yOCR ModuleSt or age & VisualizationLa y ou
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2、使用 MathPix

受 Daniel Gross 的 https://gist.github.com/danielgross/3ab4104e14faccc12b49200843adab21 的启发

from langchain.document_loaders import MathpixPDFLoader 
loader = MathpixPDFLoader("example_data/layout-parser-paper.pdf")
data = loader.load()
  • 1
  • 2
  • 3

3、Using Unstructured

from langchain.document_loaders import UnstructuredPDFLoader
loader = UnstructuredPDFLoader("example_data/layout-parser-paper.pdf")
data = loader.load()
  • 1
  • 2
  • 3

4、保留元素 (Retain Elements)

在内部,Unstructured 为不同的文本块创建不同的 “元素”。默认情况下,我们将它们组合在一起,但您可以通过指定 mode="elements" 轻松保持分离。

loader = UnstructuredPDFLoader("example_data/layout-parser-paper.pdf", mode="elements")
data = loader.load()
  • 1
  • 2

data[0]

    Document(page_content='LayoutParser: A Unified Toolkit for Deep\nLearning Based Document Image Analysis
    ...
    Introduction\nDeep Learning(DL)-based approaches are the state-of-the-art for a wide range of\ndocument image analysis (DIA) tasks including document image classification [11,\narXiv:2103.15348v2  [cs.CV]  21 Jun 2021\n', lookup_str='', metadata={'file_path': 'example_data/layout-parser-paper.pdf', 'page_number': 1, 'total_pages': 16, 'format': 'PDF 1.5', 'title': '', 'author': '', 'subject': '', 'keywords': '', 'creator': 'LaTeX with hyperref', 'producer': 'pdfTeX-1.40.21', 'creationDate': 'D:20210622012710Z', 'modDate': 'D:20210622012710Z', 'trapped': '', 'encryption': None}, lookup_index=0)
  • 1
  • 2
  • 3

5、使用 Unstructured 获取远程 PDF

这涵盖了 如何将在线 PDF 加载到 我们可以在 下游使用的文档格式中。
这可用于各种在线 PDF 网站,如 https://open.umn.edu/opentextbooks/textbooks/https://arxiv.org/archive/

注意:所有其他 PDF 加载器也可用于获取远程 PDF,但 OnlinePDFLoader 是一个旧版本函数,专门与 UnstructuredPDFLoader 配合使用。

from langchain.document_loaders import OnlinePDFLoader
loader = OnlinePDFLoader("https://arxiv.org/pdf/2302.03803.pdf")
data = loader.load()
  • 1
  • 2
  • 3

data

    [Document(page_content='A WEAK ( k, k ) -LEFSCHETZ THEOREM FOR PROJECTIVE TORIC ORBIFOLDS\n\nWilliam D. Montoya\n\nInstituto de Matem´atica, Estat´ıstica e Computa¸c˜ao Cient´ıfica,
    ...
    Cohomology of complete intersections in toric varieties. Pub-', lookup_str='', metadata={'source': '/var/folders/ph/hhm7_zyx4l13k3v8z02dwp1w0000gn/T/tmpgq0ckaja/online_file.pdf'}, lookup_index=0)]
  • 1
  • 2
  • 3

6、使用 PyPDFium2

from langchain.document_loaders import PyPDFium2Loader

loader = PyPDFium2Loader("example_data/layout-parser-paper.pdf")

data = loader.load()
  • 1
  • 2
  • 3
  • 4
  • 5

7、使用 PDFMiner

from langchain.document_loaders import PDFMinerLoader

loader = PDFMinerLoader("example_data/layout-parser-paper.pdf")

data = loader.load()
  • 1
  • 2
  • 3
  • 4
  • 5

使用 PDFMiner 生成 HTML 文本

这对于将文本语义分块到部分中非常有用,因为输出的 HTML 内容可以通过 BeautifulSoup 解析,以获取有关字体大小、页码、PDF 标题/页脚等更结构化和丰富的信息。

from langchain.document_loaders import PDFMinerPDFasHTMLLoader

loader = PDFMinerPDFasHTMLLoader("example_data/layout-parser-paper.pdf")

data = loader.load()[0]   # entire pdf is loaded as a single Document
  • 1
  • 2
  • 3
  • 4
  • 5

from bs4 import BeautifulSoup

soup = BeautifulSoup(data.page_content,'html.parser')

content = soup.find_all('div')
  • 1
  • 2
  • 3
  • 4
  • 5

import re

cur_fs = None
cur_text = ''
snippets = []   # first collect all snippets that have the same font size

for c in content:
    sp = c.find('span')
    if not sp:
        continue
    st = sp.get('style')
    if not st:
        continue
    fs = re.findall('font-size:(\d+)px',st)
    if not fs:
        continue
    fs = int(fs[0])
    if not cur_fs:
        cur_fs = fs
    if fs == cur_fs:
        cur_text += c.text
    else:
        snippets.append((cur_text,cur_fs))
        cur_fs = fs
        cur_text = c.text
        
snippets.append((cur_text,cur_fs))
# Note: The above logic is very straightforward. One can also add more strategies such as removing duplicate snippets (as
# headers/footers in a PDF appear on multiple pages so if we find duplicatess safe to assume that it is redundant info)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

from langchain.docstore.document import Document

cur_idx = -1
semantic_snippets = []

# Assumption: headings have higher font size than their respective content
for s in snippets:
    # if current snippet's font size > previous section's heading => it is a new heading
    if not semantic_snippets or s[1] > semantic_snippets[cur_idx].metadata['heading_font']:
        metadata={'heading':s[0], 'content_font': 0, 'heading_font': s[1]}
        metadata.update(data.metadata)
        semantic_snippets.append(Document(page_content='',metadata=metadata))
        cur_idx += 1
        continue
    
    # if current snippet's font size <= previous section's content => content belongs to the same section (one can also create
    # a tree like structure for sub sections if needed but that may require some more thinking and may be data specific)
    if not semantic_snippets[cur_idx].metadata['content_font'] or s[1] <= semantic_snippets[cur_idx].metadata['content_font']:
        semantic_snippets[cur_idx].page_content += s[0]
        semantic_snippets[cur_idx].metadata['content_font'] = max(s[1], semantic_snippets[cur_idx].metadata['content_font'])
        continue
    
    # if current snippet's font size > previous section's content but less tha previous section's heading than also make a new 
    # section (e.g. title of a pdf will have the highest font size but we don't want it to subsume all sections)
    metadata={'heading':s[0], 'content_font': 0, 'heading_font': s[1]}
    metadata.update(data.metadata)
    semantic_snippets.append(Document(page_content='',metadata=metadata))
    cur_idx += 1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

semantic_snippets[4]

    Document(page_content='Recently, various DL models and datasets have been developed for layout analysis\ntasks. 
    ...
    A spectrum of models\ntrained on these datasets are currently available in the LayoutParser model zoo\nto support different use cases.\n', 
    metadata={'heading': '2 Related Work\n', 'content_font': 9, 'heading_font': 11, 'source': 'example_data/layout-parser-paper.pdf'})
  • 1
  • 2
  • 3
  • 4

8、使用 PyMuPDF

这是 PDF 解析选项中最快的,并且包含有关 PDF 及其页面的详细元数据,以及每个页面返回一个文档。

from langchain.document_loaders import PyMuPDFLoader

loader = PyMuPDFLoader("example_data/layout-parser-paper.pdf")

data = loader.load()
  • 1
  • 2
  • 3
  • 4
  • 5

data[0]

    Document(page_content='LayoutParser: A Unified Toolkit for Deep\nLearning Based Document Image Analysis...
    Open Source library · Toolkit.\n1\nIntroduction\nDeep Learning(DL)-based approaches are the state-of-the-art for a wide range of\ndocument image analysis (DIA) tasks including document image classification [11,\narXiv:2103.15348v2  [cs.CV]  21 Jun 2021\n', lookup_str='', metadata={'file_path': 'example_data/layout-parser-paper.pdf', 'page_number': 1, 'total_pages': 16, 'format': 'PDF 1.5', 'title': '', 'author': '', 'subject': '', 'keywords': '', 'creator': 'LaTeX with hyperref', 'producer': 'pdfTeX-1.40.21', 'creationDate': 'D:20210622012710Z', 'modDate': 'D:20210622012710Z', 'trapped': '', 'encryption': None}, lookup_index=0)
  • 1
  • 2

此外,您可以将 PyMuPDF 文档 中的任何选项作为关键字参数传递给 load 调用,并将其传递给 get_text() 调用。


9、PyPDF 目录

从目录加载 PDF

from langchain.document_loaders import PyPDFDirectoryLoader

loader = PyPDFDirectoryLoader("example_data/")

docs = loader.load()
  • 1
  • 2
  • 3
  • 4
  • 5

10、使用 pdfplumber

与 PyMuPDF 类似,输出的文档包含有关 PDF 及其页面的详细元数据,并且每个页面返回一个文档。

from langchain.document_loaders import PDFPlumberLoader

loader = PDFPlumberLoader("example_data/layout-parser-paper.pdf")

data = loader.load()
  • 1
  • 2
  • 3
  • 4
  • 5

data[0]

    Document(page_content='LayoutParser: A Unified Toolkit for Deep\nLearning Based Document Image Analysis\nZejiang Shen1 ((cid:0)), Ruochen Zhang2, Melissa Dell3, Benjamin Charles Germain\nLee4, Jacob Carlson3, and Weining Li5\n1 Allen Institute for AI
    ...
    of\ndocumentimageanalysis(DIA)tasksincludingdocumentimageclassification[11,', metadata={'source': 'example_data/layout-parser-paper.pdf', 'file_path': 'example_data/layout-parser-paper.pdf', 'page': 1, 'total_pages': 16, 'Author': '', 'CreationDate': 'D:20210622012710Z', 'Creator': 'LaTeX with hyperref', 'Keywords': '', 'ModDate': 'D:20210622012710Z', 'PTEX.Fullbanner': 'This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020) kpathsea version 6.3.2', 'Producer': 'pdfTeX-1.40.21', 'Subject': '', 'Title': '', 'Trapped': 'False'})
  • 1
  • 2
  • 3

2024-04-08(一)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/481781
推荐阅读
相关标签
  

闽ICP备14008679号