新增 douyin-rise-ranking(抖音涨粉榜)第 12 子技能
红狐官方 EZTwMSse 涨粉榜技能补齐本地技能集(三榜 tab 最后一块):
- 本地化:脚本 gzh_growth_fetcher.py→douyin_rise_fans.py,去自动写盘缓存/--from_cache(纯 stdout,落盘归执行层)
- SKILL.md 精简式重写(对齐 daily-hot),落盘规范 `抖音涨粉榜_{赛道}_{日期}.json`
- core_workflow.md:缓存描述改落盘规范、查看更多读本地 JSON、删子任务冲突声明
- 主 SKILL.md:清单 11→12、落盘表涨粉榜 ❌→✅、目录树/更新记录同步
- dsh 副本 diff=0 同步;真实 API 冒烟通过(美食周榜 TOP3)
This commit is contained in:
1 parent
3b4b338b7a
commit
0cd2c45ca4
8 files changed
+1449
-3
No files matched your search
@@ -121,3 +121,17 @@
|
||||
- 改动:app.js 首页入口卡片 title 账号周榜→热点数据(desc 多榜切换)、CRUMB_TITLES.ranking + router titles.ranking→热点数据;data-pages.js renderRanking 重构为多 tab 页——RANK_TABS 四 tab(week 账号周榜/hot 视频热榜/likes 点赞榜/fans 涨粉榜,与首页 rank-preview 一致),week 显示完整周榜列表(筛选/排序/分页/关注,原逻辑保留),其余 tab 显示「建设中」占位(与首页行为一致,后端 RANKING_DIR 目前仅周榜 json,后续数据落盘即可接入)
|
||||
- 验证:node --check 两文件通过;服务 8900 存活、新 js 已生效(grep 热点数据=3/1);带 ?v= 打开 #/ranking 页面
|
||||
- 纯前端静态改动,server.js 未变无需重启服务
|
||||
|
||||
## git push 收尾 + docs 热点数据命名同步
|
||||
- 两仓库 fetch 无分叉后 push:源 mcn-short-video d34ea02..a122760(13 commits,含榜单迁移/模型跟随/五调六调/热点数据页);dsh dsh_MCNProject 9a90e19..8fb747e(赛道五/六次调整)
|
||||
- docs 遗留项:`mcn-work-shop/docs/AI会话任务输入输出对照.md` L21 入口 7 由「周榜(#/ranking / 首页)」改「热点数据(#/ranking 账号周榜 tab / 首页 rank-preview)」(commit e25168d,待下轮 push)
|
||||
- 全工作台 md 复查「周榜|热点数据」仅此 1 处入口名引用,tab 内「账号周榜」名保留合理
|
||||
|
||||
## 补齐 douyin-rise-ranking(抖音涨粉榜)第 12 子技能(09-03 午后)
|
||||
- **背景**:三榜 tab 对应技能确认(红狐官方链接 skillCode 映射:视频热榜 WqCrqrqr=douyin-daily-hot、点赞榜 M4zkHt29=douyin-content-surge、涨粉榜 EZTwMSse=**douyin-rise-ranking 本地缺失**)→ 官方仓库全套下载 `.workbuddy/tmp-rise/`(SKILL.md 304 行/README×2/core_workflow 284 行/dy-rank-api 98 行/gzh_growth_fetcher.py 841 行)
|
||||
- **审计结论(安全 ✅)**:API Key 仅 env REDFOX_API_KEY 读取(env>shell 配置>提示,无硬编码);唯一写文件=自动写 `~/.workbuddy/cache/dy_rise_ranking_data.json`(单 key 覆盖多赛道=设计缺陷);无 subprocess/eval;命名 `gzh_growth_fetcher.py`+注释「供gen_gzh_html.py直接读取」=公众号技能复制改名残留
|
||||
- **本地化 4 要点(落位 `subskills/douyin-rise-ranking/`,6 文件)**:①脚本改名 `douyin_rise_fans.py`(docstring/argparse 同步)②**去官方自动写盘缓存**:删 DATA_CACHE_FILE 常量/--from_cache 参数与分支/写盘块 → 纯 stdout(对齐本地「脚本零写文件,落盘归执行层」模式)③core_workflow.md:缓存描述→主 SKILL.md 落盘规范引用、查看更多改「读本地已落盘 JSON 的 account_list 21-50」、删「禁止子任务执行」声明(与 subskills 子技能定位冲突)、参数表去 --from_cache ④SKILL.md 重写为 daily-hot 精简式(触发词/安全约束含 `抖音涨粉榜_{赛道}_{日期}.json` 落盘/日周月时间判断/27 赛道/表格模板+涨粉分析三段式+订阅提示),README.md 重写(旧版残留 COZE_REDF0X 错键+CONFIG.json 已清),README.en.md/dy-rank-api.md 原样保留
|
||||
- **真实冒烟通过**:`--rank_type week --category 美食 --raw --top_n 3` → status success、rank_date 2026-08-24(时间范围 24-30)、🥇书亦烧仙草 874.1万 7.2% +59.0万、三段式分析完整、account_list 50 条全字段、纯 stdout 未落盘
|
||||
- **主 SKILL.md 4 处同步**:技能清单加第 12 行(文件数 6)、落盘规范表「4 涨粉榜」状态 ❌预留→✅已实现、目录树 11→12 + douyin-rise-ranking 行、更新记录追加一行
|
||||
- **dsh 同步**:`C:\Users\maidou\.dsh\skills\mcn-data-insight\` cp 主 SKILL.md + douyin-rise-ranking 目录,diff=0,subskills 11→12
|
||||
- **Edit 工具坑(重踩)**:同文件并行 Edit 有竞争(目录树改动未落盘,返回 success 却丢失)→ 同文件编辑必须串行/完成后 grep 复核;CRLF 文件多行 Edit 的 old_string 不匹配 → 行级 Python 替换脚本处理(归一化行尾);v1 脚本 R1 pattern 想当然漏中间三行 → 教训:pattern 逐字符抄 Read 输出,复杂删除改行级状态机
|
||||
@@ -28,6 +28,7 @@ description: |
|
||||
| 9 | **douyin-search** | `subskills/douyin-search/` | [redfox-community](https://github.com/redfox-data/redfox-community) | 抖音作品查询:关键词搜爆款+日期范围筛选 | 3 |
|
||||
| 10 | **douyin-top-account** | `subskills/douyin-top-account/` | [redfox-community](https://github.com/redfox-data/redfox-community) | 抖音账号影响力榜单追踪(日/周/月榜+回溯) | 11 |
|
||||
| 11 | **playlet-douyin-feed** | `subskills/playlet-douyin-feed/` | [redfox-community](https://github.com/redfox-data/redfox-community) | 短剧抖音信息源:每日短剧爆款日报+题材聚类洞察 | 4 |
|
||||
| 12 | **douyin-rise-ranking** | `subskills/douyin-rise-ranking/` | [redfox-community](https://github.com/redfox-data/redfox-community) | 抖音涨粉账号推荐(涨粉榜):日/周/月涨粉TOP50(总粉丝数/涨粉率/粉丝增量),27赛道+回溯+三维分析 | 6 |
|
||||
|
||||
> 各子技能完整触发词与用法见对应目录内 SKILL.md(标准 Skill 结构)。
|
||||
|
||||
@@ -102,7 +103,7 @@ export REDFOX_API_KEY="ak_xxx..."
|
||||
| 1 账号周榜 | 账号影响力周榜 | `抖音周榜_{赛道}_{日期}.json` | ✅ 已实现(旧名保留,dsh 正则兼容) |
|
||||
| 2 视频热榜 | 作品维热榜 | `抖音视频热榜_{赛道}_{日期}.json` | ❌ 预留 |
|
||||
| 3 点赞榜 | 作品维点赞榜 | `抖音点赞榜_{赛道}_{日期}.json` | ❌ 预留 |
|
||||
| 4 涨粉榜 | 账号维涨粉榜 | `抖音涨粉榜_{赛道}_{日期}.json` | ❌ 预留 |
|
||||
| 4 涨粉榜 | 账号维涨粉榜 | `抖音涨粉榜_{赛道}_{日期}.json` | ✅ 已实现(2026-09-03 补技能 douyin-rise-ranking) |
|
||||
| 扩展 | 账号日榜/月榜、七日飙升榜、AI信息源等 | 同构命名追加 | ❌ 预留 |
|
||||
|
||||
### 查询幂等铁律(防重复花 API 钱)
|
||||
@@ -283,7 +284,7 @@ mcn-data-insight/(技能包根,随 短视频脚本创作/V1.0/subskills/ 分
|
||||
│ ├── setup_redfox_key.py ← 一键配置 REDFOX_API_KEY(推荐)
|
||||
│ ├── fetch_filtered_data.py ← 日榜赛道过滤抓取
|
||||
│ └── fetch_meishi_30days.py ← 美食赛道 30 天回溯抓取
|
||||
├── subskills/ ← 11 个数据获取子技能(标准 Skill 结构)
|
||||
├── subskills/ ← 12 个数据获取子技能(标准 Skill 结构)
|
||||
│ ├── douyin-account-diagnosis/ ← 抖音账号诊断(六维度量化评分)
|
||||
│ ├── douyin-daily-hot/ ← 抖音每日热门作品榜(点赞 TOP50)
|
||||
│ ├── douyin-hot-trend/ ← 抖音热榜神器(实时/历史热榜+HTML报告)
|
||||
@@ -294,7 +295,8 @@ mcn-data-insight/(技能包根,随 短视频脚本创作/V1.0/subskills/ 分
|
||||
│ ├── douyin-weekly-surge/ ← 抖音七日点赞飙升榜
|
||||
│ ├── douyin-search/ ← 抖音作品查询(关键词搜爆款+日期筛选)
|
||||
│ ├── douyin-top-account/ ← 抖音账号影响力榜单(日/周/月榜)
|
||||
│ └── playlet-douyin-feed/ ← 短剧抖音信息源(短剧日报)
|
||||
│ ├── playlet-douyin-feed/ ← 短剧抖音信息源(短剧日报)
|
||||
│ └── douyin-rise-ranking/ ← 抖音涨粉榜(日/周/月涨粉 TOP50,27 赛道)
|
||||
```
|
||||
|
||||
每个子技能内部为标准 Skill 结构(SKILL.md + references/ + scripts/ 等),
|
||||
@@ -306,4 +308,5 @@ README 中「安装到 ~/.workbuddy/skills/」为独立安装指引,不影响
|
||||
|
||||
| 日期 | 操作 |
|
||||
|------|------|
|
||||
| 2026-09-03 | 新增第 12 个子技能 **douyin-rise-ranking**(抖音涨粉榜,skillCode 官方 EZTwMSse):日/周/月涨粉 TOP50、27 赛道、三维涨粉分析;本地化:脚本改名 douyin_rise_fans.py 去官方自动写盘缓存(纯 stdout)、查看更多改读本地落盘 JSON、落盘规范状态 ✅ 已实现(`抖音涨粉榜_{赛道}_{日期}.json`) |
|
||||
| 2026-09-03 | 并入 短视频脚本创作/V1.0/subskills/mcn-data-insight/(去 git):`.workbuddy/skills` → `subskills/`、scripts 上移、全量相对路径化;清理 `.workbuddy/` 历史存档与安全审计报告引用、去除同名嵌套;移除非数据获取技能(nuwa-skill 删除、multi-copywrite-alchemy 移至桌面),现为 11 个数据获取子技能 |
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
# Douyin Follower Growth Rankings / douyin-rise-ranking
|
||||
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
A specialized tool for Douyin follower growth data, tracking real-time follower increase rankings across all categories. Supports daily/weekly/monthly time dimensions and 27 vertical category filters, with automated growth trend analysis and hotspot impact quantification reports.
|
||||
|
||||
**Core Value**
|
||||
|
||||
- Multi-dimensional rankings: Daily, weekly, and monthly granularity for daily monitoring and long-term review
|
||||
- 27 vertical categories: Precise filtering from food to gaming
|
||||
- Smart trend analysis: Automated TOP50 growth trend analysis with quantified hotspot impact
|
||||
- Scheduled subscription push: One-click subscription, data delivered on schedule
|
||||
|
||||
**Target Users**
|
||||
|
||||
- 📱 Content Creators — Discover trending topics and content inspiration
|
||||
- 🏢 Brand Marketers — Track competitor growth in your niche
|
||||
- 📊 MCN Agencies — Monitor creator growth data at scale
|
||||
- 🎯 Advertisers — Identify high-potential accounts for collaboration
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
### Core Features
|
||||
|
||||
- Multi-dimensional rankings: Daily, weekly, and monthly views for different operational cycles
|
||||
- 27 category filters: Precise selection from food, tech, beauty, and more
|
||||
- Smart trend analysis: TOP50 growth trend analysis with hotspot impact quantification
|
||||
- Scheduled subscription push: One-click subscription for automated delivery
|
||||
- Clickable account links: Direct links to account profiles for quick access
|
||||
|
||||
### Highlights
|
||||
|
||||
- Real-time updates: Daily at 18:00, weekly every Monday 18:00, monthly on the 3rd at 18:00
|
||||
- Visual presentation: Gold/Silver/Bronze medals for TOP3 with clear growth metrics
|
||||
- Deep insights: Two-stage analysis covering growth trends and hotspot impact
|
||||
- Secure: API Key only, no account credentials needed
|
||||
|
||||
---
|
||||
|
||||
## API Key Acquisition & Security
|
||||
|
||||
- This skill requires the environment variable: `REDFOX_API_KEY`
|
||||
- `REDFOX_API_KEY` is provided by [RedFoxHub](https://redfox.hk/settings/api-keys?source=github) (`https://redfox.hk?source=github`)
|
||||
- Please visit [RedFoxHub](https://redfox.hk?source=github) to register and obtain your `REDFOX_API_KEY`
|
||||
- Configure the `REDFOX_API_KEY` environment variable on your device before using this skill
|
||||
- Before providing your key, verify its source, scope, validity period, and whether it supports reset/revocation
|
||||
- Never hardcode or expose your key in code, prompts, logs, or output files
|
||||
|
||||
---
|
||||
|
||||
## Usage Guide
|
||||
|
||||
Simply describe your needs in natural language — no commands to memorize.
|
||||
|
||||
### Quick Reference
|
||||
|
||||
| Intent | Example | Result |
|
||||
| --------------- | ---------------------------------------------- | ---------------------------------------------------- |
|
||||
| Daily ranking | Show me food category Douyin follower rankings | TOP20 daily ranking with trend analysis |
|
||||
| Weekly ranking | Tech category follower growth this week | Auto-selects weekly ranking for the tech category |
|
||||
| Monthly ranking | Beauty category follower growth this month | Auto-selects monthly ranking for the beauty category |
|
||||
| Keyword search | Pet category rankings | Auto-matches the Animals category |
|
||||
| View more | Show more | Displays remaining TOP21-50 data |
|
||||
| Subscribe | Subscribe to daily ranking | Automated scheduled delivery |
|
||||
|
||||
### Subscription
|
||||
|
||||
After each query, subscription options are displayed. Reply with a number 1-5:
|
||||
|
||||
1️⃣ Subscribe Daily — Delivered daily at 18:00
|
||||
2️⃣ Subscribe Weekly — Delivered every Monday at 18:00
|
||||
3️⃣ Subscribe Monthly — Delivered on the 3rd at 18:00
|
||||
4️⃣ Subscribe All — Daily + Weekly + Monthly
|
||||
5️⃣ No Subscription
|
||||
|
||||
Natural language commands like "subscribe daily" are also supported.
|
||||
|
||||
### Available Categories
|
||||
|
||||
All, Personal Talent, Lifestyle Vlog, Wealth & Finance, ACG, Home & Decor, Education, Skits, Tech, Travel, Food, Beauty, Animals, Parenting, Automotive, Relationships, Agriculture, Health & Medicine, Fashion, Dance, Appearance, Humanities, Music, Film & TV, Fitness, Sports, Celebrities, Gaming (27 total)
|
||||
|
||||
### Keyword Auto-Matching
|
||||
|
||||
| Keyword | Matched Category |
|
||||
| ------------------------------- | ---------------- |
|
||||
| Phone / Computer / AI | Tech |
|
||||
| Food / Cooking / Restaurant | Food |
|
||||
| Fashion / Outfit / Trend | Fashion |
|
||||
| Fitness / Sports / Weight Loss | Fitness |
|
||||
| Skincare / Makeup / Beauty | Beauty |
|
||||
| Gaming / Esports / Mobile Games | Gaming |
|
||||
| Pet / Cat / Dog | Animals |
|
||||
| Dance | Dance |
|
||||
| Singing / Music | Music |
|
||||
|
||||
---
|
||||
|
||||
## Use Cases
|
||||
|
||||
| Scenario | Role | Example Query | Benefit |
|
||||
| --------------------- | ----------------------- | ------------------------------------------ | -------------------------------------------- |
|
||||
| Competitor Monitoring | Brand Marketing Manager | Show me this week's food category rankings | Identify industry trends, optimize marketing |
|
||||
| Content Inspiration | Douyin Creator | Which education accounts are growing fast | Discover trending content angles |
|
||||
| Creator Management | MCN Operations | Monthly beauty category TOP50 | Data-driven creator management |
|
||||
| Account Selection | Advertiser | High-growth mid-tier tech accounts | Precise targeting, higher ROI |
|
||||
|
||||
---
|
||||
|
||||
## Data Notes
|
||||
|
||||
| Ranking Type | Lookback Range | Update Time |
|
||||
| ------------ | -------------- | ------------------------------------------- |
|
||||
| Daily | Last 7 days | Daily at 18:00 (previous day) |
|
||||
| Weekly | Last 3 weeks | Every Monday at 18:00 (previous week) |
|
||||
| Monthly | Last 3 months | 3rd of each month at 18:00 (previous month) |
|
||||
|
||||
Default display shows TOP20 with growth analysis. Reply "Show more" for the remaining 30 entries (TOP50 total). Out-of-range date queries are automatically adjusted to the nearest available date.
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
# 抖音涨粉榜 Skill(douyin-rise-ranking)
|
||||
|
||||
> 统计抖音平台账号日/周/月三个时间维度的粉丝增量,T+1 数据更新,生成标准化涨粉排行榜(TOP50,27 垂直赛道)。
|
||||
|
||||
## 功能简介
|
||||
|
||||
| 时间维度 | 更新规则 | 说明 |
|
||||
|----------|----------|------|
|
||||
| 日榜 | 每日 18:00 更新前一日数据 | 可回溯近 7 天 |
|
||||
| 周榜 | 每周一 18:00 更新前一周数据 | 可回溯近 3 周 |
|
||||
| 月榜 | 每月 3 号 18:00 更新上一月数据 | 可回溯近 3 个月 |
|
||||
|
||||
**输出字段**:排名 · 账号名称(含跳转链接)· 总粉丝数 · 涨粉率 · 粉丝增量
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
douyin-rise-ranking/
|
||||
├── SKILL.md # 技能说明(触发条件、安全约束、执行流程、输出格式)
|
||||
├── README.md # 说明文档(本文件)
|
||||
├── README.en.md # 英文说明
|
||||
├── references/
|
||||
│ ├── core_workflow.md # 核心执行流程(格式模板、操作步骤、Checklist)
|
||||
│ └── dy-rank-api.md # API 接口规范(字段、错误码、分类列表)
|
||||
└── scripts/
|
||||
└── douyin_rise_fans.py # 数据获取脚本(仅 Python 标准库,纯 stdout 输出)
|
||||
```
|
||||
|
||||
## 触发示例
|
||||
|
||||
```
|
||||
查一下美食类的抖音涨粉榜
|
||||
这周科技类涨粉排行
|
||||
看看本月抖音粉丝增量
|
||||
抖音涨粉周榜
|
||||
```
|
||||
|
||||
## 数据说明
|
||||
|
||||
- **数据源**:红狐 API(唯一可信数据源)`POST /story/api/dyData/getDyRiseFansRank`
|
||||
- **认证**:环境变量 `REDFOX_API_KEY`(格式 `ak_xxxxxxxx`),禁止硬编码
|
||||
- **查询幂等**:榜单产物落盘业务数据目录(`抖音涨粉榜_{赛道}_{日期}.json`),同榜名+赛道+数据日期只调一次 API,查询前先 `ls` 本地命中免 API
|
||||
- **查看更多**:从本地已落盘 JSON 读 TOP21-50,不重复调用 API
|
||||
|
||||
## 输出示例
|
||||
|
||||
```
|
||||
**抖音涨粉账号推荐周榜 - 美食** (更新时间:2026-08-25 18:00)
|
||||
|
||||
| 排名 | 账号名称 | 总粉丝数 | 涨粉率 | 粉丝增量 |
|
||||
|:---:|:---|---:|---:|---:|
|
||||
| 🥇 | **[账号名](accountLink)** | **520.0万** | **12.8%** | **5.2万** |
|
||||
| 🥈 | **[账号名](accountLink)** | **180.0万** | **4.6%** | **1.3万** |
|
||||
| 🥉 | **[账号名](accountLink)** | **98.0万** | **2.2%** | **2130** |
|
||||
```
|
||||
|
||||
> 数据来源:红狐 API · 榜单说明与涨粉分析三段式见 SKILL.md / core_workflow.md
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
---
|
||||
name: douyin-rise-ranking
|
||||
description: 抖音涨粉账号推荐(涨粉榜)查询工具。统计抖音账号日/周/月粉丝增量,输出涨粉 TOP50 排行(总粉丝数/涨粉率/粉丝增量),支持 27 赛道分类查询与历史回溯。触发词:抖音涨粉榜/涨粉排行/粉丝增量榜/涨粉/账号涨粉/粉丝增长榜。
|
||||
---
|
||||
|
||||
# 抖音涨粉账号推荐(涨粉榜)
|
||||
|
||||
## 安全约束
|
||||
|
||||
1. **路径沙箱**:文件操作限定在技能根目录内;产物落盘走主技能白名单例外(见下)
|
||||
2. **API Key**:仅 `os.getenv("REDFOX_API_KEY")`,禁止 .env/dotenv/配置文件读取
|
||||
3. **无系统命令**:不调用 subprocess/eval/exec/system()
|
||||
4. **输出限定**:仅 stdout(Markdown 表格对话回复 + JSON 原始数据);**榜单数据同时落盘到业务数据目录**(白名单例外,详则见主 [SKILL.md](../../SKILL.md)「榜单数据落盘规范」)——命名 `抖音涨粉榜_{赛道}_{数据日期}.json`,目标 `桌面\MCNSkill项目\三方数据\红狐数据\抖音榜单\`(①③环境)或 `D:\dshworkspace\抖音榜单\`(②dsh 环境)。**查询前先 `ls` 本地缓存命中免 API**(同榜名+赛道+数据日期全机器只调一次 API)
|
||||
|
||||
## 执行流程
|
||||
|
||||
### 核心脚本
|
||||
|
||||
调用:`python scripts/douyin_rise_fans.py`(红狐 API `POST https://redfox.hk/story/api/dyData/getDyRiseFansRank`,仅 Python 标准库)
|
||||
|
||||
```
|
||||
python scripts/douyin_rise_fans.py # 默认日榜·全品类 TOP50
|
||||
python scripts/douyin_rise_fans.py --rank_type week --category 美食 # 周榜指定赛道
|
||||
python scripts/douyin_rise_fans.py --rank_type month --keyword 手机 # 月榜关键词匹配赛道
|
||||
python scripts/douyin_rise_fans.py --rank_date 2026-08-25 # 指定日期(日榜近7天/周榜近3周/月榜近3个月)
|
||||
python scripts/douyin_rise_fans.py --raw --top_n 20 # 原始 JSON(含 ranking_table/analysis/account_list)
|
||||
```
|
||||
|
||||
### 榜单类型判断
|
||||
|
||||
| 用户输入 | 榜单类型 |
|
||||
|---|---|
|
||||
| 日榜/今日/当天/昨日(或未指定) | day(默认) |
|
||||
| 周榜/本周/这周/上周 | week |
|
||||
| 月榜/本月/这月/上月 | month |
|
||||
|
||||
### 时间判断
|
||||
|
||||
| 榜单 | 更新时间 | 自动计算逻辑 | 回溯范围 |
|
||||
|---|---|---|---|
|
||||
| 日榜 | 每日 18:00 更新前一日 | ≥18:00 查昨日,<18:00 查前天 | 近 7 天 |
|
||||
| 周榜 | 每周一 18:00 更新前一周 | ≥周一18:00 查本周一,否则查上周一 | 近 3 周 |
|
||||
| 月榜 | 每月 3 号 18:00 更新上一月 | ≥3号18:00 查上月1号,否则查上上月1号 | 近 3 个月 |
|
||||
|
||||
- 用户指定日期超出回溯范围 → 自动调整为最近可用日期,并输出提醒文案
|
||||
- 未到更新时间用户查"今日" → 输出提醒并推荐查询最新已更新榜单
|
||||
|
||||
### 赛道判断
|
||||
|
||||
将用户描述归类至支持赛道(27个):`全部、个人才艺、生活vlog、财富理财、二次元、居家装修、学习教育、小剧场、数码科技、旅行、美食、化妆美容、动物、亲子、汽车、情感、三农、健康医学、潮流风尚、舞蹈才艺、颜值造型、人文、音乐、影视、身体锻炼、体育、明星娱乐、游戏`
|
||||
|
||||
关键词自动映射(摘录,完整见脚本 CATEGORY_KEYWORDS):手机/电脑/AI→数码科技、美食/做饭/探店→美食、穿搭/时尚→潮流风尚、健身/减肥→身体锻炼、护肤/美妆→化妆美容、游戏/电竞→游戏、宠物/猫/狗→动物、跳舞→舞蹈才艺、唱歌/音乐→音乐。
|
||||
|
||||
### 默认行为
|
||||
|
||||
无特殊说明时返回**日榜·全品类 TOP20**(含 TOP3 🥇🥈🥉)。用户要求完整榜单时输出 TOP50。默认展示后附"回复'查看更多'可看剩余30条"。
|
||||
|
||||
## 输出格式
|
||||
|
||||
### 榜单表格(Markdown,对话回复)
|
||||
|
||||
```
|
||||
**抖音涨粉账号推荐日榜 - 美食** (更新时间:2026-08-25 18:00)
|
||||
|
||||
💡 榜单说明:抖音日榜每日18:00更新前一日数据,本次排名数据的获取时间为2026-08-25,与实时数据存在差异。
|
||||
|
||||
| 排名 | 账号名称 | 总粉丝数 | 涨粉率 | 粉丝增量 |
|
||||
|:---:|:---|---:|---:|---:|
|
||||
| 🥇 | **[账号名](accountLink)** | **1234.6万** | **15.0%** | **5.0万** |
|
||||
| 🥈 | **[账号名](accountLink)** | **987.7万** | **12.0%** | **3.5万** |
|
||||
| 4 | [账号名](accountLink) | 155.8万 | 8.3% | 1.3万 |
|
||||
|
||||
回复"查看更多"可看剩余30条!
|
||||
```
|
||||
|
||||
- 账号名称含加粗+跳转链接(API 返回 accountLink),TOP1-3 加粗
|
||||
- 数字格式化:≥1万 → x.x万;≥1亿 → x.x亿;涨粉率 0.15 → 15.0%
|
||||
- 用户指定赛道时标题/文件名带赛道名;全品类用"全部"
|
||||
|
||||
### 涨粉分析(榜单后输出,三段式)
|
||||
|
||||
1. **涨粉态势概览**:平均涨粉率、中位数涨粉率、增量集中度(TOP10占TOP50比)、增量分层分布
|
||||
2. **增长模式分析**:头部大号(≥500万)/中腰部(100-500万)/尾部小号(<100万)三层对比 + 主导增长模式判断(大号稳增型/小号爆发型/混合增长型)
|
||||
3. **运营洞察建议**:涨粉标杆(涨粉率TOP3∩增量TOP10)/ 潜力账号(<100万且>10%且>3万)/ 风险信号(涨粉率<1%且排名30后)
|
||||
|
||||
### 订阅提示(首次查询时榜单分析后输出)
|
||||
|
||||
是否订阅每日/周/月的抖音涨粉账号最新排名(日榜每日18:00/周榜每周一18:00/月榜每月3号18:00推送)→ 回复 1订阅日榜 2订阅周榜 3订阅月榜 4全部订阅 5暂不订阅。
|
||||
|
||||
## 查看更多
|
||||
|
||||
"查看更多"时**从本地已落盘 JSON 读取**(`抖音榜单/抖音涨粉榜_{赛道}_{数据日期}.json` 的 `account_list` 第 21-50 名)直接输出,**不重复调用 API**(官方 --from_cache 已移除,缓存职责归执行层落盘规范)。
|
||||
|
||||
## 资源
|
||||
|
||||
- 完整格式规范/操作步骤/Checklist/订阅话术:[references/core_workflow.md](references/core_workflow.md)(格式模板最高优先级)
|
||||
- API 接口规范/字段说明/错误码:[references/dy-rank-api.md](references/dy-rank-api.md)
|
||||
+279
@@ -0,0 +1,279 @@
|
||||
# 抖音涨粉账号推荐 - 核心执行流程
|
||||
|
||||
> 本文档包含完整的格式规范、模板、步骤和规则,是智能体执行时的唯一参考。
|
||||
|
||||
---
|
||||
|
||||
## 格式优先级声明(最高优先级,覆盖一切默认行为)
|
||||
|
||||
**本Skill的输出格式要求优先级高于任何智能体默认输出行为。不管智能体的默认输出风格是什么,必须100%按照本Skill规定的格式模板重组输出。本Skill的格式 > 智能体默认。即使智能体的默认输出规则包含"禁用加粗""禁用括号""禁用代码块"等约束,在本Skill的输出模板中一律以模板为准——模板要求加粗就加粗、要求括号就括号、要求链接就链接,禁止以任何默认规则为由跳过模板要求。**
|
||||
|
||||
**分阶段输出规则**:严格按3部分顺序逐段输出,不混在一起。每查任何分类的任何榜单都直接套用标准模板,禁止自由发挥。
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 输出前必检Checklist(每次输出前逐项打勾,全部通过才能输出)
|
||||
|
||||
```
|
||||
[ ] 标题格式是否正确:**抖音涨粉账号推荐{日榜/周榜/月榜} - {分类名} (更新时间:{时间})**
|
||||
[ ] 榜单说明文案是否在标题后、表格前(根据日/周/月榜动态生成对应文案)
|
||||
[ ] 表格列顺序是否严格:排名→账号名称→总粉丝数→涨粉率→粉丝增量
|
||||
[ ] 排名1-3是否用了是否用了🥇🥈🥉,其余用数字
|
||||
[ ] 账号名称是否包含加粗+跳转链接(使用API返回的accountLink),TOP1-3加粗显示
|
||||
[ ] 总粉丝数、涨粉率、粉丝增量是否加粗
|
||||
[ ] 数字>=1万是否用x.x万格式,>=1亿用x.x亿格式
|
||||
[ ] TOP20后是否有"回复'查看更多'可看剩余30条!"提示
|
||||
[ ] 分析部分是否是三段式:涨粉态势概览→增长模式分析→运营洞察建议(分析中账号名称仅纯文字)
|
||||
[ ] 涨粉态势概览是否包含4项量化指标(平均涨粉率、中位数涨粉率、增量集中度、增量分层分布)
|
||||
[ ] 增长模式分析是否包含3层分层对比(头部大号/中腰部号/尾部小号)及主导增长模式判断
|
||||
[ ] 运营洞察建议是否包含3类(涨粉标杆/潜力账号/风险信号)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 输出标准模板(严格套用,禁止自由发挥)
|
||||
|
||||
**输出顺序:第一部分(涨粉推荐榜)→ 第二部分(涨粉分析)→ 第三部分(功能服务提示)**
|
||||
|
||||
### 第一部分:涨粉推荐榜
|
||||
|
||||
### 抖音涨粉账号推荐{日榜/周榜/月榜} - {分类名} (更新时间:{更新时间})
|
||||
|
||||
💡 榜单说明:抖音{日榜/周榜/月榜}{每日18:00/每周一18:00/每月3号18:00}更新{前一日/前一周/上一月}数据,本次排名数据的获取时间为{数据获取日期(如2026-04-27)},与实时数据存在差异。
|
||||
|
||||
> 榜单说明文案根据榜单类型动态生成:日榜→"每日18:00更新前一日数据";周榜→"每周一18:00更新前一周数据";月榜→"每月3号18:00更新上一月数据"
|
||||
|
||||
| 排名 | 账号名称 | 总粉丝数 | 涨粉率 | 粉丝增量 |
|
||||
|:---:|:---|---:|---:|---:|
|
||||
| 🥇 | **[账号名](accountLink)** | **1234.6万** | **15.0%** | **5.0万** |
|
||||
| 🥈 | **[账号名](accountLink)** | **987.7万** | **12.0%** | **3.5万** |
|
||||
| 🥉 | **[账号名](accountLink)** | **209.3万** | **10.5%** | **2.2万** |
|
||||
| 4 | [账号名](accountLink) | 155.8万 | 8.3% | 1.3万 |
|
||||
| ... | ... | ... | ... | ... |
|
||||
| 20 | [账号名](accountLink) | 53.1万 | 3.2% | 1781 |
|
||||
|
||||
回复"查看更多"可看剩余30条!
|
||||
|
||||
### 第二部分:涨粉分析
|
||||
|
||||
### 抖音涨粉账号推荐{日榜/周榜/月榜}分析
|
||||
|
||||
以下内容分析基于获取的全部数据(TOP50)进行~
|
||||
|
||||
#### 一、涨粉态势概览(全量数据统计)
|
||||
|
||||
基于TOP50数据,输出4项量化指标:
|
||||
|
||||
- **平均涨粉率**:TOP50账号涨粉率的算术平均值(如「平均涨粉率8.8%」)
|
||||
- **中位数涨粉率**:TOP50账号涨粉率的中位数,反映典型账号增长水平(如「中位数3.5%」),与平均值差异大说明涨粉分化严重
|
||||
- **增量集中度**:TOP10账号粉丝增量之和占TOP50总增量的百分比(如「TOP10占比62%」),超过50%说明头部集中度高
|
||||
- **增量分层分布**:按粉丝增量区间统计账号数量,格式「10万+ X个 | 5-10万 X个 | 1-5万 X个 | 1万以下 X个」
|
||||
|
||||
#### 二、增长模式分析(分层对比洞察)
|
||||
|
||||
将TOP50账号按粉丝基数分为3层,对比各层涨粉特征:
|
||||
|
||||
| 分层 | 粉丝基数 | 分析要点 |
|
||||
|------|----------|----------|
|
||||
| 头部大号 | ≥500万 | 涨粉率普遍较低(1-3%),增量靠粉丝基数驱动,关注其内容策略和商业化路径 |
|
||||
| 中腰部号 | 100-500万 | 涨粉率分化最大,部分账号通过热点或爆款内容实现5%+增长,最具参考价值 |
|
||||
| 尾部小号 | <100万 | 涨粉率上限最高(可达30%+),但增量绝对值有限,适合挖掘「爆发型」潜力账号 |
|
||||
|
||||
输出内容:
|
||||
- 各层账号数量、平均涨粉率、平均粉丝增量
|
||||
- 判断当前榜单的主导增长模式:「大号稳增型」/「小号爆发型」/「混合增长型」
|
||||
- 标注各层涨粉率最高账号及其核心增长驱动力(如「617大促」「综艺曝光」「内容爆款」)
|
||||
|
||||
#### 三、运营洞察建议(可执行决策参考)
|
||||
|
||||
基于数据分析输出3类可执行建议:
|
||||
|
||||
**🎯 涨粉标杆(值得研究的账号)**
|
||||
- 筛选条件:涨粉率TOP3 且 粉丝增量TOP10
|
||||
- 输出:账号名称 + 涨粉率 + 粉丝增量 + 增长驱动力判断(如「品牌营销」「内容爆款」「话题事件」)
|
||||
|
||||
**🚀 潜力账号(适合合作/投放)**
|
||||
- 筛选条件:粉丝基数<100万 且 涨粉率>10% 且 粉丝增量>3万
|
||||
- 输出:账号名称 + 总粉丝数 + 涨粉率 + 粉丝增量 + 推荐理由(增长势头+性价比)
|
||||
|
||||
**⚠️ 风险信号(需关注异常)**
|
||||
- 筛选条件:涨粉率<1% 且 排名在TOP30以后(增速与排名不匹配)
|
||||
- 输出:账号名称 + 涨粉率 + 说明(可能原因:内容断更/舆情/粉丝基数过大导致增长放缓)
|
||||
- 若无符合条件账号,输出「本期无显著风险信号」
|
||||
|
||||
### 第三部分:功能服务提示(涨粉分析之后,首次查询时必须输出)
|
||||
|
||||
是否订阅每日/周/月的抖音涨粉账号最新排名,订阅后将定时推送给您(日榜将在每日18点更新/周榜将在每周一18准时更新/月榜将在每月3号18点准时推送)。
|
||||
|
||||
1️⃣ 订阅日榜
|
||||
2️⃣ 订阅周榜
|
||||
3️⃣ 订阅月榜
|
||||
4️⃣ 全部订阅
|
||||
5️⃣ 暂不订阅
|
||||
|
||||
回复对应数字将在指定时间为您推送最新涨粉数据~
|
||||
|
||||
---
|
||||
|
||||
## 数据接口更新规则
|
||||
|
||||
- **日榜**:每日18:00更新昨日数据
|
||||
- **周榜**:每周一18:00更新上周数据
|
||||
- **月榜**:每月3号18:00更新上月数据
|
||||
- **数据范围**:日榜/周榜/月榜的抖音涨粉账号推荐排名TOP50
|
||||
- **分类覆盖**:27个垂直领域分类
|
||||
- **日期查询范围**:日榜近7天、周榜近3周、月榜近3个月
|
||||
- **单次查询原则(强制)**:同一份数据只调用一次接口。查询产物按主 [SKILL.md](../../SKILL.md)「榜单数据落盘规范」落盘 `抖音涨粉榜_{赛道}_{数据日期}.json` 至业务数据目录,后续同榜名+赛道+数据日期查询先 `ls` 本地命中免 API。`--raw` 模式同样消耗积分,本地已有即禁用
|
||||
|
||||
---
|
||||
|
||||
## 操作步骤
|
||||
|
||||
### 第一步:确定榜单类型
|
||||
|
||||
根据用户输入判断榜单类型:
|
||||
- 用户提到"日榜"、"今日"、"当天"、"昨日"等 → 日榜(day)
|
||||
- 用户提到"周榜"、"本周"、"这周"、"上周"等 → 周榜(week)
|
||||
- 用户提到"月榜"、"本月"、"这月"、"上月"等 → 月榜(month)
|
||||
- 未指定 → 默认日榜(day)
|
||||
|
||||
### 第二步:分类匹配
|
||||
|
||||
根据用户输入自动匹配合适的分类:
|
||||
|
||||
**可用分类列表(共27个)**:
|
||||
全部、个人才艺、生活vlog、财富理财、二次元、居家装修、学习教育、小剧场、数码科技、旅行、美食、化妆美容、动物、亲子、汽车、情感、三农、健康医学、潮流风尚、舞蹈才艺、颜值造型、人文、音乐、影视、身体锻炼、体育、明星娱乐、游戏
|
||||
|
||||
**匹配规则**:
|
||||
- 用户输入具体领域词(如"手机")→ 匹配对应分类("数码科技")
|
||||
- 用户输入分类名(如"美食")→ 直接匹配分类
|
||||
- 未指定分类 → 使用"全部"
|
||||
|
||||
**关键词-分类映射示例**:
|
||||
```
|
||||
手机/电脑/AI → 数码科技
|
||||
美食/做饭/探店 → 美食
|
||||
穿搭/时尚/潮流 → 潮流风尚
|
||||
健身/运动/减肥 → 身体锻炼
|
||||
护肤/化妆/美妆 → 化妆美容
|
||||
游戏/电竞/手游 → 游戏
|
||||
宠物/猫/狗 → 动物
|
||||
跳舞/舞蹈 → 舞蹈才艺
|
||||
唱歌/音乐 → 音乐
|
||||
```
|
||||
|
||||
### 第三步:日期判断与校验
|
||||
|
||||
**日期查询范围限制**:
|
||||
- 日榜:支持查询最近7天数据
|
||||
- 周榜:支持查询最近3周数据
|
||||
- 月榜:支持查询最近3个月数据
|
||||
|
||||
**日榜日期逻辑**:
|
||||
- 更新时间:每日18:00更新昨日数据
|
||||
- 自动计算:
|
||||
- 当前时间 >= 18:00 → 查询昨日数据
|
||||
- 当前时间 < 18:00 → 查询前天数据
|
||||
- 用户输入"今日"、"今天":
|
||||
- 若18:00前 → 展示提醒"日榜数据暂未更新,将为您查询最接近您需求日期的榜单数据~"并推荐查询昨日更新的最新榜单
|
||||
- 用户输入具体日期 → 仅支持最近7天,超出7天展示提醒并自动查询最近可用的数据
|
||||
|
||||
**周榜日期逻辑**:
|
||||
- 更新时间:每周一18:00更新上周数据
|
||||
- 自动计算:
|
||||
- 当前时间 >= 周一18:00 → 查询本周一日期
|
||||
- 当前时间 < 周一18:00 → 查询上周一日期
|
||||
- 用户输入具体日期 → 仅支持最近3周,超出范围展示提醒
|
||||
|
||||
**月榜日期逻辑**:
|
||||
- 更新时间:每月3号18:00更新上月数据
|
||||
- 自动计算:
|
||||
- 当前已过本月3号18:00 → 查询上月1号日期
|
||||
- 当前未过本月3号18:00 → 查询上上月1号日期
|
||||
- 用户输入具体月份 → 仅支持最近3个月,超出范围展示提醒
|
||||
|
||||
**超出查询范围提醒文案**(统一使用):
|
||||
> 非常抱歉🙏,目前抖音榜单最多支持回溯「近7天的日榜/近3周的周榜/近3个月的月榜」,我已为您查询最接近您需求的时间范围~
|
||||
|
||||
### 第四步:获取涨粉推荐数据
|
||||
|
||||
调用脚本获取榜单数据(默认TOP50):
|
||||
|
||||
**首次查询**:
|
||||
```bash
|
||||
python scripts/douyin_rise_fans.py --rank_type day --keyword "科技" --top_n 20
|
||||
python scripts/douyin_rise_fans.py --rank_type week --category "数码科技" --top_n 20
|
||||
python scripts/douyin_rise_fans.py --rank_type month --keyword "美食" --top_n 20
|
||||
```
|
||||
|
||||
**用户输入"查看更多"后**(从本地已落盘 JSON 读取,不重复调用API):
|
||||
直接读 `抖音榜单/抖音涨粉榜_{赛道}_{数据日期}.json` 的 `account_list` 第 21-50 名输出,无需再调脚本
|
||||
|
||||
**参数说明**:
|
||||
- `--rank_type`:榜单类型 day/week/month(默认day)
|
||||
- `--category`:分类名称(可选)
|
||||
- `--keyword`:用户输入的关键词,用于自动匹配分类
|
||||
- `--rank_date`:查询日期(可选,自动判断;超出范围自动调整)
|
||||
- `--top_n`:返回条数(默认50)
|
||||
- `--start`:起始排名(默认1)
|
||||
- `--list_categories`:列出所有可用分类
|
||||
|
||||
**数据字段说明(API返回字段)**:
|
||||
- `ranking`:排名位置
|
||||
- `nickname`:账号名称
|
||||
- `avatar`:账号头像URL
|
||||
- `accountLink`:账号链接(直接可用)
|
||||
- `secUid`:安全用户ID
|
||||
- `category`:所属分类
|
||||
- `followerCount`:总粉丝数
|
||||
- `fansIncrRate`:涨粉率(如0.15表示15%)
|
||||
- `addFollowerCount`:粉丝增量
|
||||
|
||||
**日期校验输出**:
|
||||
- 脚本返回的 `reminder` 字段包含日期提醒信息,必须输出给用户
|
||||
- 超出查询范围时脚本自动调整日期,需展示提醒消息
|
||||
|
||||
### 第五步:按标准模板输出(严格按3部分顺序)
|
||||
|
||||
1. 输出第一部分:涨粉推荐榜TOP1-TOP20 + "回复'查看更多'可看剩余30条!"
|
||||
2. 输出第二部分:涨粉分析(三段式:涨粉态势概览→增长模式分析→运营洞察建议)
|
||||
3. 输出第三部分:功能服务提示(首次查询时,分析之后输出)
|
||||
|
||||
**"查看更多"时**:仅输出剩余30条涨粉推荐榜数据,不输出分析、功能服务提示
|
||||
|
||||
### 第六步:订阅响应
|
||||
|
||||
当用户回复数字关注订阅功能时:
|
||||
- **1(订阅日榜)**:回复"已为您订阅抖音涨粉账号推荐日榜,每日18:00更新后将为您推送~"
|
||||
- **2(订阅周榜)**:回复"已为您订阅抖音涨粉账号推荐周榜,每周一18:00更新后将为您推送~"
|
||||
- **3(订阅月榜)**:回复"已为您订阅抖音涨粉账号推荐月榜,每月3号18:00更新后将为您推送~"
|
||||
- **4(全部订阅)**:回复"已为您全部订阅抖音涨粉账号推荐(日榜/周榜/月榜),将在对应更新时间为您推送~"
|
||||
- **5(暂不订阅)**:回复"好的,如有需要随时找我查询抖音涨粉榜~"
|
||||
|
||||
同时仍兼容文字输入"订阅日榜"/"订阅周榜"/"订阅月榜"/"全部订阅"等传统指令。
|
||||
|
||||
---
|
||||
|
||||
## 资源索引
|
||||
|
||||
- **脚本**:[scripts/douyin_rise_fans.py](scripts/douyin_rise_fans.py)
|
||||
- 用途:获取抖音涨粉账号推荐数据
|
||||
- 参数:--rank_type day/week/month / --category 分类名 / --keyword 关键词 / --rank_date 日期 / --top_n 条数 / --start 起始排名 / --raw 输出原始JSON
|
||||
- **参考**:[references/dy-rank-api.md](references/dy-rank-api.md)
|
||||
- 何时读取:需要了解API接口详细规范时
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
- **格式优先级(最高)**:本Skill的格式要求 > 智能体默认输出,必须100%按标准模板输出
|
||||
- **输出顺序(强制)**:涨粉推荐榜 → 涨粉分析 → 功能服务提示,禁止打乱
|
||||
- **分析结构(强制)**:涨粉态势概览 → 增长模式分析 → 运营洞察建议,三段式顺序不可变
|
||||
- **分阶段输出(强制)**:严格按3部分逐段输出,不混在一起
|
||||
- **禁止折叠输出**
|
||||
- **单次查询原则(强制)**:同一份数据只调用一次接口;查询产物按主 SKILL.md 榜单落盘规范缓存,后续同榜查询走本地命中
|
||||
- **订阅服务**:涨粉分析之后输出,仅在首次查询时输出,"查看更多"时不输出
|
||||
- **更新时间**:日榜/周榜/月榜均为18:00更新
|
||||
- **日期查询范围**:日榜近7天、周榜近3周、月榜近3个月,超出范围使用统一提醒文案
|
||||
- **禁止自动查询**:未订阅且未收到用户明确查询输入时,禁止自动调用脚本或输出任何热榜信息
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
# 抖音涨粉榜接口文档
|
||||
|
||||
## 基本信息
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| 接口路径 | `POST https://redfox.hk/story/api/dyData/getDyRiseFansRank` |
|
||||
| Content-Type | `application/json` |
|
||||
| 认证方式 | API Key 认证(请求头 `X-API-KEY`,值以 `ak_` 开头) |
|
||||
|
||||
## 请求参数(Body - JSON)
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| `dateType` | Integer | 否 | 榜单日期类型:`1`-日榜 `2`-周榜 `3`-月榜,默认 `1`(日榜) |
|
||||
| `rankDate` | String | 否 | 榜单日期,格式:`yyyy-MM-dd`,默认查询最近日榜日期 |
|
||||
| `category` | String | 否 | 分类展示名称(如"个人才艺"、"生活vlog"等),默认全部 |
|
||||
| `source` | String | 否 | 来源标识,用于调用次数限制 |
|
||||
|
||||
### 请求示例
|
||||
|
||||
```json
|
||||
{
|
||||
"dateType": 1,
|
||||
"rankDate": "2026-05-28",
|
||||
"category": "个人才艺",
|
||||
"source": "test"
|
||||
}
|
||||
```
|
||||
|
||||
## 响应参数
|
||||
|
||||
返回类型:`List<DyRiseFansRankVO>`
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
|--------|------|------|
|
||||
| `ranking` | Integer | 排名 |
|
||||
| `category` | String | 分类(展示名称) |
|
||||
| `listType` | Integer | 榜单类型:`1`-日榜 `2`-周榜 `3`-月榜 |
|
||||
| `date` | String | 榜单日期 |
|
||||
| `secUid` | String | 安全用户ID |
|
||||
| `nickname` | String | 账号名称 |
|
||||
| `avatar` | String | 头像URL |
|
||||
| `accountLink` | String | 账号链接 |
|
||||
| `followerCount` | Long | 总粉丝数 |
|
||||
| `fansIncrRate` | BigDecimal | 涨粉率 |
|
||||
| `addFollowerCount` | Integer | 粉丝增量 |
|
||||
|
||||
## 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": [
|
||||
{
|
||||
"ranking": 1,
|
||||
"category": "个人才艺",
|
||||
"listType": 1,
|
||||
"date": "2026-05-28",
|
||||
"secUid": "MS4wLjABAAAAxxxxxxxxxxxx",
|
||||
"nickname": "示例账号",
|
||||
"avatar": "https://example.com/avatar.jpg",
|
||||
"accountLink": "https://www.douyin.com/user/MS4wLjABAAAAxxxxxxxxxxxx",
|
||||
"followerCount": 12345678,
|
||||
"fansIncrRate": 0.15,
|
||||
"addFollowerCount": 50000
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 错误码
|
||||
|
||||
| code | 说明 |
|
||||
|------|------|
|
||||
| 200 | 成功 |
|
||||
| 500 | 业务异常,具体原因见 msg 字段 |
|
||||
|
||||
### 常见业务错误
|
||||
|
||||
| msg | 场景 |
|
||||
|-----|------|
|
||||
| 今日调用次数已达上限,请明日再试 | source 标识对应的每日调用次数已达上限 |
|
||||
|
||||
## 错误响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 500,
|
||||
"msg": "今日调用次数已达上限,请明日再试",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
## 支持的分类列表(27个)
|
||||
|
||||
全部、个人才艺、生活vlog、财富理财、二次元、居家装修、学习教育、小剧场、数码科技、旅行、美食、化妆美容、动物、亲子、汽车、情感、三农、健康医学、潮流风尚、舞蹈才艺、颜值造型、人文、音乐、影视、身体锻炼、体育、明星娱乐、游戏
|
||||
+777
@@ -0,0 +1,777 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""抖音涨粉账号推荐数据获取脚本
|
||||
|
||||
通过红狐数据API获取抖音涨粉榜数据,支持日榜/周榜/月榜,27个分类查询。
|
||||
使用原生 urllib 发起请求,API Key 从环境变量 REDFOX_API_KEY 读取。
|
||||
|
||||
用法:
|
||||
python scripts/douyin_rise_fans.py --rank_type day --keyword 科技 --top_n 50
|
||||
python scripts/douyin_rise_fans.py --rank_type week --category 数码科技
|
||||
python scripts/douyin_rise_fans.py --rank_type month --category 全部 --top_n 50
|
||||
python scripts/douyin_rise_fans.py --list_categories
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
from datetime import datetime, timedelta
|
||||
from urllib.parse import quote
|
||||
|
||||
# Windows 终端强制 UTF-8 输出,避免 GBK 编码错误
|
||||
if sys.platform == "win32":
|
||||
import io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace")
|
||||
|
||||
|
||||
# ===== 常量 =====
|
||||
API_URL = "https://redfox.hk/story/api/dyData/getDyRiseFansRank"
|
||||
SOURCE = "抖音涨粉账号推荐-GitHub"
|
||||
SKILL_ID = "7637816739572727835"
|
||||
|
||||
# Shell 配置文件候选列表(按优先级)
|
||||
SHELL_RC_FILES = [
|
||||
os.path.expanduser("~/.zshrc"),
|
||||
os.path.expanduser("~/.bashrc"),
|
||||
os.path.expanduser("~/.bash_profile"),
|
||||
os.path.expanduser("~/.profile"),
|
||||
]
|
||||
|
||||
|
||||
def _get_api_key():
|
||||
"""获取 REDFOX_API_KEY,优先级:环境变量 > shell 配置文件 > 提示配置"""
|
||||
# 1. 优先从当前环境变量获取
|
||||
key = os.environ.get("REDFOX_API_KEY", "").strip()
|
||||
if key:
|
||||
return key
|
||||
|
||||
# 2. 尝试从 shell 配置文件中读取
|
||||
import re
|
||||
pattern = re.compile(r'^\s*export\s+REDFOX_API_KEY\s*=\s*["\']?([^"\';\s]+)["\']?', re.MULTILINE)
|
||||
for rc_file in SHELL_RC_FILES:
|
||||
if os.path.isfile(rc_file):
|
||||
try:
|
||||
with open(rc_file, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
match = pattern.search(content)
|
||||
if match:
|
||||
key = match.group(1).strip()
|
||||
if key:
|
||||
os.environ["REDFOX_API_KEY"] = key
|
||||
return key
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
# 3. 未找到,提示用户配置
|
||||
raise ValueError(
|
||||
"未找到 REDFOX_API_KEY。\n"
|
||||
"请按以下方式配置:\n"
|
||||
" macOS/Linux:export REDFOX_API_KEY=ak_xxxxxxxx(追加到 ~/.zshrc 或 ~/.bashrc 后执行 source)\n"
|
||||
" Windows:[Environment]::SetEnvironmentVariable(\"REDFOX_API_KEY\", \"ak_xxxxxxxx\", \"User\")\n"
|
||||
"API Key 可在 https://redfox.hk?source=github 注册后于个人中心获取。"
|
||||
)
|
||||
|
||||
CATEGORIES = [
|
||||
"全部", "个人才艺", "生活vlog", "财富理财", "二次元", "居家装修",
|
||||
"学习教育", "小剧场", "数码科技", "旅行", "美食", "化妆美容",
|
||||
"动物", "亲子", "汽车", "情感", "三农", "健康医学",
|
||||
"潮流风尚", "舞蹈才艺", "颜值造型", "人文", "音乐", "影视",
|
||||
"身体锻炼", "体育", "明星娱乐", "游戏"
|
||||
]
|
||||
|
||||
RANK_TYPE_MAP = {
|
||||
"day": "日榜",
|
||||
"week": "周榜",
|
||||
"month": "月榜"
|
||||
}
|
||||
|
||||
DATE_TYPE_MAP = {
|
||||
"day": 1,
|
||||
"week": 2,
|
||||
"month": 3
|
||||
}
|
||||
|
||||
# 榜单更新时间规则
|
||||
DAY_UPDATE_HOUR = 18
|
||||
DAY_UPDATE_MINUTE = 0
|
||||
MONTH_UPDATE_DAY = 3
|
||||
MONTH_UPDATE_HOUR = 18
|
||||
MONTH_UPDATE_MINUTE = 0
|
||||
|
||||
|
||||
BILL_NOTE_TEMPLATES = {
|
||||
"day": "💡 榜单说明:抖音日榜每日18:00更新前一日数据,本次排名数据的获取时间为{fetch_date},与实时数据存在差异。",
|
||||
"week": "💡 榜单说明:抖音周榜每周一18:00更新前一周数据,本次排名数据的获取时间为{fetch_date},与实时数据存在差异。",
|
||||
"month": "💡 榜单说明:抖音月榜每月3号18:00更新上一月数据,本次排名数据的获取时间为{fetch_date},与实时数据存在差异。",
|
||||
}
|
||||
|
||||
# 日期查询范围限制
|
||||
DAY_MAX_DAYS_BACK = 7
|
||||
WEEK_MAX_WEEKS_BACK = 3
|
||||
MONTH_MAX_MONTHS_BACK = 3
|
||||
|
||||
|
||||
# ===== 日期计算 =====
|
||||
def _is_after_day_update(now=None):
|
||||
"""判断当前时间是否已过日榜/周榜更新时间(18:00)"""
|
||||
if now is None:
|
||||
now = datetime.now()
|
||||
cutoff = now.replace(hour=DAY_UPDATE_HOUR, minute=DAY_UPDATE_MINUTE, second=0, microsecond=0)
|
||||
return now >= cutoff
|
||||
|
||||
|
||||
def _is_after_month_update(now=None):
|
||||
"""判断当前时间是否已过月榜更新时间(当月3号18:00)"""
|
||||
if now is None:
|
||||
now = datetime.now()
|
||||
cutoff = now.replace(day=MONTH_UPDATE_DAY, hour=MONTH_UPDATE_HOUR,
|
||||
minute=MONTH_UPDATE_MINUTE, second=0, microsecond=0)
|
||||
return now >= cutoff
|
||||
|
||||
|
||||
def get_latest_query_date(rank_type="day"):
|
||||
"""获取最新可查询的榜单日期
|
||||
|
||||
Args:
|
||||
rank_type: day/week/month
|
||||
|
||||
Returns:
|
||||
str: 查询日期字符串 (yyyy-MM-dd)
|
||||
"""
|
||||
now = datetime.now()
|
||||
|
||||
if rank_type == "day":
|
||||
if _is_after_day_update(now):
|
||||
target = now - timedelta(days=1)
|
||||
else:
|
||||
target = now - timedelta(days=2)
|
||||
return target.strftime("%Y-%m-%d")
|
||||
|
||||
elif rank_type == "week":
|
||||
weekday = now.weekday()
|
||||
this_monday = now - timedelta(days=weekday)
|
||||
if _is_after_day_update(now):
|
||||
return this_monday.strftime("%Y-%m-%d")
|
||||
else:
|
||||
last_monday = this_monday - timedelta(weeks=1)
|
||||
return last_monday.strftime("%Y-%m-%d")
|
||||
|
||||
elif rank_type == "month":
|
||||
if _is_after_month_update(now):
|
||||
if now.month == 1:
|
||||
return datetime(now.year - 1, 12, 1).strftime("%Y-%m-%d")
|
||||
else:
|
||||
return datetime(now.year, now.month - 1, 1).strftime("%Y-%m-%d")
|
||||
else:
|
||||
if now.month == 1:
|
||||
return datetime(now.year - 1, 11, 1).strftime("%Y-%m-%d")
|
||||
elif now.month == 2:
|
||||
return datetime(now.year - 1, 12, 1).strftime("%Y-%m-%d")
|
||||
else:
|
||||
return datetime(now.year, now.month - 2, 1).strftime("%Y-%m-%d")
|
||||
|
||||
return now.strftime("%Y-%m-%d")
|
||||
|
||||
|
||||
def get_earliest_query_date(rank_type="day"):
|
||||
"""获取可查询的最早日期(限制范围)
|
||||
|
||||
Args:
|
||||
rank_type: day/week/month
|
||||
|
||||
Returns:
|
||||
str: 最早可查询日期字符串 (yyyy-MM-dd)
|
||||
"""
|
||||
now = datetime.now()
|
||||
|
||||
if rank_type == "day":
|
||||
earliest = now - timedelta(days=DAY_MAX_DAYS_BACK)
|
||||
return earliest.strftime("%Y-%m-%d")
|
||||
|
||||
elif rank_type == "week":
|
||||
weekday = now.weekday()
|
||||
this_monday = now - timedelta(days=weekday)
|
||||
earliest_monday = this_monday - timedelta(weeks=WEEK_MAX_WEEKS_BACK)
|
||||
return earliest_monday.strftime("%Y-%m-%d")
|
||||
|
||||
elif rank_type == "month":
|
||||
m = now.month
|
||||
y = now.year
|
||||
m -= MONTH_MAX_MONTHS_BACK
|
||||
while m <= 0:
|
||||
m += 12
|
||||
y -= 1
|
||||
return datetime(y, m, 1).strftime("%Y-%m-%d")
|
||||
|
||||
return now.strftime("%Y-%m-%d")
|
||||
|
||||
|
||||
def validate_and_adjust_date(rank_type, user_date_str):
|
||||
"""验证用户指定的日期是否在可查询范围内,超出范围则自动调整
|
||||
|
||||
Args:
|
||||
rank_type: day/week/month
|
||||
user_date_str: 用户指定的日期 (yyyy-MM-dd)
|
||||
|
||||
Returns:
|
||||
dict: {original_date, adjusted_date, is_adjusted, reminder}
|
||||
"""
|
||||
user_date = datetime.strptime(user_date_str, "%Y-%m-%d")
|
||||
latest = datetime.strptime(get_latest_query_date(rank_type), "%Y-%m-%d")
|
||||
earliest = datetime.strptime(get_earliest_query_date(rank_type), "%Y-%m-%d")
|
||||
|
||||
if earliest <= user_date <= latest:
|
||||
return {
|
||||
"original_date": user_date_str,
|
||||
"adjusted_date": user_date_str,
|
||||
"is_adjusted": False,
|
||||
"reminder": ""
|
||||
}
|
||||
|
||||
if user_date > latest:
|
||||
adjusted = latest.strftime("%Y-%m-%d")
|
||||
else:
|
||||
adjusted = earliest.strftime("%Y-%m-%d")
|
||||
|
||||
reminder = (
|
||||
'非常抱歉🙏,目前抖音榜单最多支持回溯「近7天的日榜/近3周的周榜/近3个月的月榜」,'
|
||||
'我已为您查询最接近您需求的时间范围~'
|
||||
)
|
||||
|
||||
return {
|
||||
"original_date": user_date_str,
|
||||
"adjusted_date": adjusted,
|
||||
"is_adjusted": True,
|
||||
"reminder": reminder
|
||||
}
|
||||
|
||||
|
||||
def get_data_time_range(rank_type, rank_date):
|
||||
"""根据榜单类型和查询日期生成数据统计时间周期描述"""
|
||||
import calendar
|
||||
date_obj = datetime.strptime(rank_date, "%Y-%m-%d")
|
||||
if rank_type == "day":
|
||||
return rank_date
|
||||
elif rank_type == "week":
|
||||
end_date = date_obj + timedelta(days=6)
|
||||
return f"{date_obj.strftime('%Y-%m-%d')}至{end_date.strftime('%Y-%m-%d')}"
|
||||
elif rank_type == "month":
|
||||
last_day = calendar.monthrange(date_obj.year, date_obj.month)[1]
|
||||
return f"{date_obj.strftime('%Y-%m')}-01至{date_obj.strftime('%Y-%m')}-{last_day:02d}"
|
||||
return rank_date
|
||||
|
||||
|
||||
def get_today_query_reminder(rank_type="day"):
|
||||
"""当用户查询今日日榜但数据未更新时,生成提醒"""
|
||||
if rank_type == "day" and not _is_after_day_update():
|
||||
yesterday = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")
|
||||
return (
|
||||
'日榜数据暂未更新,将为您查询最接近您需求日期的榜单数据~\n'
|
||||
f'推荐查询昨日更新的最新榜单({yesterday}日榜)'
|
||||
)
|
||||
return ""
|
||||
|
||||
|
||||
# ===== API调用 =====
|
||||
def fetch_ranking_data(rank_type="day", rank_date=None, category="全部"):
|
||||
"""获取抖音涨粉账号推荐数据
|
||||
|
||||
Args:
|
||||
rank_type: day/week/month
|
||||
rank_date: 查询日期 (yyyy-MM-dd),None则自动计算
|
||||
category: 分类名称
|
||||
|
||||
Returns:
|
||||
list: API返回的data数组
|
||||
"""
|
||||
if rank_date is None:
|
||||
rank_date = get_latest_query_date(rank_type)
|
||||
|
||||
# 获取凭证
|
||||
credential = _get_api_key()
|
||||
|
||||
date_type = DATE_TYPE_MAP.get(rank_type, 1)
|
||||
|
||||
payload = {
|
||||
"dateType": date_type,
|
||||
"rankDate": rank_date,
|
||||
"category": category,
|
||||
"source": "dy_rise_fans_rank"
|
||||
}
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-API-KEY": credential
|
||||
}
|
||||
|
||||
try:
|
||||
data = json.dumps(payload).encode("utf-8")
|
||||
req = urllib.request.Request(API_URL, data=data, headers=headers, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
if resp.status >= 400:
|
||||
raise ValueError(f"HTTP请求失败: {resp.status}")
|
||||
body = resp.read().decode("utf-8")
|
||||
result = json.loads(body)
|
||||
|
||||
if str(result.get("code")) not in ("200", "2000"):
|
||||
raise ValueError(f"API返回错误: code={result.get('code')}, msg={result.get('msg', '未知错误')}")
|
||||
|
||||
return result.get("data", [])
|
||||
|
||||
except urllib.error.URLError as e:
|
||||
raise ValueError(f"请求失败: {str(e)}")
|
||||
|
||||
|
||||
# ===== 分类匹配 =====
|
||||
CATEGORY_KEYWORDS = {
|
||||
"全部": ["全部", "综合", "全部", "热门", "推荐", "随便", "总榜", "整体"],
|
||||
"个人才艺": ["才艺", "个人", "技能", "展示"],
|
||||
"生活vlog": ["生活", "vlog", "日常", "VLOG", "Vlog"],
|
||||
"财富理财": ["财富", "理财", "投资", "基金", "股票", "保险", "财务", "赚钱"],
|
||||
"二次元": ["二次元", "动漫", "ACG", "cosplay", "Cosplay"],
|
||||
"居家装修": ["居家", "装修", "家居", "装饰", "房子装修"],
|
||||
"学习教育": ["教育", "学习", "考试", "培训", "考研", "留学", "英语"],
|
||||
"小剧场": ["小剧场", "短剧", "剧情", "剧场"],
|
||||
"数码科技": ["数码", "科技", "手机", "电脑", "智能", "AI", "互联网", "软件"],
|
||||
"旅行": ["旅行", "旅游", "出行", "攻略", "景点", "酒店", "度假"],
|
||||
"美食": ["美食", "餐饮", "做饭", "烹饪", "餐厅", "探店", "食谱", "吃"],
|
||||
"化妆美容": ["化妆", "美容", "护肤", "美妆", "彩妆", "美体"],
|
||||
"动物": ["动物", "宠物", "猫", "狗", "萌宠"],
|
||||
"亲子": ["亲子", "育儿", "宝宝", "儿童", "母婴"],
|
||||
"汽车": ["汽车", "车", "新能源", "电动车", "买车"],
|
||||
"情感": ["情感", "恋爱", "婚姻", "情绪", "心理"],
|
||||
"三农": ["三农", "农村", "农业", "农民", "种植"],
|
||||
"健康医学": ["健康", "医学", "养生", "保健", "中医", "医疗"],
|
||||
"潮流风尚": ["潮流", "时尚", "穿搭", "服饰", "OOTD", "ootd", "搭配"],
|
||||
"舞蹈才艺": ["舞蹈", "跳舞", "舞"],
|
||||
"颜值造型": ["颜值", "造型", "美", "颜值博主"],
|
||||
"人文": ["人文", "文化", "历史", "哲学", "人文社科"],
|
||||
"音乐": ["音乐", "唱歌", "乐器", "歌曲"],
|
||||
"影视": ["影视", "电影", "电视", "剧集", "综艺"],
|
||||
"身体锻炼": ["锻炼", "健身", "运动", "减肥", "瘦身", "体能"],
|
||||
"体育": ["体育", "篮球", "足球", "运动赛事"],
|
||||
"明星娱乐": ["明星", "娱乐", "娱乐圈", "偶像"],
|
||||
"游戏": ["游戏", "电竞", "手游", "端游", "主机"]
|
||||
}
|
||||
|
||||
|
||||
def match_category(keyword):
|
||||
"""根据关键词匹配分类
|
||||
|
||||
Args:
|
||||
keyword: 搜索关键词
|
||||
|
||||
Returns:
|
||||
str or None or list: 匹配到的分类名
|
||||
"""
|
||||
if keyword in CATEGORIES:
|
||||
return keyword
|
||||
matches = [c for c in CATEGORIES if keyword in c]
|
||||
if len(matches) == 1:
|
||||
return matches[0]
|
||||
if len(matches) > 1:
|
||||
return matches
|
||||
# 关键词模糊匹配
|
||||
keyword_lower = keyword.lower().strip()
|
||||
for category, keywords in CATEGORY_KEYWORDS.items():
|
||||
for kw in keywords:
|
||||
if kw.lower() in keyword_lower or keyword_lower in kw.lower():
|
||||
return category
|
||||
return None
|
||||
|
||||
|
||||
# ===== 数据格式化 =====
|
||||
def format_number(num):
|
||||
"""格式化数字:超过1万显示x.x万,超过1亿显示x.x亿"""
|
||||
try:
|
||||
num = int(num)
|
||||
except (ValueError, TypeError):
|
||||
return str(num)
|
||||
if num >= 100000000:
|
||||
return f"{num / 100000000:.1f}亿"
|
||||
if num >= 10000:
|
||||
return f"{num / 10000:.1f}万"
|
||||
return str(num)
|
||||
|
||||
|
||||
def format_rate(rate):
|
||||
"""格式化涨粉率:0.15 -> 15.0%"""
|
||||
try:
|
||||
return f"{float(rate) * 100:.1f}%"
|
||||
except (ValueError, TypeError):
|
||||
return str(rate)
|
||||
|
||||
|
||||
def format_ranking_table(data_list, top_n=50, start=1):
|
||||
"""格式化榜单为Markdown表格
|
||||
|
||||
表格字段: 排名 | 账号名称 | 总粉丝数 | 涨粉率 | 粉丝增量
|
||||
|
||||
Args:
|
||||
data_list: API返回的data数组
|
||||
top_n: 显示前N条
|
||||
start: 起始排名(1-based)
|
||||
|
||||
Returns:
|
||||
str: Markdown表格字符串
|
||||
"""
|
||||
items = data_list[start-1:start-1+top_n]
|
||||
|
||||
header = "| 排名 | 账号名称 | 总粉丝数 | 涨粉率 | 粉丝增量 |"
|
||||
separator = "| ---: | :--- | ---: | ---: | ---: |"
|
||||
|
||||
rows = []
|
||||
for item in items:
|
||||
rank = item.get("ranking", "-")
|
||||
name = item.get("nickname", "-")
|
||||
account_link = item.get("accountLink", "")
|
||||
if account_link:
|
||||
name_link = f"[{name}]({account_link})"
|
||||
else:
|
||||
name_link = name
|
||||
follower_count = format_number(item.get("followerCount", 0))
|
||||
fans_incr_rate = format_rate(item.get("fansIncrRate", 0))
|
||||
add_follower = format_number(item.get("addFollowerCount", 0))
|
||||
|
||||
rank_medals = {1: "🥇", 2: "🥈", 3: "🥉"}
|
||||
if isinstance(rank, int) and rank in rank_medals:
|
||||
row = f"| {rank_medals[rank]} | **{name_link}** | **{follower_count}** | **{fans_incr_rate}** | **{add_follower}** |"
|
||||
else:
|
||||
row = f"| {rank} | {name_link} | {follower_count} | {fans_incr_rate} | {add_follower} |"
|
||||
rows.append(row)
|
||||
|
||||
return "\n".join([header, separator] + rows)
|
||||
|
||||
|
||||
def _safe_float(val, default=0.0):
|
||||
"""安全转换为float"""
|
||||
try:
|
||||
return float(val)
|
||||
except (ValueError, TypeError):
|
||||
return default
|
||||
|
||||
|
||||
def _median(values):
|
||||
"""计算中位数"""
|
||||
if not values:
|
||||
return 0.0
|
||||
sorted_vals = sorted(values)
|
||||
n = len(sorted_vals)
|
||||
mid = n // 2
|
||||
if n % 2 == 0:
|
||||
return (sorted_vals[mid - 1] + sorted_vals[mid]) / 2
|
||||
return sorted_vals[mid]
|
||||
|
||||
|
||||
def format_analysis(data_list, rank_type="day", top_n=50):
|
||||
"""格式化涨粉推荐分析(3维度结构化输出)
|
||||
|
||||
维度一:涨粉态势概览(全量数据统计)
|
||||
维度二:增长模式分析(分层对比洞察)
|
||||
维度三:运营洞察建议(可执行决策参考)
|
||||
|
||||
Args:
|
||||
data_list: API返回的data数组
|
||||
rank_type: day/week/month
|
||||
top_n: 分析前N个账号
|
||||
|
||||
Returns:
|
||||
str: Markdown分析文本
|
||||
"""
|
||||
rank_label = RANK_TYPE_MAP.get(rank_type, "日榜")
|
||||
items = data_list[:top_n]
|
||||
|
||||
if not items:
|
||||
return "暂无数据可供分析。"
|
||||
|
||||
lines = [f"### 抖音涨粉账号推荐{rank_label}分析\n"]
|
||||
lines.append("以下内容分析基于获取的全部数据(TOP50)进行~\n")
|
||||
|
||||
# ===== 维度一:涨粉态势概览 =====
|
||||
rates = [_safe_float(item.get("fansIncrRate", 0)) for item in items]
|
||||
increments = [_safe_float(item.get("addFollowerCount", 0)) for item in items]
|
||||
|
||||
avg_rate = sum(rates) / len(rates) if rates else 0
|
||||
median_rate = _median(rates)
|
||||
total_increment = sum(increments)
|
||||
top10_increment = sum(sorted(increments, reverse=True)[:min(10, len(increments))])
|
||||
concentration = (top10_increment / total_increment * 100) if total_increment > 0 else 0
|
||||
|
||||
# 增量分层
|
||||
tier_10w = sum(1 for v in increments if v >= 100000)
|
||||
tier_5w = sum(1 for v in increments if 50000 <= v < 100000)
|
||||
tier_1w = sum(1 for v in increments if 10000 <= v < 50000)
|
||||
tier_below = sum(1 for v in increments if v < 10000)
|
||||
|
||||
lines.append("#### 一、涨粉态势概览(全量数据统计)")
|
||||
lines.append(f"- **平均涨粉率**:{avg_rate * 100:.1f}%")
|
||||
lines.append(f"- **中位数涨粉率**:{median_rate * 100:.1f}%")
|
||||
if avg_rate > median_rate * 1.5:
|
||||
lines.append(f" > 平均值远高于中位数,说明涨粉分化严重,少数高增长账号拉高了整体水平")
|
||||
lines.append(f"- **增量集中度**:TOP10占比{concentration:.0f}%" + ("(头部集中度高)" if concentration > 50 else "(分布相对均衡)"))
|
||||
lines.append(f"- **增量分层分布**:10万+ {tier_10w}个 | 5-10万 {tier_5w}个 | 1-5万 {tier_1w}个 | 1万以下 {tier_below}个")
|
||||
lines.append("")
|
||||
|
||||
# ===== 维度二:增长模式分析 =====
|
||||
# 按粉丝基数分3层
|
||||
tier_head = [item for item in items if _safe_float(item.get("followerCount", 0)) >= 5000000]
|
||||
tier_mid = [item for item in items if 1000000 <= _safe_float(item.get("followerCount", 0)) < 5000000]
|
||||
tier_tail = [item for item in items if _safe_float(item.get("followerCount", 0)) < 1000000]
|
||||
|
||||
def _tier_stats(tier_items, tier_name):
|
||||
if not tier_items:
|
||||
return f"- **{tier_name}**:0个账号"
|
||||
tier_rates = [_safe_float(it.get("fansIncrRate", 0)) for it in tier_items]
|
||||
tier_increments = [_safe_float(it.get("addFollowerCount", 0)) for it in tier_items]
|
||||
avg_r = sum(tier_rates) / len(tier_rates) * 100
|
||||
avg_inc = sum(tier_increments) / len(tier_increments)
|
||||
# 找该层涨粉率最高的账号
|
||||
best = max(tier_items, key=lambda x: _safe_float(x.get("fansIncrRate", 0)))
|
||||
best_name = best.get("nickname", "未知")
|
||||
best_rate = format_rate(best.get("fansIncrRate", 0))
|
||||
return (f"- **{tier_name}**:{len(tier_items)}个账号,平均涨粉率{avg_r:.1f}%,"
|
||||
f"平均粉丝增量{format_number(int(avg_inc))},涨粉率最高:{best_name}({best_rate})")
|
||||
|
||||
lines.append("#### 二、增长模式分析(分层对比洞察)")
|
||||
lines.append("")
|
||||
lines.append(_tier_stats(tier_head, "头部大号(≥500万)"))
|
||||
lines.append(_tier_stats(tier_mid, "中腰部号(100-500万)"))
|
||||
lines.append(_tier_stats(tier_tail, "尾部小号(<100万)"))
|
||||
lines.append("")
|
||||
|
||||
# 判断增长模式
|
||||
head_avg_rate = sum(_safe_float(it.get("fansIncrRate", 0)) for it in tier_head) / len(tier_head) if tier_head else 0
|
||||
tail_avg_rate = sum(_safe_float(it.get("fansIncrRate", 0)) for it in tier_tail) / len(tier_tail) if tier_tail else 0
|
||||
|
||||
if tail_avg_rate > head_avg_rate * 3 and tail_avg_rate > 0.1:
|
||||
mode = "小号爆发型"
|
||||
mode_desc = "尾部小号涨粉率远超头部,增长主要由新锐账号驱动"
|
||||
elif head_avg_rate >= tail_avg_rate * 0.7 and head_avg_rate > 0:
|
||||
mode = "大号稳增型"
|
||||
mode_desc = "头部大号保持稳定增长,粉丝基数仍是增量主力"
|
||||
else:
|
||||
mode = "混合增长型"
|
||||
mode_desc = "各层级账号均有增长动力,无单一主导模式"
|
||||
lines.append(f"- **主导增长模式**:{mode} — {mode_desc}")
|
||||
lines.append("")
|
||||
|
||||
# ===== 维度三:运营洞察建议 =====
|
||||
lines.append("#### 三、运营洞察建议(可执行决策参考)")
|
||||
lines.append("")
|
||||
|
||||
# 涨粉标杆:涨粉率TOP3 且 粉丝增量TOP10
|
||||
by_rate = sorted(items, key=lambda x: _safe_float(x.get("fansIncrRate", 0)), reverse=True)
|
||||
top3_by_rate = by_rate[:3]
|
||||
top10_by_increment = sorted(items, key=lambda x: _safe_float(x.get("addFollowerCount", 0)), reverse=True)[:10]
|
||||
top10_names = {it.get("nickname") for it in top10_by_increment}
|
||||
benchmarks = [it for it in top3_by_rate if it.get("nickname") in top10_names]
|
||||
if not benchmarks:
|
||||
benchmarks = top3_by_rate[:2] # fallback
|
||||
|
||||
lines.append("**🎯 涨粉标杆(值得研究的账号)**")
|
||||
for it in benchmarks:
|
||||
name = it.get("nickname", "未知")
|
||||
r = format_rate(it.get("fansIncrRate", 0))
|
||||
inc = format_number(it.get("addFollowerCount", 0))
|
||||
rate_val = _safe_float(it.get("fansIncrRate", 0))
|
||||
fc = _safe_float(it.get("followerCount", 0))
|
||||
if rate_val >= 0.3:
|
||||
driver = "爆款内容/热点事件驱动"
|
||||
elif rate_val >= 0.1:
|
||||
driver = "内容策略优化/话题借势"
|
||||
elif fc >= 5000000:
|
||||
driver = "粉丝基数驱动/品牌营销"
|
||||
else:
|
||||
driver = "稳步增长"
|
||||
lines.append(f"- {name}:涨粉率{r},粉丝增量{inc},增长驱动力:{driver}")
|
||||
lines.append("")
|
||||
|
||||
# 潜力账号:粉丝基数<100万 且 涨粉率>10% 且 粉丝增量>3万
|
||||
potentials = [
|
||||
it for it in items
|
||||
if _safe_float(it.get("followerCount", 0)) < 1000000
|
||||
and _safe_float(it.get("fansIncrRate", 0)) > 0.1
|
||||
and _safe_float(it.get("addFollowerCount", 0)) > 30000
|
||||
]
|
||||
# 按涨粉率降序
|
||||
potentials.sort(key=lambda x: _safe_float(x.get("fansIncrRate", 0)), reverse=True)
|
||||
|
||||
lines.append("**🚀 潜力账号(适合合作/投放)**")
|
||||
if potentials:
|
||||
for it in potentials[:5]: # 最多展示5个
|
||||
name = it.get("nickname", "未知")
|
||||
fc = format_number(it.get("followerCount", 0))
|
||||
r = format_rate(it.get("fansIncrRate", 0))
|
||||
inc = format_number(it.get("addFollowerCount", 0))
|
||||
lines.append(f"- {name}:总粉丝{fc},涨粉率{r},粉丝增量{inc},推荐理由:高增长势头+中等粉丝基数性价比高")
|
||||
else:
|
||||
lines.append("- 本期无符合筛选条件的潜力账号")
|
||||
lines.append("")
|
||||
|
||||
# 风险信号:涨粉率<1% 且 排名TOP30以后
|
||||
risks = [
|
||||
it for it in items
|
||||
if _safe_float(it.get("fansIncrRate", 0)) < 0.01
|
||||
and _safe_float(it.get("ranking", 0)) > 30
|
||||
]
|
||||
|
||||
lines.append("**⚠️ 风险信号(需关注异常)**")
|
||||
if risks:
|
||||
for it in risks[:3]: # 最多展示3个
|
||||
name = it.get("nickname", "未知")
|
||||
r = format_rate(it.get("fansIncrRate", 0))
|
||||
fc = _safe_float(it.get("followerCount", 0))
|
||||
if fc >= 5000000:
|
||||
reason = "粉丝基数过大导致增长放缓"
|
||||
else:
|
||||
reason = "可能存在内容断更/舆情等问题"
|
||||
lines.append(f"- {name}:涨粉率{r},{reason}")
|
||||
else:
|
||||
lines.append("- 本期无显著风险信号")
|
||||
lines.append("")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def get_update_time_label(rank_type, rank_date):
|
||||
"""获取榜单更新时间标签"""
|
||||
if rank_type == "day":
|
||||
d = datetime.strptime(rank_date, "%Y-%m-%d")
|
||||
update_time = d + timedelta(days=1)
|
||||
return f"{update_time.strftime('%Y-%m-%d')} 18:00"
|
||||
elif rank_type == "week":
|
||||
d = datetime.strptime(rank_date, "%Y-%m-%d")
|
||||
next_monday = d + timedelta(weeks=1)
|
||||
return f"{next_monday.strftime('%Y-%m-%d')} 18:00"
|
||||
elif rank_type == "month":
|
||||
d = datetime.strptime(rank_date, "%Y-%m-%d")
|
||||
if d.month == 12:
|
||||
update_day = datetime(d.year + 1, 1, MONTH_UPDATE_DAY)
|
||||
else:
|
||||
update_day = datetime(d.year, d.month + 1, MONTH_UPDATE_DAY)
|
||||
return f"{update_day.strftime('%Y-%m-%d')} 18:00"
|
||||
return rank_date
|
||||
|
||||
|
||||
# ===== 主函数 =====
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="抖音涨粉账号推荐数据获取工具")
|
||||
parser.add_argument("--rank_type", choices=["day", "week", "month"], default="day",
|
||||
help="榜单类型: day=日榜, week=周榜, month=月榜 (默认: day)")
|
||||
parser.add_argument("--rank_date", type=str, default=None,
|
||||
help="查询日期(yyyy-MM-dd),不指定则自动计算")
|
||||
parser.add_argument("--category", type=str, default=None,
|
||||
help="分类名称,如'数码科技'")
|
||||
parser.add_argument("--keyword", type=str, default=None,
|
||||
help="分类关键词,模糊匹配分类名")
|
||||
parser.add_argument("--top_n", type=int, default=50,
|
||||
help="显示前N条 (默认: 50)")
|
||||
parser.add_argument("--start", type=int, default=1,
|
||||
help="起始排名 (默认: 1,用于查看更多时从TOP21开始)")
|
||||
parser.add_argument("--list_categories", action="store_true",
|
||||
help="列出所有可用分类")
|
||||
parser.add_argument("--raw", action="store_true",
|
||||
help="输出原始JSON数据")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# 列出分类
|
||||
if args.list_categories:
|
||||
print("可用分类列表:")
|
||||
for i, cat in enumerate(CATEGORIES, 1):
|
||||
print(f" {i}. {cat}")
|
||||
return
|
||||
|
||||
# 确定分类
|
||||
category = "全部"
|
||||
if args.category:
|
||||
category = args.category
|
||||
elif args.keyword:
|
||||
matched = match_category(args.keyword)
|
||||
if matched is None:
|
||||
print(f"未找到匹配'{args.keyword}'的分类,可用分类:{', '.join(CATEGORIES)}")
|
||||
return
|
||||
elif isinstance(matched, list):
|
||||
print(f"关键词'{args.keyword}'匹配到多个分类:{', '.join(matched)},请指定更精确的分类名")
|
||||
return
|
||||
else:
|
||||
category = matched
|
||||
print(f"根据关键词【{args.keyword}】匹配到分类:【{category}】")
|
||||
|
||||
# 确定查询日期
|
||||
rank_label = RANK_TYPE_MAP.get(args.rank_type, "日榜")
|
||||
print(f"正在获取抖音涨粉账号推荐{rank_label}数据...")
|
||||
print(f"当前时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
|
||||
reminder = ""
|
||||
|
||||
if args.rank_date:
|
||||
validation = validate_and_adjust_date(args.rank_type, args.rank_date)
|
||||
rank_date = validation["adjusted_date"]
|
||||
if validation["is_adjusted"]:
|
||||
reminder = validation["reminder"]
|
||||
print(f"用户指定日期: {args.rank_date} -> 调整为: {rank_date}")
|
||||
print(f"提醒: {reminder}")
|
||||
else:
|
||||
rank_date = get_latest_query_date(args.rank_type)
|
||||
today_reminder = get_today_query_reminder(args.rank_type)
|
||||
if today_reminder:
|
||||
reminder = today_reminder
|
||||
print(f"提醒: {reminder}")
|
||||
|
||||
print(f"榜单类型: {rank_label}")
|
||||
print(f"查询日期: {rank_date}")
|
||||
print(f"分类: {category}")
|
||||
print("-" * 60)
|
||||
|
||||
try:
|
||||
data_list = fetch_ranking_data(
|
||||
rank_type=args.rank_type,
|
||||
rank_date=rank_date,
|
||||
category=category,
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"错误: {e}")
|
||||
return
|
||||
|
||||
if not data_list:
|
||||
print("未获取到数据")
|
||||
return
|
||||
|
||||
# 格式化输出
|
||||
update_time = get_update_time_label(args.rank_type, rank_date)
|
||||
time_range = get_data_time_range(args.rank_type, rank_date)
|
||||
fetch_date = rank_date
|
||||
data_description = BILL_NOTE_TEMPLATES.get(args.rank_type, BILL_NOTE_TEMPLATES["day"]).format(fetch_date=fetch_date)
|
||||
|
||||
output = {
|
||||
"status": "success",
|
||||
"rank_type": args.rank_type,
|
||||
"rank_label": rank_label,
|
||||
"rank_date": rank_date,
|
||||
"category": category,
|
||||
"data_description": data_description,
|
||||
"total_count": len(data_list),
|
||||
"top_n": min(args.top_n, len(data_list)),
|
||||
"update_time": update_time,
|
||||
"time_range": time_range,
|
||||
"reminder": reminder,
|
||||
"ranking_table": format_ranking_table(data_list, args.top_n, args.start),
|
||||
"analysis": format_analysis(data_list, args.rank_type),
|
||||
"account_list": data_list,
|
||||
}
|
||||
|
||||
# 原始输出
|
||||
if args.raw:
|
||||
print(json.dumps(output, ensure_ascii=False, indent=2))
|
||||
return
|
||||
|
||||
print(json.dumps(output, ensure_ascii=False))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user