# 快手 接口台账

> 唯一证据来源:`/tmp/aiddit_captures/` 下真实抓包。host = `http://crawler.aiddit.com`。
> 涉及 capture:`kuaishou_search.json`、`kuaishou_detail.json`、`kuaishou_account_info.json`、`kuaishou_video_dl.json`。
> 鉴权:所有 `crawler.aiddit.com` 接口实测**无 Authorization 头**;仅需 base_url(env `CONTENTFIND_API_CRAWAPI_BASE_URL`)。
> 限流约定:两次调用间隔 ≥ 15s。
> 结构特点:快手三个接口**返回的都是归一化结构**(`channel_content_id` / `channel_account_id` 等),无抖音那种 raw 原生结构差异。

---

## 2026-06-17 URL 解包与 OSS v2 批量验证

本节来自 V4 诊断脚本 `tests/test_platform_video_url_candidates.py` 的真实探测。完整原始响应和临时 URL 仅保存在 gitignore 下的 `data/platform_url_probe/20260617_115219/`，本文只记录脱敏统计、字段路径、host 和状态。

### 探测规模

- 真实搜索 query：10 个。
- 搜索 item 样本：60 条。
- 抽取 URL 候选：`page=89`、`image=80`、`video_candidate=75`、`audio=1`。
- 推荐规则选中正片 URL：50 条。
- 其中 20 条 selected URL 做 OSS v2 批量验证。
- 历史对照 run：`v1_run_94448252f5cd`。

### 历史 run 对照

- 历史 run 决策：`ADD_TO_CONTENT_POOL=8`、`KEEP_CONTENT_FOR_REVIEW=9`、`REJECT_CONTENT=6`、`TECHNICAL_RETRY_REQUIRED=14`。
- 媒体状态：`oss_uploaded=29`、`unavailable=8`。
- 失败侧：8 条 `no_play_url` 没有 `play_url/oss_url`；1 条 media 侧出现 `oss_upload_http_error`，但技术重试直接原因仍是 Gemini timeout。
- 历史 runtime 不能反查原始字段路径：`platform_raw_payload` 只保留 `channel_content_id/channel_account_id`，没有完整 `video_url_list/image_url_list/content_link/bgm_data`。

### 推荐 URL 解包规则

- 递归抽取 search/detail 中所有 URL，不再把 `video_url_list[0]` 当作唯一事实。
- 快手优先 detail 中的正片 URL：`$.detail.video_url_list[].video_url`。
- detail 不存在或不可用时，fallback 到 search 正片 URL：`$.search.video_url_list[].video_url`。
- 明确排除：`content_link` 页面链接、`image_url_list` 封面/图片、`avatar` 头像、`bgm_data.play_url` 音频、广告/素材字段。
- 多候选排序看字段路径、HTTP 200/206、`content_type=video/mp4`、Range 支持、host 稳定性和 duration；不默认取第一个。

### 本轮真实字段与 host

- selected 字段路径：`$.detail.video_url_list[0].video_url=25`，`$.search.video_url_list[0].video_url=25`。
- selected host 分布：`tymov2.a.kwimgs.com=17`、`hwmov.a.yximgs.com=7`、`v23-3.kwaicdn.com=5`、`v2.oskwai.com=6`、`ucmov.a.kwimgs.com=5`、`dxsc.oppass.cn=4`、`v4.kwaicdn.com=3`、`cc1.xyydnode.com=2`、`v1.oskwai.com=1`。
- 所有 selected 样本轻量验证均为可下载视频：HTTP `200/206`，`content_type=video/mp4`，Range 返回字节范围或完整小文件。

### OSS v2 验证结论

- 使用生产当前 payload 形态（把 `referer` 作为 dict 字段传给 OSS v2）时：20/20 返回 HTTP 200 但 `oss_object.cdn_url` 缺失，分类为 `oss_upload_response_invalid`。
- 抽样复打发现 OSS body 为 `status=10000`、`oss_object=null`，错误原因包含 `dict object has no attribute encode`。
- 使用同一批 selected URL、去掉 `referer` 字段后做 no-referer 复验：20/20 成功返回 `oss_object.cdn_url`。
- 结论：本轮 selected URL 规则能选出可被 OSS v2 归档的正片 URL；历史/生产里的 invalid 更像 OSS v2 payload 兼容问题，不是 URL 字段拆错。

### 生产改造建议

- 第一优先级：调整 OSS v2 调用 payload，不再把 `referer` dict 直接传给 v2；如 OSS 侧确需 headers，应与上传服务约定字符串/headers 字段格式后再传。
- 第二优先级：生产 runtime 里补充 URL 候选摘要：`selected_path`、`selected_host`、`probe_status`、`content_type`、`content_range`、`oss_payload_mode`、`oss_failure_type`。
- 第三优先级：后续再改平台 adapter 的 `play_url` 抽取逻辑，改为 detail 优先、候选分类过滤、search fallback。

---

## 一、关键词搜索 PLT_KUAISHOU_KEYWORD

### ① 身份
- source_id:`PLT_KUAISHOU_KEYWORD`
- 接口名:快手关键词搜索 / kuai_shou keyword_v2
- 用途:按关键词拉一页内容卡片(视频)。
- 状态:**verified**(capture `kuaishou_search.json`,`code=0`)

### ② 请求标准
- method:`POST`
- path:`/crawler/kuai_shou/keyword_v2`
- 鉴权:无
- 参数表:

| 名 | 类型 | 必填 | 示例 |
|---|---|---|---|
| keyword | string | 是 | `彩虹` |

> 注:实测请求体只含 `keyword`(无 cursor / sort_type 字段)。

### ③ 请求示例
```bash
curl -X POST http://crawler.aiddit.com/crawler/kuai_shou/keyword_v2 \
  -H 'Content-Type: application/json' \
  -d '{"keyword":"彩虹"}'
```

### ④ 响应信封 + 翻页
- 信封:`{ "code":0, "msg":null, "data":{ "has_more":false, "next_cursor":null, "data":[...] } }`
- `has_more`:`false`(本次实测单页返回,无翻页游标)
- `next_cursor`:`null`
- `data.data`:数组,本页 `9` 条卡片(归一化结构)。

### ⑤ 字段台账(单条卡片 = `data.data[i]`,归一化名)

| 字段名 | 类型 | 含义 | 提取标注 | 真实示例值 |
|---|---|---|---|---|
| `channel_content_id` | string | 内容 id | 直接可用 | `3xepqgwddgbickc` |
| `content_link` | string | 内容链接 | 直接可用 | `https://www.gifshow.com/fw/photo/3xepqgwddgbickc` |
| `title` | string | 标题(实测可能 null) | 直接可用 | `null` |
| `body_text` | string | 正文(实测可能 null) | 直接可用 | `null` |
| `topic_list` | string[] | 话题 tag(实测 null) | 直接可用 | `null` |
| `content_type` | string | 内容类型 | 直接可用 | `video` |
| `video_url_list[].video_url` | string | 视频地址 | 需后处理下载 | `http://v23-3.kwaicdn.com/upic/2025/05/11/07/...b.mp4?pkey=...&tag=...&clientCacheKey=3xepqgwddgbickc_b.mp4...` |
| `video_url_list[].video_duration` | int(秒) | 时长 | 直接可用 | `4` |
| `image_url_list[].image_url` | string | 封面图 | 直接可用 | `http://p66-plat.wsukwai.com/upic/...540p_*.kvif?...` |
| `channel_account_id` | string | 作者 id | 直接可用 | `3xfkwajatdh7p7i` |
| `channel_account_name` | string | 作者昵称 | 直接可用 | `夏若.` |
| `view_count` | int | 播放/观看数 | 直接可用 | `18472` |
| `like_count` | int | 点赞 | 直接可用 | `75` |
| `collect_count` | int | 收藏 | 直接可用 | `4` |
| `comment_count` | int | 评论 | 直接可用 | `0` |
| `share_count` | int | 分享 | 直接可用 | `0` |
| `publish_timestamp` | int(ms) | 发布时间戳(毫秒) | 直接可用 | `1746919311784` |
| `data.has_more` | bool | 是否有下一页 | 直接可用 | `false` |
| `data.next_cursor` | string | 下一页游标 | 直接可用 | `null` |

> 搜索结果只带作者 `channel_account_id` + `channel_account_name`,**不含作者粉丝数/签名等档案**(这点与抖音不同)。要拿作者画像需二次请求 account_info(见三)。

### ⑥ 提取与后处理规则
- tag:`topic_list` 实测为 null,快手搜索阶段常无结构化 tag;若 `body_text` 有内容可正则 `#([^\s#]+)` 抽取,否则 tag 为空。
- 时间:`publish_timestamp` 已是毫秒,直接用。
- 视频可下载性结论(capture `kuaishou_video_dl.json`):`http_status=206`、`content_type=video/mp4`、`content_range=bytes 0-546454/546455`、`bytes_received=546455`、`looks_like_video=true` → **可下载**。
- **下载头**:快手 CDN(`*.kwaicdn.com`)URL 自带 `pkey` / `tag` 签名,实测带 `Range: bytes=0-` 即可 206;建议补 `Referer: https://www.kuaishou.com/`。相比抖音对 UA 不敏感,但保留通用 UA 更稳。

### ⑦ 边界与失败
- 限流 ≥ 15s。
- 空值约定:`title`/`body_text`/`topic_list` 常为 null,提取层需容忍。
- 本次实测 `has_more=false`,单页即止;若后续版本返回 next_cursor 再实现翻页。

### ⑧ 落点
| 归一字段 | canonical 名 | 游走边 |
|---|---|---|
| `channel_content_id` | `platform_content_id` | search_page_to_content |
| `channel_account_id` | `platform_author_id` | video_to_author |
| `channel_account_name` | `author_display_name` | video_to_author |
| `topic_list` | `tags` | video_to_hashtag |
| `like_count`/`comment_count`/`share_count`/`collect_count` | `statistics.*` | — |
| `has_more`/`next_cursor` | `has_more`/`next_cursor` | search_page_to_content |

---

## 二、内容详情 PLT_KUAISHOU_DETAIL

### ① 身份
- source_id:`PLT_KUAISHOU_DETAIL`
- 接口名:快手内容详情 / kuai_shou detail
- 用途:按 content_id 拉单条归一化详情(视频、互动数、BGM、作者)。
- 状态:**verified**(capture `kuaishou_detail.json`,`code=0`)

### ② 请求标准
- method:`POST`
- path:`/crawler/kuai_shou/detail`
- 鉴权:无
- 参数表:

| 名 | 类型 | 必填 | 示例 |
|---|---|---|---|
| content_id | string | 是 | `3xepqgwddgbickc` |

### ③ 请求示例
```bash
curl -X POST http://crawler.aiddit.com/crawler/kuai_shou/detail \
  -H 'Content-Type: application/json' \
  -d '{"content_id":"3xepqgwddgbickc"}'
```

### ④ 响应信封 + 翻页
- 信封:`{ "code":0, "msg":null, "data":{ "has_more":false, "next_cursor":null, "data":{...} } }`
- 详情无翻页:`has_more=false`、`next_cursor=null`。

### ⑤ 字段台账(`data.data`,归一化名)

| 字段名 | 类型 | 含义 | 提取标注 | 真实示例值 |
|---|---|---|---|---|
| `channel_content_id` | string | 内容 id | 直接可用 | `3xepqgwddgbickc` |
| `content_link` | string | 内容链接 | 直接可用 | `https://www.gifshow.com/fw/photo/3xepqgwddgbickc` |
| `content_type` | string | 内容类型 | 直接可用 | `video` |
| `title` / `body_text` / `topic_list` | string / string[] | 标题/正文/话题(实测均 null) | 直接可用 | `null` |
| `video_url_list[].video_url` | string | 视频地址 | 需后处理下载 | `http://v4.kwaicdn.com/ksc1/...b.mp4?pkey=...&clientCacheKey=3xepqgwddgbickc_b.mp4&tt=b...` |
| `video_url_list[].video_duration` | int(秒) | 时长 | 直接可用 | `4` |
| `image_url_list[].image_url` | string | 封面图 | 直接可用 | `http://hw2.a.kwimgs.com/upic/...540p_*.kvif?...` |
| `channel_account_id` | string | 作者 id | 直接可用 | `3xfkwajatdh7p7i` |
| `channel_account_name` | string | 作者昵称 | 直接可用 | `夏若.` |
| `avatar` | string | 作者头像 | 直接可用 | `http://p4.a.yximgs.com/kimg/uhead/...:200x200.heif` |
| `view_count` | int | 观看数 | 直接可用 | `18472` |
| `like_count` | int | 点赞 | 直接可用 | `75` |
| `collect_count` | int | 收藏 | 直接可用 | `4` |
| `comment_count` | int | 评论 | 直接可用 | `0` |
| `share_count` | int | 分享 | 直接可用 | `7` |
| `bgm_data.title` / `bgm_data.author` / `bgm_data.play_url` | string | BGM | 直接可用 | `没去学校(DJ版)` / `kiss` / `https://v4-plat-fdl.wsukwai.com/ksc2/*.m4a?...` |
| `publish_timestamp` | int(ms) | 发布时间戳 | 直接可用 | `1746919311784` |

> 注:同一条内容 `share_count` 在 detail=`7`、在 search=`0`,以 detail 为准(search 该字段可能滞后)。

### ⑥ 提取与后处理规则
- tag:`topic_list` 实测 null;按需从 `body_text` 正则抽取。
- 视频地址:`video_url_list[].video_url` 为 kwaicdn 直链,带签名参数,直接下载。
- 时间:`publish_timestamp` 毫秒,直接用。
- 视频可下载性:同搜索结论(`kuaishou_video_dl.json`:206 / video/mp4 / looks_like_video=true)。**下载带 `Range: bytes=0-` + `Referer: https://www.kuaishou.com/`**。

### ⑦ 边界与失败
- 限流 ≥ 15s。
- 空值:`title`/`body_text`/`topic_list` 常 null。

### ⑧ 落点
| 归一字段 | canonical 名 | 游走边 |
|---|---|---|
| `channel_content_id` | `platform_content_id` | — |
| `channel_account_id` | `platform_author_id` | video_to_author |
| `channel_account_name` | `author_display_name` | video_to_author |
| `topic_list` | `tags` | video_to_hashtag |
| `like_count`/`comment_count`/`share_count`/`collect_count` | `statistics.*` | — |

---

## 三、账号信息 PLT_KUAISHOU_ACCOUNT_INFO

### ① 身份
- source_id:`PLT_KUAISHOU_ACCOUNT_INFO`
- 接口名:快手账号信息 / kuai_shou account_info
- 用途:按作者 id 拉作者档案(昵称、粉丝数、签名、标签、性别),补齐搜索/详情缺失的作者画像。
- 状态:**verified**(capture `kuaishou_account_info.json`,`code=0`)

### ② 请求标准
- method:`POST`
- path:`/crawler/kuai_shou/account_info`
- 鉴权:无
- 参数表:

| 名 | 类型 | 必填 | 示例 |
|---|---|---|---|
| account_id | string | 是 | `3xfkwajatdh7p7i` |
| is_cache | bool | 否 | `true` |

### ③ 请求示例
```bash
curl -X POST http://crawler.aiddit.com/crawler/kuai_shou/account_info \
  -H 'Content-Type: application/json' \
  -d '{"account_id":"3xfkwajatdh7p7i","is_cache":true}'
```

### ④ 响应信封 + 翻页
- 信封:`{ "code":0, "msg":null, "data":{ "has_more":false, "next_cursor":null, "data":{...} } }`
- 无翻页:`has_more=false`、`next_cursor=null`。

### ⑤ 字段台账(`data.data`)

| 字段名 | 类型 | 含义 | 提取标注 | 真实示例值 |
|---|---|---|---|---|
| `channel_account_id` | string | 作者 id | 直接可用 | `3xfkwajatdh7p7i` |
| `ks_id` / `digit_id` | string | 快手号 | 直接可用 | `1514225378` |
| `account_link` | string | 主页链接 | 直接可用 | `https://www.kuaishou.com/profile/3xfkwajatdh7p7i` |
| `account_name` | string | 作者昵称 | 直接可用 | `夏若.` |
| `avatar_url` | string | 头像 | 直接可用 | `http://p4.a.yximgs.com/kimg/uhead/...:750x750.heif` |
| `gender` | string | 性别 | 直接可用 | `女` |
| `description` | string | 签名 | 直接可用 | `可以帮我破1000吗\n谢谢🙏...` |
| `tags` | string[] | 账号标签(星座/地区) | 直接可用 | `["摩羯座","贵州 安顺市"]` |
| `follower_count` | int | 粉丝数 | 直接可用 | `511` |
| `publish_count` | int | 作品数 | 直接可用 | `184` |
| `like_count` | int | 总获赞 | 直接可用 | `1598` |
| `update_timestamp` | int(ms) | 抓取时间 | 直接可用 | `1781158922013` |

### ⑥ 提取与后处理规则
- 作者档案:把 `account_name`/`description`/`tags`/`follower_count` 合并进作者节点。
- tag:`tags` 已结构化(星座+地区),直接用作账号级标签。
- 空值:`ip_location`/`collect_count`/`create_timestamp` 实测 null。

### ⑦ 边界与失败
- 限流 ≥ 15s。
- `account_id` 用 `channel_account_id`(如 `3xfkwajatdh7p7i`),非数字快手号。
- 快手无独立的"作者作品列表"接口 capture;"作者→作品"游走在快手侧暂依赖搜索结果聚合(本批未抓到 kuaishou blogger 接口)。

### ⑧ 落点
| 字段 | canonical 名 | 游走边 |
|---|---|---|
| `channel_account_id` | `platform_author_id` | video_to_author |
| `account_name` | `author_display_name` | video_to_author |
| `tags` | `tags`(账号级) | — |
| `follower_count`/`like_count` | `statistics.*`(账号聚合) | — |
