# 第三方小红书 API 对照与调用说明

本说明用于 MediaCrawler Cloud 的调用方、后台操作者和后续接入项目。它把供应商原始接口、Cloud 网关接口、后台任务选项、数据结果和调用次数逐项对应起来。

## 1. 先选哪一种调用方式

| 场景 | 调用入口 | 是否异步任务 | 是否用本地 Cookie / IP / Profile | 返回内容 |
| --- | --- | --- | --- | --- |
| 按关键词批量采集，并保存到统一结果库 | `POST /third-party-xhs/tasks` | 是 | 否 | `task.id`，随后轮询任务和下载标准化 JSONL |
| 多个关键词批量采集 | `POST /third-party-xhs/campaigns` | 是 | 否 | `campaign.id` 和子任务 |
| 已知笔记 ID，只取一篇完整笔记 | `GET /integrations/third-party/notes/{note_id}` | 否 | 否 | 供应商原始 JSON |
| 已知笔记 ID，只取评论或作者 | 对应 `/integrations/third-party/...` 网关 | 否 | 否 | 供应商原始 JSON |

所有 Cloud 接口都使用 `X-API-Key`。调用项目**绝不传**供应商 Token、Cookie、代理或验证码凭据；供应商 Token 只在服务器 `.env` 中保存。

## 2. 后台的“图文详情”究竟调用什么

后台任务中勾选：

`图文详情（GET /getFeedInfo；正文、图片、互动、话题；每篇 +1 次）`

等同于 Cloud 对每个搜索到的笔记调用供应商：

```http
GET /getFeedInfo?noteid=NOTE_ID&token=<服务端保存>
```

这会写入标准化笔记 JSONL 的正文、图片 URL、话题、发布时间、笔记属地、点赞/收藏/评论/分享、基础作者 ID 和昵称等字段。

它**不等同于**“作者简介”。完整作者简介、粉丝、关注、发帖数来自另一个独立接口 `/user/info`，只有勾选“作者简介”才调用。

当任务是“关键词 + 1 篇 + 仅图文详情”时，最低调用数是：

```text
1 次 /search/notes 搜索 + 1 次 /getFeedInfo 图文详情 = 最少 2 次
```

当已知 `note_id`，直接调用 Cloud 的 `GET /integrations/third-party/notes/{note_id}` 时，只会代理一次 `/getFeedInfo`，即 1 次供应商调用。分页、重试、作者、评论和二级评论会增加实际次数。

## 3. 供应商接口与 Cloud 对照

每次实际发送至供应商的 HTTP 请求均计 1 次，当前系统按 `¥0.01 / 次` 统计。网络或供应商错误后的重试也是一次实际调用，状态探测只有使用 `refresh=true` 时才调用供应商。

| 供应商原接口 | Cloud 直连接口 | 后台任务选项 / 自动步骤 | 参数与用途 | 数据写入 / 成本 |
| --- | --- | --- | --- | --- |
| `GET /search/notes` | `GET /integrations/third-party/search/notes` | 每个关键词按页自动搜索 | `keyword` 必填；`page`、`sort_type`、`note_type`、`note_time` 可选 | 选择笔记的搜索卡；每页 +1 次 |
| `GET /getFeedInfo` | `GET /integrations/third-party/notes/{note_id}` | 勾选“图文详情” | `noteid` 必填 | 正文、图片、互动、话题和基础作者字段；每篇 +1 次 |
| `GET /video/feed` / `GET /video_feed` | `GET /integrations/third-party/notes/{note_id}/video` | 目前只供已知视频 ID 的直连调用 | `note_id` 或 `noteid` 必填 | 视频 feed 原始 JSON；每次 +1 次 |
| `GET /user/info` | `GET /integrations/third-party/users/{user_id}` | 勾选“作者简介” | `user_id` 或 `userid` 必填 | 简介、粉丝、关注、获赞、发帖数；同任务同作者去重，每位 +1 次 |
| `GET /note/comment` | `GET /integrations/third-party/notes/{note_id}/comments` | 勾选“一级评论” | `note_id`、`start`、`sort_strategy` | 一级评论；每篇至少 +1 次 |
| `GET /note/subcomment` | `GET /integrations/third-party/notes/{note_id}/comments/{comment_id}/replies` | 同时勾选一级与二级评论 | `note_id`、`comment_id`、`start` | 二级评论；每条选中的一级评论最多 +1 次 |
| `GET /topic/feed` | `GET /integrations/third-party/topics/{page_id}/notes` | 直连调用 | `page_id` 必填；`sort`、`cursor` 可选 | 话题页笔记；每页 +1 次 |
| `GET /user/posted` | `GET /integrations/third-party/users/{user_id}/notes` | 直连调用 | `user_id` 必填；`cursor` 可选 | 某作者公开笔记；每页 +1 次 |

## 4. 任务接口完整示例

下面这个任务只搜索 1 篇图文并取完整图文详情，不采作者资料和评论：

```http
POST /third-party-xhs/tasks
X-API-Key: <MEDIACRAWLER_API_KEY>
Content-Type: application/json
```

```json
{
  "keywords": "香港大学房东直租",
  "max_notes": 1,
  "search_sort": "general",
  "search_note_type": 2,
  "third_party_note_time": "一周内",
  "collect_note_details": true,
  "collect_creator_profiles": false,
  "collect_comments": false,
  "collect_sub_comments": false,
  "max_comments_per_note": 0,
  "concurrency": 12,
  "requests_per_second": 20,
  "request_burst": 24
}
```

此示例最低为 2 次供应商调用，预估最低 `¥0.02`。任务返回 `task.id` 后使用：

```http
GET /tasks/{task_id}
GET /tasks/{task_id}/results
```

任务详情的 `third_party_api_calls` 和 `third_party_api_cost` 是该任务实际调用数与费用。全局按接口统计使用：

```http
GET /integrations/third-party/status
```

## 5. 搜索参数对照

| 后台显示 | 任务 JSON `search_sort` | 供应商 `sort_type` |
| --- | --- | --- |
| 综合 | `general` | `general` |
| 最新发布 | `time_descending` | `time_descending` |
| 最热 / 最多点赞 | `popularity_descending` | `popularity_descending` |
| 最多评论 | `comment_descending` | `comment_descending` |
| 最多收藏 | `collect_descending` | `collect_descending` |

| 后台显示 | `search_note_type` | 供应商 `note_type` |
| --- | --- | --- |
| 全部 | `0` | `不限` |
| 视频 | `1` | `视频` |
| 图文 | `2` | `图文` |

`third_party_note_time` 支持 `不限`、`一天内`、`一周内`、`半年内`，会原样传给供应商 `note_time`。

## 6. 数据保存与字段

任务产物在服务端任务目录中，调用方通过结果接口获取文件名和下载路径，不要直接依赖服务器磁盘路径。

| 文件 | 内容 |
| --- | --- |
| `xhs/jsonl/third_party_search_contents.jsonl` | 标准化笔记：`note_id`、标题、正文、图片 URL、互动字段、话题、作者基础字段和可选作者简介 |
| `xhs/jsonl/third_party_search_comments.jsonl` | 标准化一级/二级评论；二级评论含 `parent_comment_id` |
| `third_party_raw/*.json` | 已脱敏的供应商原始响应审计，用于字段排查，不含 Token |

图片仅保存小红书 CDN URL，后台通过受限图片代理显示；系统不会把图片二进制塞进数据库。

## 7. 限速、并发和失败规则

- `concurrency` 控制可并行处理的笔记工作流，默认 12，最大 50。
- `requests_per_second` 控制所有供应商 HTTP 请求的全局速率，默认 20。
- `request_burst` 是启动时可立即发出的请求上限，默认 24，不代表持续速率。
- 第三方任务不申请 Cookie、动态代理或 Chromium Profile，也不会因本地 Cookie/IP 失败而切换这些资源。
- 第三方任务由独立 worker 队列领取，创建后不等待本地“满 300 篇才启动”的 Cookie 阈值；第三方任务量也不计入 Cookie 采购预估或本地自动启动条件。
- 本地任务如需只等待新 Cookie、禁止转到本通道，可在 `POST /local-xhs/tasks` 或 `POST /campaigns` 中传入 `allow_third_party_fallback: false`。该字段不影响本通道自身任务。
- 如果供应商明确返回 `402`、`insufficient_points`、点数/余额/配额不足，任务会显示为“等待第三方恢复”，不消耗本地 Cookie、代理或 Chromium Profile，也不耗尽重试次数。默认冷却 10 分钟后仅以一个排队任务重新探测；恢复后整队自动续采，仍不足则继续等待。可用 `THIRD_PARTY_XHS_RECOVERY_WAIT_SECONDS` 调整冷却时间。
- 默认使用 1 个第三方 worker，防止多个任务叠加超过供应商 20 RPS。供应商明确允许更高**总**速率后，才应提高 `THIRD_PARTY_XHS_WORKERS`。
- 供应商临时失败只在第三方通道有限重试；仍失败时任务保留失败摘要，可从后台“续采”。
- `collect_sub_comments=true` 必须同时开启 `collect_comments=true`，否则接口返回 422 参数错误。

## 8. 后续项目接入顺序

1. 读取 `GET /integration-contract`，确认服务入口。
2. 需要入库的关键词采集调用 `POST /third-party-xhs/tasks`。
3. 保存返回的 `task.id`，轮询 `GET /tasks/{task_id}` 至 `completed`、`partial`、`failed` 或 `cancelled`。
4. 调用 `GET /tasks/{task_id}/results`，下载标准化 JSONL 并按 `note_id` / `comment_id` 去重入自己的业务库。
5. 用 `third_party_api_calls`、`third_party_api_cost` 和全局状态接口做配额与费用对账。
