时钟、采集卡片、成功关卡和存储栈组成一条相连的工作流。
可靠的工作流会先检查运行,再存储这些行。

本指南提供一套供你在本地构建和测试的参考方法,用来建立可重复的摄取工作流,而不是预先建好的导入,也不是经过验证的完整部署。检查点是已保存的执行进度记录。upsert 是一种数据库操作:插入新行;如果唯一键匹配到重复记录,则更新已有记录。

技术范围与运行限制

配置 n8n 工作流之前,你必须了解这条提取管道处理什么,以及它的边界在哪里。本系统只做自动数据提取和持久存储。它不做垃圾信息过滤、情感分析、客户意图评分、自动回复或内容审核。如果需要把评论分到自定义类别,请阅读我们的指南:Facebook 评论客户反馈分析。如果需要把支持问题路由到正在处理的服务台,请查看我们的教程:Facebook 未回复评论问题队列。

本教程使用标准 n8n 节点和 Facebook Comments Scraper 的 HTTP API 讲解技术实现。API(应用程序接口)是一组规则和端点,让不同软件程序可以通过网络互相通信。抓取器只接受具体、已知的公开帖子 URL 作为输入。它不接受一般的个人主页链接、小组 URL 或品牌频道页面。执行后,抓取器返回格式为 JSON(JavaScript 对象表示法)的扁平数据记录。JSON 是用于共享结构化数据的标准文本格式。

每一条输出行要么是一条顶层评论,要么是一条直接的嵌套回复。返回的载荷包含 commentId 和 contentId 这类字符串标识符。请注意,authorId 和 publishedAt 可能为 null,publishedAt 和 scrapedAt 是时间戳,isReply 是布尔值,而不是精确的字符串标识符。原始 JSON 对象保留 parentCommentId、rootCommentId、isReply、text 以及 null 值。多次运行之间,数据库通过 contentId 与 commentId 上的复合唯一约束去重,该约束由参数化查询实现。

配置管道时,请记住以下运行参数:

  • 目标链接:必须在一个数组中传入 1 到 100 个明确的公开帖子 URL,每一项都是字符串。
  • 结果上限:参数 maxResultsPerPost 设置每个帖子最多采集的评论和回复行数。把它设为 25,会使该次运行每个帖子最多 25 行。
  • 回复提取:把 includeReplies 设为 true,会让抓取器在采集父评论的同时采集嵌套回复。
  • 完整提取:把 collectAllAvailable 设为 true 会绕过数字结果上限,但仍受平台可见性限制和计算费用约束。
  • 排序参数:all、newest 或 most_relevant 等排序选项决定 Facebook 在提取前如何排列评论,其作用独立于结果上限。

核心 API 端点参考

下表汇总本工作流使用的端点,以及它们的原始 URL 结构和预期响应字段:

核心 API 端点参考
端点 URL说明预期响应字段
https://api.apify.com/v2/acts/chronometrica~facebook-comments-scraper/runs启动抓取器运行data.id, data.defaultDatasetId, data.defaultKeyValueStoreId, data.status
https://api.apify.com/v2/actor-runs/RUN_ID检查执行状态data.status
https://api.apify.com/v2/datasets/DATASET_ID/items?format=json&clean=false&offset=0&limit=100获取数据集条目的分页评论记录的 JSON 数组
https://api.apify.com/v2/key-value-stores/STORE_ID/records/OUTPUT查看运行摘要存储该次运行的摘要记录

注意:把 RUN_ID、DATASET_ID 和 STORE_ID 等占位符替换为你实际保存的执行值。切勿把访问令牌直接放进 URL 字符串。

分步架构与节点顺序

要在 n8n 中构建这条管道,你需要配置一串结构化的内置节点。你需要已安装的 n8n、保存在 n8n Header Auth 凭据中的 Apify API 令牌(名称是 Authorization,值是 Bearer your_token),以及关系型数据库凭据(例如 Postgres)。

1. 触发器与凭据设置

先在 n8n 中新建一个工作流。打开工作流设置,选择你的本地时区,并在构建和测试组件期间保持工作流未发布。

你将创建两个入口:

  • Schedule Trigger:根据你的时区设置,自动触发每日或每小时的重复采集运行。
  • 手动触发与恢复输入:给操作人员使用的单独手动入口。因为手动触发器不能直接接受运行 ID,所以在它后面放一个 Edit Fields 节点,让操作人员输入已保存的现有运行 ID。这样可以恢复停滞的运行,而不必启动新的抓取任务。

2. 启动抓取器

把日程触发器连接到名为 Launch Scraper 的 HTTP Request 节点。方法设为 POST,目标为 Apify Actor 运行端点。附上你的 Header Auth 凭据。用原始 URL 字符串配置一份简短的纯 JSON 请求体:

text
{
  "startUrls": [
    "https://www.facebook.com/story.php?story_fbid=123456789&id=987654321"
  ],
  "maxResultsPerPost": 25,
  "includeReplies": true,
  "collectAllAvailable": false,
  "commentSortMode": "all"
}
分步架构与节点顺序

此请求成功时,Apify API 会返回一个包含执行元数据的外层对象。工作流必须立即捕获并保存四个值:data.id、data.defaultDatasetId、data.defaultKeyValueStoreId 和 data.status。

把这些值保存到以运行 ID 为键的持久数据库检查点,尝试次数初始化为 0,下一偏移量初始化为 0。持久是指永久存储在关系型数据库中,位于临时工作流内存之外。

关键安全规则:如果发生网络超时,切勿自动重试一次结果不明确的启动 POST 请求。如果第一次请求已经到达服务器,盲目重试会启动第二次计费的抓取运行。如果某次启动结果不明确,请先手动查看 Apify 账户中的运行列表,再触发新的运行。

3. 恢复路径

如果某次执行失败或停滞,手动恢复分支会用操作人员提供的运行 ID 选择已有检查点记录,并完全跳过启动节点。

工作流规定每个运行 ID 只能有一个活动执行。如果恢复的运行在数据库中已经显示终态已保存状态 SUCCEEDED,工作流会完全跳过状态轮询,直接进入页面加载。如果已保存状态是 failed,则转到人工复查。其他状态进入有上限的轮询。恢复运行绝不会自动重置尝试次数。

4. 状态轮询与计数门

每次状态检查之前,工作流查询数据库,检查当前尝试次数。如果尝试次数已达到 30,系统把执行转到人工复查分支,以防止无限循环。

如果次数低于 30,工作流在数据库中递增尝试次数,用 Wait 节点暂停执行 10 秒,然后用 HTTP GET 请求从 Apify API 检查运行状态(https://api.apify.com/v2/actor-runs/RUN_ID)。

轮询期间的网络请求错误应使用有上限的读取重试(最多 3 次,每次等待 10 秒),而不是重试 POST 请求。

工作流评估返回的状态:

  • SUCCEEDED:工作流把该扁平状态保存到数据库,并继续获取数据集。
  • READY / RUNNING / ABORTING / TIMING-OUT:工作流把执行送回计数门,以便等待并再次检查。
  • FAILED / ABORTED / TIMED-OUT:工作流记录失败状态,然后停止或转到人工复查。
  • 未知状态:转到人工复查。

注意:停止轮询循环并不会取消远程 Apify Actor;它只是让你的 n8n 工作流不再等待。轮询超时并不表示该帖子有 0 条评论。

5. 分页与页面加载

运行成功后,工作流进入分页循环。获取每一页之前,它从数据库重新加载当前检查点状态,以取得最新偏移量和数据集 ID。

它发出 HTTP GET 请求以获取数据集条目(https://api.apify.com/v2/datasets/DATASET_ID/items?format=json&clean=false&offset=0&limit=100,代入当前偏移量和已保存的数据集 ID)。你必须针对该次运行的具体数据集 ID,而不是泛用的 latest 别名。把请求格式设为 JSON,使用 clean=false,并设置 limit=100。

把 HTTP 节点配置为返回完整响应体。在把数据传给任何拆分节点之前,先记录传入数组的长度。如果初始页数组完全为空,则跳过拆分,并通过专用端点(https://api.apify.com/v2/key-value-stores/STORE_ID/records/OUTPUT),用已保存的存储 ID 检查键值存储的输出记录,以判断是否发生错误。对于后续页面,空数组只表示采集结束。

6. 拆分与数据库 upsert

把数据集数组传入 Split Out 节点,并配置明确的目标字段,字段名为 comment。

下游数据库存储节点接收每个拆分后的条目,并直接从 comment 映射字段。它验证非空字符串标识符和有效时间戳是否存在。它针对复合唯一键约束(contentId 与 commentId)用参数化查询执行 upsert。upsert 会插入新行;如果出现重复匹配,则更新已有记录。

为防止旧数据覆盖较新的更新,请配置数据库更新条件:仅当传入的 scrapedAt 时间戳大于或等于已存储的时间戳时,才更新传入的文本和元数据。这表示该公开帖子最新观测到的状态,而不是不可变的历史审计日志,也不是删除证明。

7. 推进检查点

当前页的每个条目都成功写入数据库之后,工作流把数据库中的检查点偏移量加上 100。

关键规则:切勿每条评论推进一次检查点偏移量。必须在整批成功之后,每一页恰好更新一次检查点偏移量。如果某一页写到中途时数据库写入失败,检查点偏移量保持不变,工作流就可以安全地重放该页,而不会产生重复记录。

在拆分步骤和数据库步骤之间,单独保留当前页长度的元数据。最终检查使用当前页长度,而不是第一次循环的长度。如果当前页长度少于 100 项,采集即完成,工作流结束。如果页长度等于 100,工作流回到重新加载检查点,并获取下一批。

读者测试与验证规程

在把工作流发布为在生产环境中自动运行之前,你必须在本地构建并测试工作流,并使用结构化验证检查。不要依赖未经测试的模板。

  1. 隔离摄取测试:把一份在重复测试中保持不变的固定测试输入 {body: [25 comments]} 直接注入 Split Out 节点。该节点路由到你的数据库节点,并配置为立即 STOP,从而绕过所有实时检查点节点和页面摘要节点。验证数据库恰好存储 25 行。第二次运行完全相同的测试输入时,新插入 0 行,并且已有记录被评估且不出现回退。再加入一条不同的第 26 条记录时,恰好插入 1 条新行。此外,验证较旧的测试输入不能覆盖较新的文本。
  2. 分页测试输入测试:用一个桩检查点,以及分布在 3 页上的 201 条记录(分别为 100、100 和 1),测试偏移量循环,且不产生任何付费 Actor 调用。
  3. 容错与轮询测试:测试在轮询路径上,最大轮询次数会正确触发人工复查。模拟中间某一页的数据库写入失败。验证检查点偏移量保持不变,恢复不会重新启动抓取器,并且非成功状态会正确阻止数据集获取。
  4. 真实服务手动验证:针对一条真实的公开帖子链接运行一次手动测试。查看你的平台限制、计算成本和执行日志。

只有在全部本地测试通过,并且日程时区配置正确之后,才保存并发布你的 n8n 工作流。

来源和延伸阅读

作者 Chronometrica · 更新日期

返回顶部 ↑