文章目录
- 一、关于 检索
- 二、文档加载器
- 入门指南
- 三、CSV
- 1、使用每个文档一行的 CSV 数据加载
- 2、自定义 csv 解析和加载
- 3、指定用于标识文档来源的列
- 四、文件目录 file_directory
- 1、加载文件目录数据
- 2、显示进度条 (tqdm
- 3、使用多线程 use_multithreading
- 4、更改加载器类
- 5、使用 TextLoader 自动检测文件编码
- A. 默认行为
- B. 静默失败
- C. 自动检测编码
- 五、HTML
- 1、加载 html
- 2、使用 BeautifulSoup4 加载 HTML
- 六、JSON
- 1、使用 jq 加载数据
- 2、使用 JSONLoader
- 3、提取元数据 (Extracting metadata)
- 4、`metadata_func`
- 5、jq schema 中常见的 JSON 结构
- 七、Markdown
- 保留元素(Retain Elements)
- 八、PDF
- 1、使用 PyPDF
- 2、使用 MathPix
- 3、Using Unstructured
- 4、保留元素 (Retain Elements)
- 4、使用 Unstructured 获取远程 PDF
- 5、使用 PyPDFium2
- 6、使用 PDFMiner
- 使用 PDFMiner 生成 HTML 文本
- 7、使用 PyMuPDF
- 8、PyPDF 目录
- 9、使用 pdfplumber
一、关于 检索
许多LLM应用程序需要用户特定数据,这些数据不是模型的训练集的一部分。
完成这一任务的主要方法是通过检索增强生成(RAG)。
在此过程中,检索外部数据,然后在生成步骤中将其传递给LLM。
LangChain为RAG应用程序提供了所有的构建模块 - 从简单到复杂。
本文档部分涵盖了与检索步骤相关的所有内容,例如数据的获取。
虽然听起来很简单,但可能有微妙的复杂性. 这涵盖了几个关键模块。
文档加载器
从许多不同来源加载文档. LangChain提供了100多种不同的文档加载器,并与空间中的其他主要提供商(如AirByte和Unstructured)集成。
我们提供了加载各种类型文档(HTML、PDF、代码)的集成,从各种位置(私人S3存储桶、公共网站)加载。
文档转换器
检索的一个关键部分是仅获取文档的相关部分,为了最好地准备文档以进行检索,这涉及几个转换步骤,其中一个主要步骤是 将大型文档分割(或分块)为较小的块。
LangChain提供了几种不同的算法来完成此操作,以及针对特定文档类型(代码、markdown等)进行优化的逻辑.
文本嵌入模型
检索的另一个关键部分是 为文档创建嵌入。
嵌入捕捉文本的语义含义,使您能够 快速高效地查找其他相似的文本。
LangChain与25多个不同的嵌入提供商和方法进行集成, 从开源到专有API, 使您能够选择最适合您需求的一种。
LangChain提供了标准接口,使您可以轻松切换模型。
向量存储
随着嵌入的兴起,出现了对支持这些嵌入的数据库的需求。
LangChain与50多个不同的向量存储进行集成,从开源本地存储到云托管专有存储, 使您能够选择最适合您需求的一种。
LangChain公开了标准接口,使您可以轻松切换向量存储。
检索器
一旦数据在数据库中,您仍然需要检索它。
LangChain支持许多不同的检索算法,并且是我们增加最多价值的地方之一。
我们支持 易于入门的基本方法 - 即简单的语义搜索。
但是,我们还添加了一系列算法以提高性能,这些算法包括:
- 父文档检索器: 允许您为每个父文档 创建 多个嵌入,允许您查找较小的块 但返回较大的上下文。
- 自查询检索器: 用户的问题通常包含 对不仅仅是语义的东西的引用,而是表达一些 最好用元数据过滤器 表示的逻辑。
自查询允许您从查询中解析出语义 部分和查询中存在的其他元数据过滤器。 - 集合检索器: 有时您可能希望 从 多个不同的来源 或 使用多个不同的算法 检索文档。
集合检索器使您可以轻松实现此目的。
二、文档加载器
前往Integrations 以获取与第三方工具 集成的内置文档 加载器 的文档。
使用文档加载器从源加载数据作为Document
。
Document
是一段文本和相关元数据。
例如,有用于加载简单的.txt
文件的文档加载器,用于加载任何网页的文本内容,甚至用于加载YouTube视频的转录稿。
文档加载器提供了一个“load”方法,用于从配置的源加载数据作为文档。
它们还可以选择实现“延迟加载”,以便将数据惰性加载到内存中。
入门指南
最简单的加载器将文件作为文本读入,并将其全部放入一个文档中。
from langchain.document_loaders import TextLoader
loader = TextLoader("./index.md")
loader.load()
[
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'})
]
三、CSV
逗号分隔值(CSV) 文件是一种使用逗号分隔值的定界文本文件。
文件的每一行是一个数据记录;每个记录由一个或多个字段组成,字段之间用逗号分隔。
1、使用每个文档一行的 CSV 数据加载
from langchain.document_loaders.csv_loader import CSVLoader
loader = CSVLoader(file_path='./example_data/mlb_teams_2012.csv')
data = loader.load()
print(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)]
2、自定义 csv 解析和加载
参见 csv 模块 文档,了解支持的 csv 参数的更多信息。
loader = CSVLoader(file_path='./example_data/mlb_teams_2012.csv', csv_args={
'delimiter': ',',
'quotechar': '"',
'fieldnames': ['MLB Team', 'Payroll in millions', 'Wins']
})
data = loader.load()
print(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)]
3、指定用于标识文档来源的列
使用 source_column
参数指定从每一行创建的文档的来源。
否则,file_path
将作为从 CSV 文件创建的所有文档的来源。
在使用基于来源回答问题的链时,这非常有用。
loader = CSVLoader(file_path='./example_data/mlb_teams_2012.csv', source_column="Team")
data = loader.load()
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)]
四、文件目录 file_directory
这里介绍了如何加载目录中的所有文档。
在底层,默认情况下使用 UnstructuredLoader
我们可以使用 glob
参数来控制要加载的文件。
请注意,这里不加载 .rst
文件或 .html
文件。
1、加载文件目录数据
from langchain.document_loaders import DirectoryLoader
loader = DirectoryLoader('../', glob="**/*.md")
docs = loader.load()
len(docs) # -> 1
2、显示进度条 (tqdm
默认情况下,不会显示进度条。
要显示进度条,请安装 tqdm
库(如 pip install tqdm
),并将 show_progress
参数设置为 True
。
loader = DirectoryLoader('../', glob="**/*.md", show_progress=True)
docs = loader.load()
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]
3、使用多线程 use_multithreading
默认情况下,加载操作在 一个线程 中进行。
为了利用多个线程,将 use_multithreading
标志设置为 True。
loader = DirectoryLoader('../', glob="**/*.md", use_multithreading=True)
docs = loader.load()
4、更改加载器类
默认情况下使用 UnstructuredLoader
类。但是,您可以相当容易地 更改加载器的类型。
from langchain.document_loaders import TextLoader
loader = DirectoryLoader('../', glob="**/*.md", loader_cls=TextLoader)
docs = loader.load()
len(docs)
# -> 1
如果需要加载 Python 源代码文件,请使用 PythonLoader
。
from langchain.document_loaders import PythonLoader
loader = DirectoryLoader('../../../../../', glob="**/*.py", loader_cls=PythonLoader)
docs = loader.load()
len(docs)
# -> 691
5、使用 TextLoader 自动检测文件编码
在这个示例中,我们将看到一些在使用 TextLoader
类从目录加载一系列任意文件时可能有用的策略。
首先为了说明问题,让我们尝试加载多个具有任意编码的文本。
path = '../../../../../tests/integration_tests/examples'
loader = DirectoryLoader(path, glob="**/*.txt", loader_cls=TextLoader)
A. 默认行为
loader.load()
<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>
文件 example-non-utf8.txt
使用了不同的编码,load()
函数会失败,并显示一条有用的消息指示哪个文件解码失败。
使用 TextLoader
的默认行为,任何一个文档加载失败,整个加载过程都将失败,并且不会加载任何文档。
B. 静默失败
我们可以将参数 silent_errors
传递给 DirectoryLoader
,以跳过无法加载的文件并继续加载过程。
loader = DirectoryLoader(path, glob="**/*.txt", loader_cls=TextLoader, silent_errors=True)
docs = loader.load()
Error loading ../../../../../tests/integration_tests/examples/example-non-utf8.txt
doc_sources = [doc.metadata['source'] for doc in docs]
doc_sources
['../../../../../tests/integration_tests/examples/whatsapp_chat.txt',
'../../../../../tests/integration_tests/examples/example-utf8.txt']
C. 自动检测编码
我们也可以 在失败前,使用 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]
doc_sources :
['../../../../../tests/integration_tests/examples/example-non-utf8.txt',
'../../../../../tests/integration_tests/examples/whatsapp_chat.txt',
'../../../../../tests/integration_tests/examples/example-utf8.txt']
五、HTML
超文本标记语言或 HTML 是用于在 Web 浏览器中显示的文档的标准标记语言。
1、加载 html
这部分介绍如何将 HTML
文档加载到 我们可以在下游使用的文档格式中。
from langchain.document_loaders import UnstructuredHTMLLoader
loader = UnstructuredHTMLLoader("example_data/fake-content.html")
data = loader.load()
data :
[Document(page_content='My First Heading\n\nMy first paragraph.', lookup_str='', metadata={'source': 'example_data/fake-content.html'}, lookup_index=0)]
2、使用 BeautifulSoup4 加载 HTML
我们还可以使用 BeautifulSoup4
使用 BSHTMLLoader
加载 HTML 文档。
这将提取 HTML 中的文本到 page_content
,并将页面标题作为 metadata
的 title
。
from langchain.document_loaders import BSHTMLLoader
loader = BSHTMLLoader("example_data/fake-content.html")
data = loader.load()
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'})]
六、JSON
JSON (JavaScript Object Notation) 是一种开放标准的文件格式和数据交换格式,存储和传输方便,且可读。
JSON 对象由属性 key - 值 value 对和数组(或其他可序列化值)组成的数据对象。
1、使用 jq 加载数据
JSONLoader
使用指定的 jq schema 来解析 JSON 文件。
它使用 jq
python 包。 查看这个 手册 来详细了解 jq
语法。
pip install jq
import json
from pathlib import Path
from pprint import pprint
from langchain.document_loaders import JSONLoader
file_path='./example_data/facebook_chat.json'
data = json.loads(Path(file_path).read_text())
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'}
2、使用 JSONLoader
假设我们有兴趣提取 JSON 数据中 messages
键下的 content
字段的值。
通过以下示例可以轻松完成这项工作。
loader = JSONLoader(
file_path='./example_data/facebook_chat.json',
jq_schema='.messages[].content')
data = loader.load()
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})]
3、提取元数据 (Extracting metadata)
通常,我们希望将 JSON 文件中的元数据包含到从内容创建的文档中。
下面演示了如何使用 JSONLoader
提取元数据。
需要注意一些关键的更改。在前一个示例中,我们没有收集元数据,在模式中直接指定了 page_content
的值的提取位置。
.messages[].content
在当前示例中,我们必须告诉加载器迭代 messages
字段中的记录。
因此,jq_schema 必须是以下形式:
.messages[]
这样我们就可以将记录(字典)传递给必须实现的 metadata_func
。
metadata_func
负责确定哪些记录中的信息应该包含在最终 Document
对象中的元数据中。
此外,现在我们必须通过加载器显式指定 content_key
参数,用于从记录中提取 page_content
的值的键。
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")
return metadata
loader = JSONLoader(
file_path='./example_data/facebook_chat.json',
jq_schema='.messages[]',
content_key="content",
metadata_func=metadata_func
)
data = loader.load()
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})]
现在,您将看到文档包含与我们提取的内容相关联的元数据。
4、metadata_func
如上所示,metadata_func
接受 JSONLoader
生成的默认元数据。这允许用户完全控制元数据的格式。
例如,默认元数据包含 source
和 seq_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()
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})]
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 -> ".[]"
七、Markdown
Markdown 是一种轻量级标记语言,用于使用纯文本编辑器创建格式化文本。
这部分内容介绍了如何将 Markdown
文档 加载到我们可以在应用程序中 要使用的文档格式中。
! pip install unstructured > /dev/null
from langchain.document_loaders import UnstructuredMarkdownLoader
markdown_path = "../../../../../README.md"
loader = UnstructuredMarkdownLoader(markdown_path)
data = loader.load()
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'})]
保留元素(Retain Elements)
在底层,Unstructured 为不同的文本块创建不同的“元素”。
默认情况下,我们将它们组合在一起,但通过指定 mode="elements"
可以轻松保留该分离。
loader = UnstructuredMarkdownLoader(markdown_path, mode="elements")
data = loader.load()
data[0]
Document(page_content='ð\x9f¦\x9cï¸\x8fð\x9f”\x97 LangChain', metadata={'source': '../../../../../README.md', 'page_number': 1, 'category': 'Title'})
八、PDF
便携式文档格式(PDF),标准化为 ISO 32000,是 Adobe 于 1992 年开发的一种文件格式,用于以与应用软件、硬件和操作系统无关的方式呈现文档,包括文本格式和图像。
这涵盖了如何将 PDF
文档加载到我们在下游使用的 Document 格式中。
1、使用 PyPDF
使用 pypdf
将 PDF 加载到文档数组中,其中每个文档包含页面内容和元数据,以及 page
号码。
pip install pypdf
from langchain.document_loaders import PyPDFLoader
loader = PyPDFLoader("example_data/layout-parser-paper.pdf")
pages = loader.load_and_split()
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})
这种方法的优点是可以通过页面号码检索文档。
我们想使用 OpenAIEmbeddings
,所以我们需要获取 OpenAI API 密钥。
import os
import getpass
os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key:')
OpenAI API Key: ········
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])
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
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()
3、Using Unstructured
from langchain.document_loaders import UnstructuredPDFLoader
loader = UnstructuredPDFLoader("example_data/layout-parser-paper.pdf")
data = loader.load()
4、保留元素 (Retain Elements)
在内部,Unstructured 为不同的文本块创建不同的 “元素”。默认情况下,我们将它们组合在一起,但您可以通过指定 mode="elements"
轻松保持分离。
loader = UnstructuredPDFLoader("example_data/layout-parser-paper.pdf", mode="elements")
data = loader.load()
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)
4、使用 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()
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)]
5、使用 PyPDFium2
from langchain.document_loaders import PyPDFium2Loader
loader = PyPDFium2Loader("example_data/layout-parser-paper.pdf")
data = loader.load()
6、使用 PDFMiner
from langchain.document_loaders import PDFMinerLoader
loader = PDFMinerLoader("example_data/layout-parser-paper.pdf")
data = loader.load()
使用 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
from bs4 import BeautifulSoup
soup = BeautifulSoup(data.page_content,'html.parser')
content = soup.find_all('div')
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)
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
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'})
7、使用 PyMuPDF
这是 PDF 解析选项中最快的,并且包含有关 PDF 及其页面的详细元数据,以及每个页面返回一个文档。
from langchain.document_loaders import PyMuPDFLoader
loader = PyMuPDFLoader("example_data/layout-parser-paper.pdf")
data = loader.load()
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)
此外,您可以将 PyMuPDF 文档 中的任何选项作为关键字参数传递给 load
调用,并将其传递给 get_text()
调用。
8、PyPDF 目录
从目录加载 PDF
from langchain.document_loaders import PyPDFDirectoryLoader
loader = PyPDFDirectoryLoader("example_data/")
docs = loader.load()
9、使用 pdfplumber
与 PyMuPDF 类似,输出的文档包含有关 PDF 及其页面的详细元数据,并且每个页面返回一个文档。
from langchain.document_loaders import PDFPlumberLoader
loader = PDFPlumberLoader("example_data/layout-parser-paper.pdf")
data = loader.load()
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'})
2024-04-08(一)