核心模块(Modules)
数据连接(DataConnection)
文档加载器(DocumentLoaders)
PDF(Pdf)

PDF

便携式文档格式(PDF) (opens in a new tab),简称 ISO 32000,是 Adobe 于 1992 年开发的文件格式,用于呈现文档,包括文字格式和图像,与应用软件,硬件和操作系统无关。

本篇介绍如何将 PDF 文档加载到我们后续使用的文档格式中。

使用 PyPDF

使用 pypdf 将 PDF 加载到文档数组中,每个文档包含页面内容和具有 page 编号的元数据。

pip install pypdf
from langchain_community.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\n...', metadata={'source': 'example_data/layout-parser-paper.pdf', 'page': 0})

这种方法的一个优势是文档可以带页码检索。

我们想使用 OpenAIEmbeddings,因此需要获取 OpenAI API Key。

import os
import getpass
 
os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key:')
from langchain_community.vectorstores import FAISS
from langchain_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.
...
3: 4 Z. Shen et al.
...

提取图像

使用 rapidocr-onnxruntime 包可以将图像提取为文本:

pip install rapidocr-onnxruntime
loader = PyPDFLoader("https://arxiv.org/pdf/2103.15348.pdf", extract_images=True)
pages = loader.load()
pages[4].page_content
'LayoutParser : A Unified Toolkit for DL-Based DIA 5\nTable 1: Current layout detection models in the LayoutParser model zoo\n...'

使用 PyMuPDF

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

from langchain_community.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\n...', metadata={'file_path': 'example_data/layout-parser-paper.pdf', 'page_number': 1, 'total_pages': 16, 'format': 'PDF 1.5', ...})

此外,你可以将 PyMuPDF 文档 (opens in a new tab) 中的任意选项作为关键字参数传入 load 调用,它会传递给 get_text() 调用。

使用 MathPix

灵感来自 Daniel Gross 的 https://gist.github.com/danielgross/3ab4104e14faccc12b49200843adab21 (opens in a new tab)

from langchain_community.document_loaders import MathpixPDFLoader
loader = MathpixPDFLoader("example_data/layout-parser-paper.pdf")
data = loader.load()

使用 Unstructured

unstructured[all-docs] 包目前支持加载文本文件、powerpoint、html、pdf、图像等。

pip install unstructured[pdf]
from langchain_community.document_loaders import UnstructuredPDFLoader
loader = UnstructuredPDFLoader("example_data/layout-parser-paper.pdf")
data = loader.load()

保留元素

底层,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\n...', metadata={...})

使用 Unstructured 获取远程 PDF

这包括如何将在线 PDF 加载到我们可以在下游使用的文档格式中。这可用于各种在线 PDF 站点,如 https://open.umn.edu/opentextbooks/textbooks/ (opens in a new tab) 和 https://arxiv.org/archive/ (opens in a new tab)

请注意:所有其他 PDF 加载程序也可以用于获取远程 PDF,但 OnlinePDFLoader 是一个遗留函数,专门与 UnstructuredPDFLoader 一起使用。

from langchain_community.document_loaders import OnlinePDFLoader
loader = OnlinePDFLoader("https://arxiv.org/pdf/2302.03803.pdf")
data = loader.load()
print(data)
[Document(page_content='A WEAK ( k, k ) -LEFSCHETZ THEOREM FOR PROJECTIVE TORIC ORBIFOLDS\n...', metadata={'source': '.../online_file.pdf'})]

使用 PyPDFium2

from langchain_community.document_loaders import PyPDFium2Loader
loader = PyPDFium2Loader("example_data/layout-parser-paper.pdf")
data = loader.load()

使用 PDFMiner

PDFMiner 是一个可以帮助从 PDF 文档中提取信息和分析数据的工具。

pip install pdfminer.six
from langchain_community.document_loaders import PDFMinerLoader
loader = PDFMinerLoader("example_data/layout-parser-paper.pdf")
data = loader.load()

使用 PDFMiner 生成 HTML 文本

这对于将文本按语义分块为章节很有帮助,因为输出的 html 内容可以通过 BeautifulSoup 解析,以获取关于字体大小、页码、PDF 页眉/页脚等更结构化和丰富的信息。

from langchain_community.document_loaders import PDFMinerPDFasHTMLLoader
loader = PDFMinerPDFasHTMLLoader("example_data/layout-parser-paper.pdf")
data = loader.load()[0]   # 整个 PDF 作为单个 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 = []   # 首先收集所有具有相同字体大小的片段
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))
# 注意:上面的逻辑非常直接。也可以添加更多策略,例如移除重复的片段(因为 PDF 中的
# 页眉/页脚会出现在多个页面上,所以如果我们发现重复,可以安全地认为它是冗余信息)
from langchain_community.docstore.document import Document
cur_idx = -1
semantic_snippets = []
# 假设:标题的字体大小大于其相应内容的字体大小
for s in snippets:
    # 如果当前片段的字体大小 > 上一节的标题字体大小 => 这是一个新标题
    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 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
 
    # 如果当前片段的字体大小 > 上一节的内容字体大小但小于上一节的标题字体大小,那么也创建
    # 一个新节(例如 PDF 的标题将具有最大的字体大小,但我们不希望它包含所有节)
    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. ...', metadata={'heading': '2 Related Work\n', 'content_font': 9, 'heading_font': 11, 'source': 'example_data/layout-parser-paper.pdf'})

PyPDF 目录

从目录加载 PDF

from langchain_community.document_loaders import PyPDFDirectoryLoader
loader = PyPDFDirectoryLoader("example_data/")
docs = loader.load()

使用 PDFPlumber

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

from langchain_community.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\n...', metadata={'source': 'example_data/layout-parser-paper.pdf', 'file_path': 'example_data/layout-parser-paper.pdf', 'page': 1, 'total_pages': 16, ...})

使用 AmazonTextractPDFParser

AmazonTextractPDFLoader 调用 Amazon Textract 服务 (opens in a new tab) 将 PDF 转换为 Document 结构。该加载器目前进行纯 OCR,更多功能(如布局支持)正在计划中,取决于需求。支持单页和多页文档,最多 3000 页和 512 MB 大小。

为了使调用成功,需要一个 AWS 账户,类似于 AWS CLI (opens in a new tab) 的要求。

除了 AWS 配置外,它与其他 PDF 加载器非常相似,同时还支持 JPEG、PNG 和 TIFF 以及非原生 PDF 格式。

from langchain_community.document_loaders import AmazonTextractPDFLoader
loader = AmazonTextractPDFLoader("example_data/alejandro_rosalez_sample-small.jpeg")
documents = loader.load()

使用 AzureAIDocumentIntelligenceLoader

Azure AI 文档智能 (opens in a new tab)(以前称为 Azure Form Recognizer)是基于机器学习的服务,用于从数字或扫描的 PDF、图像、Office 和 HTML 文件中提取文本(包括手写)、表、文档结构(例如标题、节标题等)和键值对。文档智能支持 PDF、JPEG/JPG、PNG、BMP、TIFF、HEIF、DOCX、XLSX、PPTX 和 HTML。

这个使用 文档智能 的当前实现 (opens in a new tab)可以逐页合并内容并将其转换为 LangChain 文档。默认的输出格式是 markdown,可以轻松地与 MarkdownHeaderTextSplitter 链接以进行语义文档分块。您还可以使用 mode="single" 或 mode="page" 来返回纯文本,一个页面或根据页面拆分的文档。

前提条件

在 3 个预览区域之一拥有 Azure AI 文档智能资源:East US、West US2、West Europe - 如果没有,请参考此文档 (opens in a new tab) 创建一份。您将作为参数传递 <endpoint> 和 <key> 给加载器。

%pip install --upgrade --quiet  langchain langchain-community azure-ai-documentintelligence
 
from langchain_community.document_loaders import AzureAIDocumentIntelligenceLoader
 
file_path = "<filepath>"
endpoint = "<endpoint>"
key = "<key>"
loader = AzureAIDocumentIntelligenceLoader(
    api_endpoint=endpoint, api_key=key, file_path=file_path, api_model="prebuilt-layout"
)
 
documents = loader.load()

使用 UpstageLayoutAnalysisLoader

UpstageLayoutAnalysisLoader 调用 Upstage Layout Analysis API (opens in a new tab) 来检测文档元素,包括来自各种文档格式的表格和图形。该加载器使用纯 OCR 提取文本信息并检测文档中的元素,如 JPEG、PNG、BMP、PDF、TIFF 和 HEIC 文件。对于数字生成的 PDF 文档,用户可以选择放弃 OCR,并通过设置 use_ocr=False 来利用文件中的文本信息,这是默认值。支持单页和多页文档,当 use_ocr=True 时限制为 100 页和 50 MB 文件大小,而 use_ocr=False 时没有限制(仅适用于 PDF 文件)。

前提条件

要访问 Upstage Layout Analysis API,您需要一个 API 访问令牌。请参考提供的快速入门指南 (opens in a new tab) 获取访问令牌并开始使用 Upstage Layout Analysis API。

pip install langchain_upstage

示例

import os
 
os.environ["UPSTAGE_DOCUMENT_AI_API_KEY"] = "YOUR_API_KEY"
 
from langchain_upstage import UpstageLayoutAnalysisLoader
 
file_path = "/PATH/TO/FILE.pdf"
 
loader = UpstageLayoutAnalysisLoader(file_path)
data = loader.load()