API 分页:那个让无数自动化流程深夜崩溃的“隐形杀手”
你有没有遇到过这种情况?明明 HTTP Request 节点配置好了,跑了一次,数据只抓了一半就停了。或者更惨,数据量一大,干脆直接报错超时。
笔者在 N8N 大学做咨询时,发现 80% 的新手在处理 API 数据时,都死在了“分页”这道坎上。API 不是一次性把所有数据给你的,它们像挤牙膏一样,分页返回。不懂分页逻辑,你的 n8n 流程就只能抓取到第一页的“残羹冷炙”。
今天,我们就来硬核拆解 n8n HTTP Request 节点处理分页的两种核心模式:**游标(Cursor)**与**偏移量(Offset)**。看完这篇,你不仅能搞定分页,还能写出更优雅、更高效的自动化流程。
分页的两种“方言”:Offset 与 Cursor
在动手之前,我们得先搞懂 API 是怎么说话的。API 分页主要有两种逻辑,就像南方人说“勺子”,北方人说“调羹”,意思一样,叫法不同。
1. 偏移量模式 (Offset-based)
这是最传统的分页方式,常见于 MySQL 数据库或简单的 REST API。
- 逻辑:告诉服务器,“从第 10 条数据开始,给我 20 条”。
- 参数:通常包含
page=2或offset=20,加上limit=20。 - 缺点:数据量大时性能差,且如果数据实时变动,容易出现重复或遗漏。
2. 游标模式 (Cursor-based)
这是现代 API(如 Slack、Stripe、某些大型电商平台)的宠儿。
- 逻辑:不看页码,看“位置”。服务器给你一个标记(Cursor),你下次请求带上这个标记,服务器就知道从哪继续。
- 参数:通常是
cursor=abcdef123456。 - 优点:性能稳定,适合实时数据流,不会因为翻页而跳过数据。
实战:在 n8n 中搞定 Offset 分页
Offset 分页在 n8n 里处理相对简单,我们利用 IF 节点和 Set 节点就能搞定循环。
步骤 1:初始化参数
在流程开始处,使用 Set 节点(或 Start 节点)定义初始变量:
page= 1hasMore= true
步骤 2:配置 HTTP Request 节点
在 HTTP Request 节点的 Query Parameters 中:
- Key:
page,Value:={{$json.page}} - Key:
limit,Value:100(根据 API 限制填写)
注意:这里一定要勾选 "Full Response" 或者确保你能获取到响应头,因为判断是否有下一页通常在响应头里(比如 X-Total-Count)或者响应体里(比如 data.length)。
步骤 3:判断是否继续 (IF 节点)
连接一个 IF 节点,判断逻辑如下:
- 如果
={{$json.data.length}} > 0(假设响应体里有个 data 数组) - 或者
={{$json.meta.total}} > {{$json.meta.page * 100}}
如果满足条件,说明还有下一页,进入“增加页码”的分支。
步骤 4:更新状态并循环
在“是”的分支,加一个 Set 节点:
- Key:
page,Value:={{$json.page + 1}}
然后将这个节点连接回 HTTP Request 节点,形成闭环。如果判断为“否”,则走剩下的流程。
实战:在 n8n 中搞定 Cursor 分页
Cursor 分页稍微复杂一点,因为下一页的参数依赖于上一页的响应结果。
步骤 1:初始状态
Start 节点或 Set 节点,设置初始 Cursor 为空或 null:
cursor= nullhasMore= true
步骤 2:HTTP Request 节点配置
在 Query Parameters 中:
- Key:
cursor,Value:={{$json.cursor}}(第一次为空,API 会忽略) - Key:
limit,Value:50
避坑点:有些 API 的 Cursor 参数名是 after、offset(虽然逻辑是 cursor)。务必看 API 文档!
步骤 3:提取下一页的 Cursor
API 响应通常会返回一个类似 next_cursor 或 pagination.next_cursor 的字段。
在 HTTP Request 节点后连接一个 Set 节点:
- Key:
cursor,Value:={{$json.pagination.next_cursor}} - Key:
hasMore,Value:={{$json.pagination.has_more}}(布尔值)
步骤 4:循环控制
连接一个 IF 节点,判断 hasMore 是否为 true。
- 如果是,将流程指向 HTTP Request 节点(注意:n8n 不支持直接的“自循环”,你需要将流程设计为从 HTTP Request 出发,经过 IF,如果为真再连回 HTTP Request。通常做法是把 HTTP Request 放在一个子流程中,或者利用 n8n 的“While”循环逻辑,但最简单的还是用 IF 节点连线回指)。
笔者提示:在 n8n 界面上,你可以拖动 HTTP Request 节点的输出线,指向 IF 节点,再从 IF 节点的“Yes”出口连线回 HTTP Request 节点的左侧。这是 n8n 最经典的“兜圈子”画法。
高级技巧:使用 n8n 的 Loop Over Items
如果你觉得手写 IF 循环太累,n8n 有一个隐藏神器:Split In Batches 节点。
虽然名字叫 Split In Batches(分批处理),但它本质上就是一个循环器。你可以把 HTTP Request 放在这个节点里,设置 Batch Size 为 1(或者根据你的分页逻辑设置)。
但对于 Cursor 分页,Split In Batches 有时候不好用,因为它不支持动态更新参数。这时候,手动构建循环(HTTP -> Set -> IF -> HTTP)依然是最稳健的方案。
避坑指南:笔者踩过的雷
在 N8N 大学的教学案例中,分页问题通常报错如下:
400 Bad Request:参数名错误。比如 API 要page,你传了p。429 Too Many Requests:分页跑太快,触发了 API 的限流。
解决方案 1:添加 Wait 节点
在循环的回路中,或者 HTTP Request 之前,添加一个 Wait 节点。设置等待 1-2 秒,给 API 喘息的机会。
解决方案 2:处理空数据
有时候 API 返回 200,但 data 是空数组。你的 IF 判断逻辑必须严谨。建议判断:
{{ $json.data && $json.data.length > 0 }}
或者判断 API 返回的特定的结束标志位。
解决方案 3:内存溢出 (OOM)
如果你在循环中一直收集数据,最后一次性输出,数据量大时 n8n 会崩溃。正确的做法是:在循环内部直接处理数据(比如写入数据库),或者使用 n8n 的 Streaming 功能(企业版特性),普通版建议分批写入。
FAQ:关于 n8n 分页的常见疑问
Q1: 如果 API 的分页逻辑很怪,n8n 能处理吗?
A: 只要你能用数学或逻辑表达出来,n8n 就能处理。n8n 的 HTTP Request 节点支持 JavaScript 表达式({{ ... }}),这是你的终极武器。遇到怪异的分页,多用表达式拼接参数。
Q2: 我怎么知道 API 是 Cursor 还是 Offset?
A: 看文档。看它的请求参数列表。如果看到 page, pagenum,一般是 Offset。如果看到 cursor, after, before,一般是 Cursor。如果文档没写,抓包看上一个请求返回了什么。
Q3: 循环跑起来了,但怎么把所有页的数据汇总?
A: n8n 的循环是流式的。如果你需要汇总,可以在循环外接一个 Aggregator 节点(或者 Set 节点配合数组操作),但更推荐在循环内直接把数据推送到 Google Sheets、数据库或 Webhook,避免内存爆炸。
总结与资源
处理 API 分页是 n8n 进阶的必修课。Offset 适合简单场景,Cursor 适合大数据量场景。核心在于利用 IF 节点判断状态,利用 Set 节点更新参数,形成闭环。
希望这篇实战解析能帮你理清思路。如果你在配置过程中遇到具体的报错,欢迎前往 N8N大学 社区发帖,笔者和众多“学长”会为你解惑。
记住,自动化不是一蹴而就的,而是通过不断的调试和迭代,让机器替你完成枯燥的工作。