LlamaIndex NotionPageReader 完全指南:从 Notion 页面与数据库加载文档的实践与源码解析
【免费下载链接】llama_indexLlamaIndex is the leading document agent and OCR platform项目地址: https://gitcode.com/GitHub_Trending/ll/llama_index
Notion 是许多团队沉淀文档、数据库与知识库的核心工具,而把这些内容接入 RAG(检索增强生成)流水线,第一步就是可靠地读取数据。本文围绕当前仓库中 Notion 读取器 API 参考文档 指向的核心类NotionPageReader,结合 Notion 读取器集成包 的完整源码,系统讲解如何安装、鉴权、按页面 ID / 数据库 / 全文搜索三种方式加载 Notion 数据,并深入解析其底层的块递归读取、游标分页与限流重试实现。读完本文,你将能够用几行代码把 Notion 工作区中的页面与数据库内容转换为 LlamaIndex 的Document,直接用于索引构建与下游检索。
一、定位:NotionPageReader 是什么
NotionPageReader是 LlamaIndex 生态中面向 Notion 的数据连接器(Reader),定义于 llama-index-readers-notion 集成包。从源码可见,它继承自llama_index.core.readers.base.BasePydanticReader(base.py),而BasePydanticReader又继承自BaseReader与BaseComponent,因此它天然具备 LlamaIndex Reader 的统一接口(load_data返回List[Document])、Pydantic 字段校验能力,并且is_remote = True,明确标识这是一个远程数据源读取器。
集成包的 测试用例 直接验证了这一继承关系:通过NotionPageReader.__mro__断言BaseReader存在于其方法解析顺序中。这意味着所有以BaseReader为抽象接口的 LlamaIndex 上层组件(索引、节点解析器、IngestionPipeline 等)都可以直接消费该 Reader 的输出。
在包管理层面,pyproject.toml 显示该包版本为0.5.0,包名为llama-index-readers-notion,导入路径为llama_index.readers.notion,依赖llama-index-core>=0.13.0,<0.15,要求 Python>=3.10,<4.0。
二、安装
通过 pip 直接安装该集成包即可:
pip install llama-index-readers-notion安装完成后,包入口 会暴露NotionPageReader类,可直接从llama_index.readers.notion导入。
三、初始化与鉴权:令牌与请求头
NotionPageReader的构造函数签名非常简单:
NotionPageReader(integration_token: Optional[str] = None)其鉴权逻辑在 base.py 的__init__方法 中有三个关键行为:
- 令牌来源优先级:优先使用显式传入的
integration_token;若为None,则读取环境变量NOTION_INTEGRATION_TOKEN;两者皆无时抛出ValueError,提示"必须指定integration_token或设置环境变量NOTION_INTEGRATION_TOKEN"。 - 自动构造请求头:内部会生成三组 HTTP 头——
Authorization: Bearer <token>(Bearer 令牌认证);Content-Type: application/json;Notion-Version: 2022-06-28(固定使用该版本的 Notion API)。
- Pydantic 字段存储:
token与headers以字段形式写入实例,便于后续所有请求复用。
使用示例:
from llama_index.readers.notion import NotionPageReader # 方式一:显式传入 Integration Token reader = NotionPageReader(integration_token="<Integration Token>") # 方式二:依赖环境变量(推荐,避免令牌写入代码) import os os.environ["NOTION_INTEGRATION_TOKEN"] = "secret_xxx" reader = NotionPageReader()注意:Notion 的 Integration Token 需在 Notion 集成设置中创建,并确保该集成已被授权访问目标页面或数据库(在 Notion 页面右上角 Connect 关联到对应集成),否则 API 会返回 401/403。
四、核心 API 全景:三种加载数据的方式
NotionPageReader对外暴露了 8 个主要方法。其中最高层、最常用的是load_data,它内部组合了list_databases、list_pages、query_database与read_page完成端到端加载。
4.1load_data:统一入口
def load_data( self, page_ids: List[str] = [], database_ids: Optional[List[str]] = None, load_all_if_empty: bool = False, ) -> List[Document]参数语义(来自 load_data 实现):
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
page_ids | List[str] | [] | 要加载的 Notion 页面 ID 列表 |
database_ids | Optional[List[str]] | None | 数据库 ID 列表,会先查询出库内所有页面 ID 再加载 |
load_all_if_empty | bool | False | 当page_ids与database_ids均为空时,是否加载工作区内全部页面与数据库 |
执行流程如下:
- 空参数校验:若
page_ids和database_ids均为空且load_all_if_empty=False,抛出ValueError,避免无目标地全量拉取; - 全量模式:若
load_all_if_empty=True,则调用list_databases()与list_pages()兜底填充; - 数据库展开:遍历每个
database_id,调用query_database(database_id)取回库内所有页面 ID,并入统一的page_ids集合(使用set去重); - 逐页读取:对每个页面 ID 调用
read_page(page_id)获取纯文本,包装为Document返回。
每个Document的构造也值得注意(base.py 第 232-234 行):
Document(text=page_text, id_=page_id, extra_info={"page_id": page_id})text:页面块内容渲染出的纯文本;id_:直接复用 Notion 的页面 ID,保证 Document 与来源页面一一对应,便于去重与溯源;extra_info:携带page_id元数据,供下游节点解析器与检索器使用。
4.2read_page:读取单个页面
def read_page(self, page_id: str) -> str内部直接调用私有方法_read_block(page_id)返回页面渲染文本。它是"块读取"的入口。
4.3query_database:从数据库拉取页面 ID
def query_database( self, database_id: str, query_dict: Dict[str, Any] = {"page_size": 100} ) -> List[str]向 Notion 的/v1/databases/{database_id}/query端点发起 POST 请求,默认每页取 100 条结果,返回该数据库下所有页面 ID 列表(注意:返回的是 ID 而非内容,真正的内容仍需通过read_page读取)。
该方法实现了完整的游标分页逻辑:首次请求后检查响应中的has_more字段,若为True,则将next_cursor写入query_dict["start_cursor"]继续请求,直到取完所有数据。用户也可通过query_dict传入 Notion API 支持的过滤(filter)、排序(sorts)等高级查询参数。
4.4search/list_pages/list_databases:工作区发现能力
三者均调用 Notion 的/v1/search端点(POST):
search(query):按文本关键词搜索页面,返回匹配的页面 ID 列表,同样支持start_cursor游标分页;list_databases():以{"filter": {"property": "object", "value": "database"}}过滤出工作区内全部数据库 ID;list_pages():以{"filter": {"property": "object", "value": "page"}}过滤出工作区内全部页面 ID。
这三者合起来,构成了"先发现、再加载"的能力:你可以先用search找到目标页面,再交给load_data批量加载。
五、底层原理:块的递归读取与分页遍历
5.1_read_block:递归渲染 Notion 块结构
Notion 的页面本质是"块(Block)"的嵌套树。_read_block(base.py 第 57-101 行)实现了这套结构的递归遍历:
- 请求
GET /v1/blocks/{block_id}/children获取当前块的子块列表; - 对每个子块,取出
result["type"]对应的内容对象(如段落 paragraph、标题 heading 等); - 若该内容对象含
rich_text字段,则逐个拼接富文本中的text.content,并用\t制表符按num_tabs缩进层级; - 若该块
has_children为True,递归进入其子块,缩进层级num_tabs + 1; - 同一层级的内容以
\n连接,最终返回整棵块树的纯文本。
分页处理:块子列表同样可能超过单次返回上限,代码以data["next_cursor"]是否为None作为终止条件,否则更新cur_block_id继续遍历,确保大页面也能被完整读取。
5.2_request_with_retry:限流重试与指数退避
由于 Notion API 有严格的速率限制,_request_with_retry内置了一套稳健的重试机制,所有 API 请求都经由它发出:
- 最大重试次数:5 次;
- 指数退避:
time.sleep(backoff_factor * (2 ** attempt) + retry_after),即退避时间随重试次数指数增长; - 429 限流响应:读取响应头
Retry-After作为附加等待时间(默认 1 秒),避免限流期内的无效重试; - 其他 HTTP 错误:直接抛出携带响应体的
HTTPError;网络层面的RequestException也会被包装后抛出; - 超过 5 次仍失败:抛出
"Maximum retries exceeded"。
这一实现保证了批量加载大量页面时,面对 429 限流能够自动降速恢复,而不是立即失败,是实际生产中保证稳定性的关键一环。
六、完整实战示例
结合 集成包 README 的用法 与源码能力,给出三种可运行的完整场景:
from llama_index.readers.notion import NotionPageReader from llama_index.core import VectorStoreIndex # 1. 初始化(推荐从环境变量读取令牌) reader = NotionPageReader(integration_token="<Integration Token>") # 2a. 按页面 ID 加载 documents = reader.load_data( page_ids=["<Page ID 1>", "<Page ID 2>"], ) # 2b. 从数据库加载(自动展开库内所有页面) documents = reader.load_data( database_ids=["<Database ID>"], ) # 2c. 先用关键词搜索定位页面,再加载 page_ids = reader.search("What I") # 返回匹配页面 ID 列表 documents = reader.load_data(page_ids=page_ids) # 2d. 全量加载工作区所有页面与数据库 all_docs = reader.load_data(load_all_if_empty=True) # 3. 直接构建索引,进入检索/问答流程 index = VectorStoreIndex.from_documents(documents) query_engine = index.as_query_engine() response = query_engine.query("根据这些 Notion 文档总结核心要点") print(response)辅助方法也可独立使用,便于先探测再加载:
# 列出工作区所有数据库与页面 databases = reader.list_databases() pages = reader.list_pages() # 自定义数据库查询(如按属性过滤) custom_docs = reader.load_data( database_ids=["<Database ID>"], ) # 或直接调用 query_database 拿到 ID 后自行处理 db_page_ids = reader.query_database("<Database ID>", {"page_size": 50})七、注意事项与已知限制
基于源码结构与注释,以下几点需要在使用中留意:
- 数据库读取是"平铺"的:
query_database只返回页面 ID,数据库内容仍逐页通过read_page渲染,属于页级别粒度,而非数据库表结构(数据库属性、公式等结构化字段不会出现在Document.text中)。 - 纯文本输出:读取结果仅保留富文本内容与层级缩进,Notion 中的图片、文件、代码块语言、表格结构等样式信息不会保留。
- 待办与演进:源码中留有
# TODO: Notion DB reader coming soon!与# TODO: in the future add special logic for database_ids注释,说明数据库专项逻辑仍在演进中,后续版本可能增强数据库维度的读取能力。 - 令牌安全:
integration_token会作为token字段保存在实例中,生产环境建议通过环境变量注入,避免硬编码进代码仓库。 - API 版本固定:请求头固定使用
Notion-Version: 2022-06-28,Notion API 版本更新后,该 Reader 需要相应升级才能使用新端点特性。
八、结语
NotionPageReader通过"搜索/列表发现 → 数据库展开 → 块递归渲染 → Document 包装"的清晰链路,将 Notion 的页面与数据库内容无缝接入 LlamaIndex 的文档生态。其内部的游标分页、递归块遍历与 429 指数退避重试,正是面向真实 Notion API 场景的工程化打磨。读者既可以直接用文中的代码示例快速上手,也可以深入 base.py 理解每一个请求与分页细节,为定制化扩展(如保留数据库属性、自定义过滤器)打下基础。
【免费下载链接】llama_indexLlamaIndex is the leading document agent and OCR platform项目地址: https://gitcode.com/GitHub_Trending/ll/llama_index
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考