wechat-top-account
公众号热门账号推荐快速获取微信公众号综合实力排行榜TOP50,支持按日榜/周榜/月榜及垂直领域筛选,展示综合评分以及详细数据。支持深度分析单个账号、导出HTML可视化报告、订阅日/周/月榜推送。当用户需要查询公众号实力排行、按时间或领域筛选、查看互动指标、获取榜单解读、导出图表或接收定期推送时使用。
Install
npx skills add https://github.com/redfox-data/redfox-community/tree/main/skills/wechat-top-account
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install redfox-data-redfox-community@llmmart
git clone https://github.com/redfox-data/redfox-community.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole redfox-data/redfox-community collection as a plugin from our marketplace. Git is the plain clone.
README
公众号热门账号推荐 / wechat-top-account
简介
快速获取微信公众号综合实力排行榜 TOP50,帮助运营者和品牌方掌握同赛道头部账号的阅读与互动数据,支撑运营对标与竞品跟踪。
核心价值
- 覆盖 23 个垂直分类的 TOP50 榜单,支持日榜、周榜、月榜自由切换
- 综合评分体系基于阅读、点赞、在看、转发等多项指标,满分 100 分
- 自动输出内容生态变化、热点影响、蓝海赛道三类深度解读
- 一键生成可视化页面报告,支持导出图片和 PDF
- 支持订阅日榜、周榜、月榜,按更新时间自动推送
适用对象
- 📱 公众号运营者 — 了解赛道头部账号表现,优化选题与发布节奏
- 🏢 MCN 机构运营 — 统一监控旗下账号竞争格局,生成定期数据汇报
- 🏷️ 品牌营销经理 — 跟踪竞品公众号排名与互动变化,调整内容策略
- 🚀 内容创业者 — 评估候选赛道竞争度与阅读需求,选择蓝海方向
功能特性
核心功能
- 多维排行查询:按日榜、周榜、月榜查询 23 个分类的 TOP50,输入关键词自动匹配分类
- 综合评分体系:基于发布数、阅读量、点赞、在看、转发等多维度综合评分,满分 100
- 深度分析解读:提供内容生态变化、热点影响力量化、蓝海赛道挖掘三种分析视角
- 可视化报告导出:一键生成榜单可视化页面,支持导出图片和 PDF
- 定时订阅推送:支持订阅日榜、周榜、月榜,按更新时间自动推送最新数据
特色亮点
- 定时更新:日榜每日 17:30、周榜每周一 17:30、月榜每月 3 日 23:00 自动刷新
- 有限回溯:日榜近 7 天、周榜近 3 周、月榜近 3 个月,超出范围自动调整并提示
- 数据一致:榜单表格与可视化报告共用同一数据来源,顺序与内容完全一致
- 按需扩展:榜单直接输出无需确认,分析、报告、订阅由用户自主选择
密钥获取与安全说明
- 本技能需要使用环境变量:
REDFOX_API_KEY。 REDFOX_API_KEY由 红狐 hub (https://redfox.hk?source=github)提供。- 请前往 红狐 hub 注册账号,获取
REDFOX_API_KEY。 - 配置设备环境变量
REDFOX_API_KEY后使用本技能。 - 在提供密钥前,请先确认密钥来源、可用范围、有效期及是否支持重置/撤销。
- 禁止在代码、提示词、日志或输出文件中硬编码/明文暴露密钥。
使用指南
直接用自然语言描述需求,无需记忆命令。
常用说法速查
| 意图 | 示例话术 | 效果 |
|---|---|---|
| 查询分类日榜 | 查询科技类公众号日榜 | 输出科技数码分类日榜 TOP50,含综合评分与互动指标 |
| 查询分类周榜 | 本周美食类公众号排名 | 输出美食餐饮周榜 TOP50,标注数据时间周期 |
| 查询分类月榜 | 财经类公众号月榜 | 输出财经类月榜 TOP50 |
| 获取深度分析 | (榜单输出后回复)1 | 基于 TOP50 输出内容生态、热点影响、蓝海赛道三类分析 |
| 生成可视化报告 | (榜单输出后回复)2 | 生成可导出图片和 PDF 的榜单页面 |
| 订阅推送 | 订阅周榜 | 设置每周定时推送最新榜单 |
| 全部订阅 | 全部订阅 | 同时订阅日榜、周榜、月榜 |
输出示例
查询「科技类公众号日榜」后,你将收到:
- 榜单表格:排名、账号名称(可点击跳转)、综合评分、发布数、总阅读、头条阅读、最高阅读、总点赞、总在看、总转发
- 扩展功能提示:回复 1 获取深度分析、回复 2 生成可视化报告、回复 3 或说「订阅日/周/月榜」开启定时推送
使用场景
| 场景 | 角色 | 示例问法 | 收益 |
|---|---|---|---|
| 日常运营参考 | 公众号运营者 | 查询科技类公众号日榜 | 对比发布频率与互动率,明确对标账号,优化选题节奏 |
| 竞品追踪 | 品牌营销经理 | 本周快消类公众号排名 | 跟踪竞品排名波动与内容发力方向,调整自身策略 |
| 赛道研判 | 内容创业者 | 对比美食和旅游类月榜 | 评估候选赛道竞争度,选择蓝海方向切入 |
| 定期数据汇报 | MCN 机构运营 | 订阅月榜 | 按月导出可视化报告,数据口径统一可追溯 |
重要数据说明
| 榜单 | 更新时间 | 数据范围 |
|---|---|---|
| 日榜 | 每日 17:30 | 昨日数据,近 7 天 |
| 周榜 | 每周一 17:30 | 上周数据,近 3 周 |
| 月榜 | 每月 3 日 23:00 | 上月数据,近 3 个月 |
共覆盖 23 个垂直分类,包括总排名、科技数码、美食餐饮、财经、教育考试、时尚潮流、健康养生、情感心理、职场发展等。输入关键词即可自动匹配对应分类。
Skill manifest
公众号热门账号推荐
简介
公众号热门账号推荐是一款微信公众号综合实力排行查询工具,帮助用户快速获取同赛道头部账号的阅读与互动数据,支撑运营对标与竞品跟踪。
通过对话或命令行,你可以:
- 📊 查询 23 个垂直分类的 TOP50 榜单,支持日榜、周榜、月榜切换
- 📈 查看综合评分(满分 100)及发布数、阅读分布、点赞、在看、转发等指标
- 🔍 获取内容生态、热点影响、蓝海赛道三类数据解读
- 📄 导出 HTML 可视化报告(支持图片/PDF)
- 🔔 订阅日/周/月榜,按更新时间自动推送
适用于公众号运营者、MCN 机构、品牌营销人员及需跟踪公众号竞争格局的内容团队。
功能特性
🎯 核心功能
- 📊 多维排行查询:按日榜、周榜、月榜查询 23 个分类 TOP50,输入关键词自动匹配分类
- 📈 综合评分体系:基于阅读、点赞、在看、转发等指标计算综合评分,满分 100
- 🔍 深度分析:提供内容生态变化、热点影响力量化、蓝海赛道挖掘三种解读视角
- 📄 HTML 报告:生成可视化榜单页面,支持导出图片和 PDF
- 🔔 定时订阅:支持订阅日榜、周榜、月榜,按更新时间自动推送
✨ 特色亮点
- ⚡ 定时更新:日榜每日 17:30、周榜每周一 17:30、月榜每月 3 日 23:00 自动刷新
- 📅 有限回溯:日榜近 7 天、周榜近 3 周、月榜近 3 个月,超出范围自动调整并提示
- 🔗 数据一致:榜单表格与 HTML 报告共用同一 API,顺序与内容完全一致
- 🔒 API 接入:通过红狐Hub API Key 调用
鉴权
获取 API Key
配置 API Key
REDFOX_API_KEY 从环境变量获取,格式 ak_xxxxxxxx。脚本采用三级认证回退:
| 优先级 | 来源 | 说明 |
|---|---|---|
| 1 | 环境变量 REDFOX_API_KEY |
直接读取当前会话的环境变量 |
| 2 | Shell 配置文件 | 自动读取 ~/.zshrc / ~/.bashrc / ~/.bash_profile / PowerShell Profile 中的配置 |
| 3 | 用户输入 | 若以上均未检测到,提示用户手动配置 |
macOS / Linux:
echo 'export REDFOX_API_KEY=<你的apikey>' >> ~/.zshrc
source ~/.zshrc
Windows(PowerShell):
[Environment]::SetEnvironmentVariable("REDFOX_API_KEY", "<你的apikey>", "User")
或将以下内容写入 PowerShell Profile:
$env:REDFOX_API_KEY = "<你的apikey>"
配置后需重启终端。验证:echo $REDFOX_API_KEY(macOS/Linux)或 $env:REDFOX_API_KEY(Windows PowerShell)。
一键安装
前置条件
- Python 3.8 及以上版本
requests库:pip install requests- 红狐Hub API Key,格式
ak_xxxxxxxx
安装方式
SkillHub
- 前往 SkillHub
- 搜索 wechat-top-account 或 公众号热门账号推荐
- 安装说明
- 可通过「通过对话安装」方式安装
- 如未安装 SkillHub 商店,先安装商店再安装 skill
- 支持下载 Zip 包到本地,通过本地导入方式安装
ClawHub
- 前往 ClawHub
- 搜索 wechat-top-account 或 公众号热门账号推荐
- 安装说明
- 支持使用 CLI 命令安装:
openclaw skills install wechat-top-account或clawhub install wechat-top-account - 支持下载 skill 包到本地并导入安装
- 安装成功后可在对应客户端直接使用
安装完成后,请先完成 鉴权 配置 API Key,重启终端即可在对话中发起查询。
方式二:命令行独立使用
无需安装技能包,配置好 API Key 后可直接运行 scripts/ 下脚本:
pip install requests
python scripts/gzh_growth_fetcher.py --list_categories
环境变量配置
| 变量名 | 必填 | 说明 |
|---|---|---|
REDFOX_API_KEY |
是 | 红狐Hub API 访问密钥,格式 ak_xxxxxxxx |
详细获取与配置步骤见上方 鉴权 章节。
使用指南
基础使用
1. 发起榜单查询
在对话中输入查询需求,系统自动识别榜单类型和分类:
用户:查询科技类公众号日榜
助手:输出科技数码分类日榜 TOP50 表格(含综合评分、阅读与互动指标),并附功能询问文案。
2. 查看榜单结果
榜单数据直接输出,无需确认。每条结果包含排名、账号名称(可跳转)、综合评分、发布数/文章数、总阅读、头条阅读、最高阅读、总点赞、总在看、总转发。
用户:本周美食类公众号排名
助手:输出美食餐饮周榜 TOP50,标注数据时间周期与更新时间。
3. 选择扩展功能
榜单输出后,可按需选择后续服务:
用户:1
助手:基于 TOP50 数据输出内容生态变化、热点影响、蓝海赛道三类分析。
回复 1 获取分析、2 生成 HTML 报告、3 或「订阅日/周/月榜」开启定时推送。
高级使用
1. 指定分类与日期
通过 --category 或 --keyword 精确匹配分类,--rank_date 指定查询日期(超出范围自动调整):
# 首次查询时必须同时保存到临时文件(唯一一次API调用)
python scripts/gzh_growth_fetcher.py --rank_type week --category "美食餐饮" --top_n 50 > /tmp/gzh_result.json 2>/dev/null
python scripts/gzh_growth_fetcher.py --rank_type month --keyword "财经" --rank_date 2026-04-01 --top_n 50 > /tmp/gzh_result.json 2>/dev/null
2. 生成 HTML 可视化报告
用户选择 HTML 功能后,使用已保存的数据生成可导出图片/PDF 的页面(不调用API):
python scripts/gen_gzh_html.py --input_json_file /tmp/gzh_result.json --top 50
3. 获取深度分析解读
用户确认后,基于当前 TOP50 全量数据输出三段式分析:内容生态变化、热点影响力量化、蓝海赛道挖掘。
4. 订阅定时推送
用户回复「订阅日榜」「订阅周榜」「订阅月榜」或「全部订阅」,系统将按对应更新时间推送最新榜单。
完整参数、输出模板与日期校验逻辑见 核心工作流程。
常用命令速查
| 命令 | 功能 |
|---|---|
python scripts/gzh_growth_fetcher.py --rank_type day --keyword "科技" --top_n 50 > /tmp/gzh_result.json 2>/dev/null |
科技类日榜 TOP50(首次查询,同时保存文件) |
python scripts/gzh_growth_fetcher.py --rank_type week --category "美食餐饮" --top_n 50 > /tmp/gzh_result.json 2>/dev/null |
美食餐饮周榜 TOP50(首次查询,同时保存文件) |
python scripts/gzh_growth_fetcher.py --rank_type month --keyword "财经" --top_n 50 > /tmp/gzh_result.json 2>/dev/null |
财经类月榜 TOP50(首次查询,同时保存文件) |
python scripts/gzh_growth_fetcher.py --list_categories |
列出全部可用分类 |
python scripts/gen_gzh_html.py --input_json_file /tmp/gzh_result.json --top 50 |
用预取数据生成 HTML(不调用API) |
python scripts/gzh_growth_fetcher.py --input_json_file /tmp/gzh_result.json |
用预取数据重新分析(不调用API) |
使用场景
场景一:日常运营参考
角色:公众号运营者
需求:了解所属赛道头部账号的内容产出与互动表现
使用方式:
- 查询所属分类日榜或周榜
- 对比发布频率、单篇阅读与互动率
- 选择分析功能提取内容策略参考
预期收益:明确对标账号,优化选题与发布节奏
场景二:竞品追踪
角色:品牌营销经理
需求:跟踪竞品公众号排名与互动变化
使用方式:
- 按周查询竞品所在分类榜单
- 关注排名波动与阅读/转发变化
- 订阅周榜定时接收更新
预期收益:及时掌握竞品内容发力方向,调整自身策略
场景三:赛道研判
角色:内容创业者
需求:评估候选赛道的竞争度与阅读需求
使用方式:
- 查询多个候选分类的月榜数据
- 选择蓝海赛道分析功能
- 对比上榜账号数量与平均阅读/评分
预期收益:选择竞争度低、需求旺盛的方向切入
场景四:定期数据汇报
角色:MCN 机构运营人员
需求:为内部复盘或客户汇报提供可视化数据
使用方式:
- 订阅日榜和周榜保持数据更新
- 按月导出 HTML 报告(图片/PDF)
- 结合分析功能撰写复盘结论
预期收益:提升汇报效率,数据口径统一可追溯
项目架构
目录结构
wechat-top-account/
├── SKILL.md # 产品说明文档
├── references/
│ └── core_workflow.md # 核心工作流程(模板、逻辑、步骤)
├── scripts/
│ ├── gzh_growth_fetcher.py # 榜单数据抓取
│ └── gen_gzh_html.py # HTML 可视化报告生成
└── assets/ # 静态资源
技术栈
| 项目 | 说明 |
|---|---|
| 运行环境 | Python 3.8+ |
| 核心依赖 | requests |
| 数据来源 | 红狐Hub API |
| 部署方式 | SkillHub / ClawHub / 命令行脚本 |
核心模块
| 模块 | 职责 |
|---|---|
gzh_growth_fetcher.py |
通过 红狐Hub API 获取榜单数据,支持关键词匹配分类、日期校验与三级认证回退 |
gen_gzh_html.py |
将榜单数据渲染为可导出图片和 PDF 的 HTML 页面 |
core_workflow.md |
输出格式模板、Checklist、日期计算逻辑、完整操作步骤 |
数据更新规则
| 榜单 | 更新时间 | 数据范围 |
|---|---|---|
| 日榜 | 每日 17:30 | 昨日数据,近 7 天 |
| 周榜 | 每周一 17:30 | 上周数据,近 3 周 |
| 月榜 | 每月 3 号 23:00 | 上月数据,近 3 个月 |
常见问答
安装相关问题
Q1:未检测到 API Key 怎么办?
按 鉴权 章节步骤注册 红狐Hub 并配置 REDFOX_API_KEY 环境变量。新注册用户有免费积分。
Q2:如何验证配置已生效?
执行 echo $REDFOX_API_KEY,确认输出格式为 ak_xxxxxxxx。若未生效,检查终端是否已重启。
Q3:支持哪些部署方式?
支持 SkillHub、ClawHub 平台安装,也可直接运行 scripts/ 下 Python 脚本独立调用。
使用相关问题
Q4:支持哪些分类?
共 23 个分类:总排名、乐活生活、人文资讯、企业品牌、体育娱乐、健康养生、创投商业、学术研究、情感心理、房产楼市、搞笑幽默、教育考试、文摘精选、旅游出行、时尚潮流、民生资讯、汽车交通、知识百科、科技数码、美容美体、美食餐饮、职场发展、财富理财。
Q5:数据可回溯多久?
日榜近 7 天、周榜近 3 周、月榜近 3 个月。超出范围时系统自动调整至最近可用数据。
Q6:榜单与 HTML 报告数据是否一致?
两者共用同一 API 接口,数据完全一致。
Q7:数据多久更新一次?
日榜每日 17:30、周榜每周一 17:30、月榜每月 3 日 23:00 更新。
故障排除
Q8:查询返回空数据?
确认分类名称准确(可用 --list_categories 列出全部分类),检查查询日期在有效范围内。
Q9:HTML 生成失败?
确认已安装 requests 库且 API Key 有效,查看脚本错误输出定位原因。
安全与许可
Q10:API Key 如何保管?
REDFOX_API_KEY 仅通过环境变量读取,请勿写入代码仓库或公开分享。Key 泄露可能导致积分被消耗。
Q11:数据来源与使用范围?
榜单数据由 红狐Hub 平台提供,仅供运营分析、竞品跟踪等合法商业用途,请遵守平台服务条款。
核心工作流程
输出格式模板、数据校验规则、日期计算逻辑、操作步骤及使用示例详见 references/core_workflow.md。
执行任务时须严格遵循 core_workflow.md 中的:
- 格式优先级声明与分阶段输出规则
- 输出前必检 Checklist
- 榜单标准模板与按需输出模板
- 榜单类型判断与分类匹配规则
- 日榜 / 周榜 / 月榜日期校验逻辑
- 数据获取与格式化输出流程
- API 调用次数上限(同一轮查询仅允许 1 次 API 调用)
注意事项
- 热门账号推荐榜直接输出,无需确认;分析、HTML、订阅须用户选择后方可生成
- 榜单表格与 HTML 报告须使用同一数据源,禁止修改接口返回顺序
- 同一轮查询(榜单+分析+HTML)全过程只允许调用 1 次 API 接口,首次获取数据时必须同时保存到临时文件,后续操作一律使用
--input_json_file读取,禁止重复调用 API - 未收到明确查询指令时,禁止自动调用脚本或输出榜单内容
- 禁止折叠输出
- 脚本输出的
formatted_markdown字段已包含完整的标准模板格式,直接原样输出即可,禁止自行拼装或修改格式
Files (redfox-community)
-
references
-
core_workflow.md 18.2 KB
# 公众号热门账号推荐 · 核心工作流程 本文档与根目录 `SKILL.md` 配套使用:`SKILL.md` 提供产品说明、安装配置与使用场景;本文档涵盖输出格式、日期逻辑、完整脚本操作步骤与示例。执行任务时须完整遵循本文档规范。 ## 格式优先级声明(最高优先级) 本 Skill 的输出格式优先级高于智能体默认输出行为。无论智能体默认风格如何,须严格按照本文档规定的格式模板重组输出。 分阶段输出规则:热门账号推荐榜直接输出;分析、HTML 页面、订阅服务需先输出功能询问文案,用户确认后再生成。 ## 执行环境(强制) 本 Skill 禁止在子任务或子 Agent 中执行,仅限主 Agent 任务。若检测到子任务环境,立即停止并返回:公众号热门账号推荐查询请在主对话中直接提问。 --- ## API 调用次数上限(强制,违反即为严重错误) **同一轮查询(榜单 + 分析 + HTML)全过程只允许调用 1 次 API 接口,禁止重复调用。** 执行规则: 1. 首次获取数据时,必须同时将输出保存到临时文件(步骤A) 2. 后续所有操作(提取 formatted_markdown、生成 HTML、深度分析)必须使用 `--input_json_file` 读取已保存的文件,禁止再次运行脚本调用 API 3. 若因执行方式导致临时文件未成功保存,仅允许补存操作(用已输出的 JSON 手动写入文件),不得重新调用 API **错误示例**:查询榜单调用1次 → 保存文件又调用1次 → 提取 markdown 再调用1次 = 3次调用(严重违规) **正确示例**:查询榜单并保存文件调用1次 → 提取 markdown 从文件读取(0次)→ 生成 HTML 从文件读取(0次)= 总计1次调用 --- ## 输出前必检Checklist(每次输出前逐项打勾,全部通过才能输出) ``` [ ] 标题格式是否使用二级标题:## 公众号热门账号推荐榜{日榜/周榜/月榜} - {分类名} (更新时间:{时间}) [ ] 数据说明是否在标题后、表格前(斜体) [ ] 表格列顺序是否严格:排名→账号名称→综合评分(满分100)→发布数/文章数→总阅读数→头条阅读数→最高阅读数→总点赞→总在看→总转发 [ ] 排名1-3是否用了🥇🥈🥉,其余用数字 [ ] 账号名称是否加粗+跳转链接(https://open.weixin.qq.com/qr/code?username={accountId}) [ ] 指定字段是否都加粗(综合评分、总阅读、头条阅读、最高阅读、总点赞、总转发) [ ] 数字>=1万是否用x.x万格式,>=1亿用x.x亿格式 [ ] 榜单表格是否输出了全部数据(TOP50) [ ] 榜单表格后是否输出了功能询问文案 [ ] 分析/HTML/订阅是否在用户确认后才输出(禁止未经询问直接输出) [ ] 本轮查询是否仅调用了1次API(首次获取数据时已保存临时文件,后续操作均使用--input_json_file) ``` ## 输出标准模板(严格套用,禁止自由发挥) **输出顺序:热门账号推荐榜(直接输出)→ 功能询问文案(直接输出)→ 用户选择后按需输出分析/HTML/订阅** ### 热门账号推荐榜(直接输出) ## 公众号热门账号推荐榜{日榜/周榜/月榜} - {分类名} (更新时间:{更新时间}) *公众号综合实力{日榜/周榜/月榜},基于阅读、点赞、转发、在看等多维数据综合排名* *数据统计时间周期:{时间范围}* > 时间周期格式:日榜="2026-05-05";周榜="2026-05-04至2026-05-10";月榜="2026-04-01至2026-04-30" | 排名 | 账号名称 | 综合评分(满分100) | 发布数/文章数 | 总阅读数 | 头条阅读数 | 最高阅读数 | 总点赞 | 总在看 | 总转发 | |:---:|:---|---:|:---:|---:|---:|---:|---:|:---:|---:| | 🥇 | **[公众号名](https://open.weixin.qq.com/qr/code?username={accountId})** | **94.7** | 21/73 | **226.9万** | **151.4万** | **10.0万** | **1.5万** | **5729** | **9.3万** | | 🥈 | **[公众号名](https://open.weixin.qq.com/qr/code?username={accountId})** | **93.8** | 21/46 | **179.7万** | **153.2万** | **10.0万** | **1.9万** | **6064** | **5.9万** | | 🥉 | **[公众号名](https://open.weixin.qq.com/qr/code?username={accountId})** | **93.2** | 7/51 | **209.3万** | **25.5万** | **10.0万** | **2736** | **821** | **5295** | | 4 | [公众号名](https://open.weixin.qq.com/qr/code?username={accountId}) | 92.5 | 7/43 | 155.8万 | 65.7万 | 10.0万 | 1.3万 | 1941 | 2.0万 | | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | | 50 | [公众号名](https://open.weixin.qq.com/qr/code?username={accountId}) | 80.2 | 3/15 | 28.6万 | 9.1万 | 5.2万 | 892 | 413 | 3210 | ### 功能询问文案(榜单表格后直接输出,一字不改) 技能还支持以下功能: 1️⃣ 支持对以上数据进行分析。其中包含**内容生态变化分析**、**热点影响力量化**、**蓝海赛道挖掘**三种分析角度,方便您了解公众号阅读数据或获取运营参考时使用。 2️⃣ 可生成公众号风格的html文件包。支持**导出图片**、**导出PDF**,方便阅读、保存和分享。 3️⃣ 支持订阅每日/周/月的公众号账号最新排名,订阅后将定时推送给您(日榜将在每日17点30更新/周榜将在每周一17:30准时更新/月榜将在每月3号23:00准时推送)。直接回复"订阅日/周/月榜"即可订阅相关榜单,或者"全部订阅"将会为您推送全部类型的榜单。 回复数字即可~ --- ## 按需输出模板(用户确认后使用) ### 热门账号推荐榜分析(用户选择1️⃣后输出) ## 公众号热门账号推荐榜{日榜/周榜/月榜}分析 以下内容分析基于获取的全部数据(TOP50)进行~ ## 内容生态变化分析 - 平均发布数趋势:统计TOP50账号的平均发布数(发布天数/总天数),如近半年平均发布数从XX降至XX,说明用户从「追内容数量」转向「追内容质量」 - 平均单篇阅读趋势:统计TOP50账号的平均单篇阅读(总阅读/发布篇数),如从XX升至XX,验证内容质量提升带来的阅读效率增长 - 平均互动率趋势:统计TOP50的平均互动率(点赞+在看+转发/总阅读),如转发率从XX%持续降至XX%,说明公众号公域传播能力减弱,私域留存价值提升;如点赞率上升,说明读者深度阅读意愿增强 ### 热点影响力量化 - 特殊事件节点前后对比:如618大促期间消费测评类账号排名集体上升XX位、政策出台后财经解读类账号入榜数增加XX个 - 热点对内容流量的拉动效果:量化特殊事件期间相关分类账号的平均阅读增幅(如+XX%)、综合评分提升幅度(如+XX分) - 行业流量周期规律:总结热点事件的流量拉动持续周期(如促销类约X天、政策类约X天),以及非热点期间的流量回落规律 ### 蓝海赛道挖掘 - 细分赛道竞争度统计:统计TOP50里各细分赛道的上榜账号数量,如老年教育、宠物医疗这类细分赛道上榜账号仅X个 - 蓝海判断标准:上榜账号少(<=X个)但对应账号综合评分高(>=XX)、平均阅读高(>=XX万),说明需求旺盛但供给不足 - 切入建议:列出具体蓝海赛道及其核心指标(上榜数/平均评分/平均阅读),给出内容定位和运营策略建议 ### HTML页面(用户选择2️⃣后生成) 由 `python scripts/gen_gzh_html.py` 生成,直接输出页面内容,禁止只输出文件路径。 ### 订阅响应(用户选择3️⃣或回复订阅指令时) 用户回复"订阅日榜" → 回复:"您已成功订阅日榜,将在每日17:30准时推送🌟~" 用户回复"订阅周榜" → 回复:"您已成功订阅周榜,将在每周一17:30准时推送🌟~" 用户回复"订阅月榜" → 回复:"您已成功订阅月榜,将在每月3号23:00准时推送🌟~" 用户回复"全部订阅" → 回复:"您已成功订阅全部榜单,将在每日17:30推送日榜、每周一17:30推送周榜、每月3号23:00推送月榜🌟~" --- ## 数据接口更新规则 - **日榜**:每日17:30更新昨日数据 - **周榜**:每周一17:30更新上周数据 - **月榜**:每月3号23:00更新上月数据 - **数据范围**:日榜/周榜/月榜的公众号热门账号推荐榜排名TOP50 - **分类覆盖**:23个垂直领域分类 - **日期查询范围**:日榜近7天、周榜近3周、月榜近3个月 ## 数据一致性要求 - 热门账号推荐榜表格与HTML页面数据必须完全一致(相同顺序、相同内容) - 两个脚本使用相同的API接口,禁止修改接口返回的顺序 ## 操作步骤 ### 第一步:确定榜单类型 根据用户输入判断榜单类型: - 用户提到"日榜"、"今日"、"当天"、"昨日"等 → 日榜(day) - 用户提到"周榜"、"本周"、"这周"、"上周"等 → 周榜(week) - 用户提到"月榜"、"本月"、"这月"、"上月"等 → 月榜(month) - 未指定 → 默认日榜(day) ### 第二步:分类匹配 根据用户输入自动匹配合适的分类: **可用分类列表(共23个)**: 总排名、乐活生活、人文资讯、企业品牌、体育娱乐、健康养生、创投商业、学术研究、情感心理、房产楼市、搞笑幽默、教育考试、文摘精选、旅游出行、时尚潮流、民生资讯、汽车交通、知识百科、科技数码、美容美体、美食餐饮、职场发展、财富理财 **匹配规则**: - 用户输入具体产品词(如"手机")→ 匹配对应分类("科技数码") - 用户输入领域词(如"穿搭"、"美食")→ 直接匹配分类 - 未指定分类 → 使用"人文资讯" **关键词-分类映射示例**: ``` 手机/电脑/AI → 科技数码 穿搭/时尚/潮流 → 时尚潮流 美食/做饭/探店 → 美食餐饮 职场/求职/跳槽 → 职场发展 股票/基金/理财 → 财富理财 健身/运动/减肥 → 体育娱乐 护肤/化妆/美妆 → 美容美体 ``` ### 第三步:日期判断与校验 **日期查询范围限制**: - 日榜:支持查询最近7天数据 - 周榜:支持查询最近3周数据 - 月榜:支持查询最近3个月数据 **日榜日期逻辑**: - 更新时间:每日17:30更新昨日数据 - 自动计算: - 当前时间 >= 17:30 → 查询昨日数据 - 当前时间 < 17:30 → 查询前天数据 - 用户输入"今日"、"今天": - 若17:30前 → 展示提醒"日榜数据暂未更新,将为您查询最接近您需求日期的榜单数据⭐~"并推荐查询昨日更新的最新榜单 - 用户输入"昨日"、"昨天" → 查询前天的具体日期 - 用户输入具体日期 → 仅支持最近7天,超出7天展示提醒:"非常抱歉🙏,目前公众号榜单最多支持回溯「近7天的日榜/近3周的周榜/近3个月的月榜」,我将为您查询最接近您需求时间的{日榜/周榜/月榜}数据⭐~"并自动查询最近可用的数据 **周榜日期逻辑**: - 更新时间:每周一17:30更新上周数据 - 自动计算: - 当前时间 >= 周一17:30 → 查询本周一日期 - 当前时间 < 周一17:30 → 查询上周一日期 - 用户输入具体日期 → 仅支持最近3周,超出范围同上提醒 **月榜日期逻辑**: - 更新时间:每月3号23:00更新上月数据 - 自动计算: - 当前已过本月3号23:00 → 查询上月1号日期(上月数据已更新) - 当前未过本月3号23:00 → 查询上上月1号日期 - 用户输入"当月"、"本月" → 根据更新时间判断,若未到更新时间则展示提醒 - 用户输入"上月"、"上个月" → 查询相应月份 - 用户输入具体月份 → 仅支持最近3个月,超出范围展示提醒:"非常抱歉🙏,目前公众号榜单最多支持回溯「近7天的日榜/近3周的周榜/近3个月的月榜」,我将为您查询最接近您需求时间的月榜数据⭐~"并自动查询最近可用数据 ### 第四步:获取热门账号推荐榜数据 调用脚本获取榜单数据(默认TOP50),**首次查询必须同时将输出保存到临时文件(这是唯一一次API调用)**: **查询命令**: ```bash # 首次查询:必须同时保存到临时文件(唯一一次API调用) python scripts/gzh_growth_fetcher.py --rank_type day --keyword "科技" --top_n 50 > /tmp/gzh_result.json 2>/dev/null python scripts/gzh_growth_fetcher.py --rank_type week --category "科技数码" --top_n 50 > /tmp/gzh_result.json 2>/dev/null python scripts/gzh_growth_fetcher.py --rank_type month --keyword "美食" --top_n 50 > /tmp/gzh_result.json 2>/dev/null ``` **参数说明**: - `--rank_type`:榜单类型 day/week/month(默认day) - `--category`:分类名称(可选) - `--keyword`:用户输入的关键词,用于自动匹配分类 - `--rank_date`:查询日期(可选,自动判断;超出范围自动调整) - `--top_n`:返回条数(默认50) - `--start`:起始排名(默认1) - `--list_categories`:列出所有可用分类 - `--input_json_file`:预取数据JSON文件路径(指定后不再调用API,复用已获取的数据) **数据字段说明(API返回字段)**: - `accountName`:公众号名称 - `accountId`:公众号ID(用于构建搜索链接) - `accountAvatar`:公众号头像URL - `rankPosition`:排名位置 - `category`:所属分类 - `publishCount`:发布数/文章数(如"3/9",表示9天内发布了3天) - `totalReadCount`:总阅读数 - `headlineReadCount`:头条阅读数 - `maxReadCount`:最高阅读数 - `totalLikeCount`:总点赞数 - `totalInSeeCount`:总在看数 - `totalForwardCount`:总转发数 - `averageReadCount`:平均阅读数(API返回,但展示表格中不显示) - `comprehensiveScore`:综合评分(满分100,接口返回) **日期校验输出**: - 脚本返回的 `reminder` 字段包含日期提醒信息,必须输出给用户 - 超出查询范围时脚本自动调整日期,需展示提醒消息 ### 第五步:按标准模板输出 **重要:脚本输出的 `formatted_markdown` 字段已包含完整的标准模板格式(标题+数据说明+reminder+表格+功能询问文案),直接原样输出该字段即可,禁止自行拼装或修改格式。** 1. 直接原样输出 `formatted_markdown` 字段内容(已包含榜单表格+功能询问文案) 2. 等待用户回复,按需输出: - 用户选择分析 → 输出热门账号推荐榜分析(三段式) - 用户选择HTML → 调用脚本生成HTML页面并输出 - 用户选择订阅 → 按订阅响应模板回复 - 用户可多选,按用户提到的顺序依次输出 **数据复用规则(强制)**:同一轮查询全过程只允许调用 1 次 API。当用户先查看榜单再请求HTML/分析时,必须使用 `--input_json_file` 传入已获取的数据,禁止再次调用API。步骤如下: ```bash # 步骤A:首次获取数据时,必须同时将输出保存到临时文件(唯一一次API调用) python scripts/gzh_growth_fetcher.py --rank_type day --keyword "科技" --top_n 50 > /tmp/gzh_result.json 2>/dev/null # 步骤B:用预取数据生成 HTML(不调用API) python scripts/gen_gzh_html.py --input_json_file /tmp/gzh_result.json --top 50 # 步骤C:用预取数据重新分析(不调用API) python scripts/gzh_growth_fetcher.py --input_json_file /tmp/gzh_result.json ``` **HTML生成命令(必须复用数据,禁止直接调用API)**: ```bash python scripts/gen_gzh_html.py --input_json_file {预取数据文件路径} --top 50 ``` **HTML文件命名**:`公众号热门账号推荐榜【日/周/月】_【分类名】_【8位时间戳】.html` **输出后自检**:对照"输出前必检Checklist"逐项确认。 ## 使用示例 ### 示例1:日榜关键词查询 - **场景/输入**:"帮我查一下科技类的公众号排行" - **预期产出**:匹配科技数码分类,日榜,输出热门账号推荐榜表格 + 功能询问文案,等待用户选择 ### 示例2:周榜查询 + 用户选择分析 - **场景/输入**:"这周公众号热门账号推荐榜怎么样" → 用户回复"1" - **预期产出**:使用周榜(week),输出热门账号推荐榜 + 询问文案 → 用户选1后输出三段式分析 ### 示例3:月榜 + 订阅 - **场景/输入**:"本月美食类公众号排行" → 用户回复"订阅月榜" - **预期产出**:使用月榜(month),输出热门账号推荐榜 + 询问文案 → 用户订阅月榜后回复"月榜每月3号23:00更新,将在对应时间为您推送~" ## 资源索引 - **脚本**:见 [scripts/gzh_growth_fetcher.py](../scripts/gzh_growth_fetcher.py) - 用途:获取公众号热门账号推荐榜单数据 - 参数:--rank_type day/week/month / --category 分类名 / --keyword 关键词 / --rank_date 日期 / --top_n 条数 / --start 起始排名 / --input_json_file 预取数据文件路径 - **脚本**:见 [scripts/gen_gzh_html.py](../scripts/gen_gzh_html.py) - 用途:生成可视化HTML页面 - 参数:--rank_type day/week/month / --category 分类名 / --keyword 关键词 / --rank_date 日期 / --top 条数 / --output 输出路径 / --input_json_file 预取数据文件路径 ## 注意事项 - **格式优先级(最高)**:本Skill的格式要求 > 智能体默认输出,必须100%按标准模板输出 - **执行环境(强制)**:本Skill只能在主Agent任务中执行,禁止在子任务/子Agent中执行 - **热门账号推荐榜**:直接输出,无需询问 - **分析/HTML/订阅**:必须先输出功能询问文案,用户确认后才可输出,禁止未经询问直接输出 - **禁止折叠输出** - **HTML页面**:用户选择后生成,直接输出页面内容,禁止只输出文件路径 - **数据一致性**:热门账号推荐榜表格与HTML页面必须使用相同的数据源,禁止修改接口返回的顺序 - **数据复用(强制)**:同一轮查询(榜单+分析+HTML)全过程只允许调用 1 次 API 接口。首次获取数据时必须同时保存到临时文件,后续操作一律使用 `--input_json_file` 读取,禁止重复调用 API - **更新时间**:日榜/周榜每日17:30更新,月榜每月3号23:00更新 - **日期查询范围**:日榜近7天、周榜近3周、月榜近3个月,超出范围自动调整并展示提醒 - **禁止自动查询**:未订阅且未收到用户明确查询输入时,禁止自动调用脚本或输出任何热榜信息
-
-
scripts
-
gen_gzh_html.py 39.6 KB
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """公众号热门账号推荐HTML生成器 从API获取公众号热门账号推荐榜单数据,生成可独立打开的HTML页面。 用法:python gen_gzh_html.py [--rank_type day|week|month] [--rank_date YYYY-MM-DD] [--category 分类名] [--top N] [--output PATH] 输出:gzh_growth.html(与脚本同目录) 样式特性: - 公众号风格(绿色主题 #07c160) - 卡片式布局 - 公众号头像+名称+数据指标 - TOP3 奖牌徽章 + 左边框高亮 - 导出 PDF/图片功能 - 页面最大宽度 750px """ import json import sys import os import pathlib from datetime import datetime, timedelta from urllib.parse import quote import requests # ===== 常量 ===== API_URL = "https://redfox.hk/story/api/cozeSkill/getGzhCozeSkillDataIndex" SOURCE = "公众号综合实力账号榜-GitHub" CATEGORIES = [ "总排名", "乐活生活", "人文资讯", "企业品牌", "体育娱乐", "健康养生", "创投商业", "学术研究", "情感心理", "房产楼市", "搞笑幽默", "教育考试", "文摘精选", "旅游出行", "时尚潮流", "民生资讯", "汽车交通", "知识百科", "科技数码", "美容美体", "美食餐饮", "职场发展", "财富理财" ] CATEGORY_KEYWORDS = { "总排名": ["总排名", "综合", "全部", "热门", "推荐", "随便", "总榜", "整体"], "乐活生活": ["乐活", "生活", "日常", "生活方式", "生活日常", "好物推荐"], "人文资讯": ["人文", "资讯", "文化", "历史", "哲学", "人文社科"], "企业品牌": ["企业", "品牌", "公司", "商业品牌", "品牌营销"], "体育娱乐": ["体育", "娱乐", "运动", "健身", "篮球", "足球", "综艺", "明星"], "健康养生": ["健康", "养生", "保健", "中医", "调理", "减肥", "瘦身"], "创投商业": ["创投", "商业", "投资", "创业", "融资", "商业模式"], "学术研究": ["学术", "研究", "论文", "科研", "学报"], "情感心理": ["情感", "心理", "恋爱", "婚姻", "情绪", "心理咨询"], "房产楼市": ["房产", "楼市", "买房", "房价", "地产", "租房", "装修"], "搞笑幽默": ["搞笑", "幽默", "段子", "吐槽", "沙雕", "表情包"], "教育考试": ["教育", "考试", "培训", "考研", "考公", "留学", "英语", "学习"], "文摘精选": ["文摘", "精选", "美文", "散文", "故事", "文章精选"], "旅游出行": ["旅游", "出行", "旅行", "攻略", "景点", "酒店", "度假"], "时尚潮流": ["时尚", "潮流", "穿搭", "服饰", "OOTD", "ootd", "搭配"], "民生资讯": ["民生", "社会", "热点", "时事", "政策", "民生新闻"], "汽车交通": ["汽车", "交通", "车", "新能源", "电动车", "买车"], "知识百科": ["知识", "百科", "科普", "常识", "冷知识"], "科技数码": ["科技", "数码", "手机", "电脑", "智能", "AI", "互联网", "软件", "硬件"], "美容美体": ["美容", "美体", "护肤", "化妆", "美妆", "彩妆"], "美食餐饮": ["美食", "餐饮", "做饭", "烹饪", "餐厅", "探店", "食谱"], "职场发展": ["职场", "工作", "求职", "面试", "跳槽", "升职", "加薪", "简历"], "财富理财": ["财富", "理财", "财经", "投资", "基金", "股票", "保险", "财务", "赚钱"], } # 榜单更新时间规则 DAY_UPDATE_HOUR = 17 DAY_UPDATE_MINUTE = 30 MONTH_UPDATE_DAY = 3 MONTH_UPDATE_HOUR = 23 MONTH_UPDATE_MINUTE = 0 DAY_MAX_DAYS_BACK = 7 WEEK_MAX_WEEKS_BACK = 3 MONTH_MAX_MONTHS_BACK = 3 # ===== 日期计算 ===== def _is_after_day_update(now=None): """判断当前时间是否已过日榜/周榜更新时间(17:30)""" 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号23:00)""" if now is None: now = datetime.now() try: cutoff = now.replace(day=MONTH_UPDATE_DAY, hour=MONTH_UPDATE_HOUR, minute=MONTH_UPDATE_MINUTE, second=0, microsecond=0) except ValueError: 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"): """获取最新可查询的榜单日期""" 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": # 月榜: 每月3号23:00更新上月数据 # rankDate传上月1号 = 上月数据 # 3号23:00后 → 上月1号(=上月数据已更新); 3号23:00前 → 上上月1号 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"): """获取可查询的最早日期""" 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): """验证用户指定日期是否在可查询范围""" 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") rank_label = {"day": "日榜", "week": "周榜", "month": "月榜"}.get(rank_type, "日榜") reminder = ( '非常抱歉🙏,目前公众号榜单最多支持回溯「近7天的日榜/近3周的周榜/近3个月的月榜」,' '我将为您查询最接近您需求时间的{}数据⭐~' ).format(rank_label) return { "original_date": user_date_str, "adjusted_date": adjusted, "is_adjusted": True, "reminder": reminder } def get_query_date(rank_type="day", user_date=None): """根据榜单类型和当前时间确定查询日期 Args: rank_type: day/week/month user_date: 用户指定日期 Returns: (查询日期字符串, 是否自动推断, 提醒消息) """ if user_date: validation = validate_and_adjust_date(rank_type, user_date) return validation["adjusted_date"], False, validation["reminder"] rank_date = get_latest_query_date(rank_type) # 检查今日日榜是否已更新 reminder = "" if rank_type == "day" and not _is_after_day_update(): yesterday = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d") reminder = ( '日榜数据暂未更新,将为您查询最接近您需求日期的榜单数据⭐~\n' f'推荐查询昨日更新的最新榜单({yesterday}日榜)' ) now = datetime.now() if rank_type == "day": if _is_after_day_update(now): pass # 已过17:30,查询昨日数据 else: pass # 未过17:30,查询前天数据 elif rank_type == "week": if _is_after_day_update(now): pass else: pass elif rank_type == "month": if _is_after_month_update(now): pass else: pass return rank_date, True, reminder # ===== 分类匹配 ===== def match_category(user_input): """根据用户输入匹配分类""" if not user_input: return "人文资讯" if user_input in CATEGORIES: return user_input user_lower = user_input.lower().strip() for category, keywords in CATEGORY_KEYWORDS.items(): for keyword in keywords: if keyword.lower() in user_lower: return category return "人文资讯" # ===== 认证 ===== def _get_redfox_api_key(): """三级认证回退获取 红狐Hub API Key 优先级: 1. 环境变量 REDFOX_API_KEY 2. Shell 配置文件(.zshrc / .bashrc / PowerShell Profile) 3. 返回 None,由上层提示用户配置 Returns: str or None: API Key """ # 第一级:环境变量 api_key = os.getenv("REDFOX_API_KEY") if api_key and api_key.startswith("ak_"): return api_key # 第二级:Shell 配置文件 home = pathlib.Path.home() config_files = [] if sys.platform == "win32": # PowerShell Profile ps_dir = home / "Documents" / "WindowsPowerShell" if ps_dir.exists(): for f in ps_dir.glob("Microsoft.PowerShell_profile.ps1"): config_files.append(f) ps7_dir = home / "Documents" / "PowerShell" if ps7_dir.exists(): for f in ps7_dir.glob("Microsoft.PowerShell_profile.ps1"): config_files.append(f) # Git Bash / MSYS2 for name in [".bashrc", ".bash_profile", ".profile"]: p = home / name if p.exists(): config_files.append(p) else: for name in [".zshrc", ".bashrc", ".bash_profile", ".profile"]: p = home / name if p.exists(): config_files.append(p) for config in config_files: try: content = config.read_text(encoding="utf-8", errors="ignore") for line in content.splitlines(): line = line.strip() if line.startswith("export REDFOX_API_KEY=") or line.startswith("set REDFOX_API_KEY="): parts = line.split("=", 1) if len(parts) == 2: key = parts[1].strip().strip('"').strip("'") if key and key.startswith("ak_"): return key # 匹配 PowerShell: $env:REDFOX_API_KEY = "xxx" if "$env:REDFOX_API_KEY" in line: parts = line.split("=", 1) if len(parts) == 2: key = parts[1].strip().strip('"').strip("'") if key and key.startswith("ak_"): return key except Exception: continue return None # ===== HTTP请求 ===== def fetch_gzh_growth(rank_type="day", rank_date=None, category="人文资讯"): """获取公众号热门账号推荐榜单数据""" if not rank_date: rank_date = get_latest_query_date(rank_type) params = { "rankType": rank_type, "rankDate": rank_date, "category": category, "source": SOURCE, } credential = _get_redfox_api_key() if not credential: print( "未检测到 红狐Hub API Key。请按以下步骤配置:\n" "1. 访问 https://redfox.hk/?source=github 了解服务详情\n" "2. 前往 https://redfox.hk/login?source=github 注册账号(新用户获赠免费积分)\n" "3. 注册登录后在个人中心获取 API Key(格式 ak_xxxxxxxx)\n" "4. 设置环境变量:\n" " macOS/Linux: export REDFOX_API_KEY=<你的apikey>\n" " Windows PowerShell: $env:REDFOX_API_KEY = \"<你的apikey>\"\n" " 或告知我帮你自动配置到 Shell 配置文件中", file=sys.stderr ) return { "fetch_time": _get_update_time_label(rank_type, rank_date), "rank_type": rank_type, "rank_date": rank_date, "category": category, "time_range": _get_data_time_range(rank_type, rank_date), "account_list": [], "error": "MISSING_API_KEY" } headers = { "Content-Type": "application/json", "X-API-KEY": credential, } try: response = requests.get(API_URL, params=params, headers=headers, timeout=30) if response.status_code != 200: return { "fetch_time": _get_update_time_label(rank_type, rank_date), "rank_type": rank_type, "rank_date": rank_date, "category": category, "time_range": _get_data_time_range(rank_type, rank_date), "account_list": [] } api_response = response.json() except Exception: return { "fetch_time": _get_update_time_label(rank_type, rank_date), "rank_type": rank_type, "rank_date": rank_date, "category": category, "time_range": _get_data_time_range(rank_type, rank_date), "account_list": [] } if isinstance(api_response, dict): data = api_response.get("data", []) elif isinstance(api_response, list): data = api_response else: data = [] if not data: return { "fetch_time": _get_update_time_label(rank_type, rank_date), "rank_type": rank_type, "rank_date": rank_date, "category": category, "time_range": _get_data_time_range(rank_type, rank_date), "account_list": [] } # 保持接口原始顺序,禁止重新排序 # 综合评分直接采用接口返回的comprehensiveScore字段 account_list = [] for idx, item in enumerate(data): account_list.append({ "index": idx + 1, "rankPosition": item.get("rankPosition", idx + 1), "accountName": item.get("accountName", ""), "accountId": item.get("accountId", ""), "accountAvatar": item.get("accountAvatar", ""), "category": item.get("category", ""), "compositeScore": round(float(item.get("comprehensiveScore", 0)), 1) if item.get("comprehensiveScore") else 0, "totalReadCount": item.get("totalReadCount", 0), "headlineReadCount": item.get("headlineReadCount", 0), "maxReadCount": item.get("maxReadCount", 0), "totalLikeCount": item.get("totalLikeCount", 0), "totalForwardCount": item.get("totalForwardCount", 0), "totalInSeeCount": item.get("totalInSeeCount", 0), "publishCount": item.get("publishCount", "-"), }) return { "fetch_time": _get_update_time_label(rank_type, rank_date), "rank_type": rank_type, "rank_date": rank_date, "category": category, "time_range": _get_data_time_range(rank_type, rank_date), "account_list": account_list } 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 f"{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_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')} 17:30" 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')} 17:30" 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')} 23:00" return rank_date # ===== 数据转换 ===== def raw_data_to_account_list(raw_data): """将 gzh_growth_fetcher 的 raw_data 转换为 generate_html 所需的 account_list 格式 Args: raw_data: gzh_growth_fetcher 输出 JSON 中的 raw_data 数组 Returns: list: generate_html 所需的 account_list 格式 """ account_list = [] for idx, item in enumerate(raw_data): account_list.append({ "index": idx + 1, "rankPosition": item.get("rankPosition", idx + 1), "accountName": item.get("accountName", ""), "accountId": item.get("accountId", ""), "accountAvatar": item.get("accountAvatar", ""), "category": item.get("category", ""), "compositeScore": item.get("compositeScore", 0), "totalReadCount": item.get("totalReadCount", 0), "headlineReadCount": item.get("headlineReadCount", 0), "maxReadCount": item.get("maxReadCount", 0), "totalLikeCount": item.get("totalLikeCount", 0), "totalForwardCount": item.get("totalForwardCount", 0), "totalInSeeCount": item.get("totalInSeeCount", 0), "publishCount": item.get("publishCount", "-"), }) return account_list # ===== HTML生成 ===== def generate_html(result, top_n=50): """生成HTML页面 - 公众号热门账号推荐榜 Args: result: 榜单数据结果 top_n: 显示条数,默认50 """ account_list = result["account_list"] fetch_time = result["fetch_time"] rank_date = result.get("rank_date", "") category = result.get("category", "人文资讯") rank_type = result.get("rank_type", "day") rank_type_label = {"day": "日榜", "week": "周榜", "month": "月榜"}.get(rank_type, "日榜") time_range = result.get("time_range", _get_data_time_range(rank_type, rank_date)) top_n = min(top_n, len(account_list), 100) account_list = account_list[:top_n] js_data = json.dumps(account_list, ensure_ascii=False, indent=2) page_title = f"公众号综合实力{rank_type_label} - {category}" html = f'''<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{page_title}</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> <style> * {{ margin: 0; padding: 0; box-sizing: border-box; }} body {{ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', sans-serif; background: #f5f5f5; color: #333; line-height: 1.5; }} .page-wrap {{ max-width: 700px; margin: 0 auto; padding: 12px 12px 24px; }} .export-bar {{ position: sticky; top: 0; z-index: 100; display: flex; justify-content: flex-end; gap: 10px; padding: 8px 0 10px; }} .btn-export-pdf, .btn-export-img {{ background: #fff; color: #07c160; border: 1.5px solid #07c160; border-radius: 20px; padding: 6px 18px; font-size: 13px; font-weight: 600; cursor: pointer; transition: all 0.2s ease; }} .btn-export-pdf:hover, .btn-export-img:hover {{ background: #f0faf4; }} .pdf-content {{ background: transparent; }} .header-wrap {{ background: linear-gradient(135deg, #07c160 0%, #2dc84d 100%); border-radius: 12px; padding: 20px 16px; margin-bottom: 12px; color: #fff; }} .header-wrap h1 {{ font-size: 22px; font-weight: 700; margin-bottom: 4px; }} .header-meta {{ font-size: 13px; opacity: 0.9; }} .category-badge {{ display: inline-block; background: rgba(255,255,255,0.25); border-radius: 10px; padding: 2px 10px; font-size: 13px; margin-left: 8px; }} .stats-row {{ display: flex; justify-content: space-around; margin-top: 14px; padding-top: 14px; border-top: 1px solid rgba(255,255,255,0.2); }} .stat-item {{ text-align: center; }} .stat-num {{ font-size: 20px; font-weight: 700; }} .stat-label {{ font-size: 12px; opacity: 0.85; margin-top: 2px; }} .account-list {{ display: flex; flex-direction: column; gap: 10px; }} .account-card {{ background: #fff; border-radius: 12px; padding: 14px; transition: all 0.2s ease; border: 1px solid #f0f0f0; }} .account-card:hover {{ box-shadow: 0 4px 12px rgba(0,0,0,0.08); }} .account-card.top1 {{ border-left: 3px solid #07c160; }} .account-card.top2 {{ border-left: 3px solid #2dc84d; }} .account-card.top3 {{ border-left: 3px solid #7ed89a; }} .row1 {{ display: flex; align-items: center; margin-bottom: 10px; }} .rank-num {{ min-width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; font-size: 18px; font-weight: 700; color: #999; margin-right: 12px; flex-shrink: 0; }} .account-avatar {{ width: 40px; height: 40px; border-radius: 50%; object-fit: cover; margin-right: 10px; flex-shrink: 0; }} .account-info {{ flex: 1; min-width: 0; }} .account-name {{ font-size: 16px; font-weight: 600; color: #333; line-height: 1.4; }} .account-name a {{ color: #333; text-decoration: none; }} .account-name a:hover {{ color: #07c160; }} .account-id {{ font-size: 12px; color: #999; margin-top: 2px; }} .publish-tag {{ display: inline-block; background: #f0faf4; color: #07c160; border-radius: 4px; padding: 1px 6px; font-size: 12px; font-weight: 500; margin-left: 8px; }} .row2 {{ display: flex; flex-wrap: wrap; gap: 14px; padding-left: 48px; margin-top: 8px; padding-top: 8px; border-top: 1px solid #f5f5f5; }} .data-item {{ display: flex; align-items: center; gap: 4px; font-size: 13px; }} .data-label {{ color: #999; }} .data-value {{ color: #333; font-weight: 600; }} .footer-note {{ text-align: center; font-size: 12px; color: #bbb; margin-top: 16px; padding: 10px 0; }} @media (max-width: 600px) {{ .page-wrap {{ padding: 8px 8px 20px; }} .header-wrap {{ padding: 16px 12px; }} .header-wrap h1 {{ font-size: 20px; }} .account-card {{ padding: 12px; }} .account-name {{ font-size: 15px; }} .rank-num {{ min-width: 32px; height: 32px; font-size: 16px; }} .row2 {{ gap: 10px; padding-left: 44px; margin-top: 8px; }} .data-item {{ font-size: 12px; }} }} </style> </head> <body> <div class="page-wrap"> <div class="export-bar"> <button class="btn-export-img" onclick="exportImage()">导出图片</button> <button class="btn-export-pdf" onclick="exportPdf()">导出 PDF</button> </div> <div class="pdf-content" id="pdfContent"> <div class="header-wrap"> <h1>公众号综合实力{rank_type_label}<span class="category-badge">{category}</span></h1> <div class="header-meta">更新时间:{fetch_time}</div> <div style="font-size:12px;color:rgba(255,255,255,0.75);margin-top:4px;">*公众号综合实力{rank_type_label},基于阅读、点赞、转发、在看等多维数据综合排名*</div><div style="font-size:12px;color:rgba(255,255,255,0.75);">*数据统计时间周期:{time_range}*</div> <div class="stats-row"> <div class="stat-item"> <div class="stat-num" id="totalCount">--</div> <div class="stat-label">公众号数</div> </div> <div class="stat-item"> <div class="stat-num" id="maxRead">--</div> <div class="stat-label">最高总阅读</div> </div> <div class="stat-item"> <div class="stat-num" id="avgRead">--</div> <div class="stat-label">平均总阅读</div> </div> </div> </div> <div class="account-list" id="accountList"></div> <div class="footer-note">数据来源:公众号平台 · {category} · 公众号综合实力{rank_type_label}</div> </div> </div> <script> (function() {{ function bindCardClick() {{ var cards = document.querySelectorAll('.account-card[data-href]'); cards.forEach(function(card) {{ card.addEventListener('click', function(e) {{ if (e.target.closest('a, button')) return; var url = this.getAttribute('data-href'); if (url && url !== '#') window.open(url, '_blank'); }}); }}); }} if (document.readyState === 'loading') {{ document.addEventListener('DOMContentLoaded', bindCardClick); }} else {{ bindCardClick(); }} }})(); (function() {{ var RAW = {js_data}; var totalCount = RAW.length; var maxRead = 0, sumRead = 0; function fmtNum(val) {{ var n = parseInt(val) || 0; if (n >= 100000000) return (n / 100000000).toFixed(1) + '亿'; if (n >= 10000) return (n / 10000).toFixed(1) + '万'; return n.toString(); }} for (var i = 0; i < RAW.length; i++) {{ var totalRead = parseInt(RAW[i].totalReadCount) || 0; if (totalRead > maxRead) maxRead = totalRead; sumRead += totalRead; }} var avgRead = totalCount > 0 ? sumRead / totalCount : 0; document.getElementById('totalCount').textContent = totalCount; document.getElementById('maxRead').textContent = fmtNum(maxRead); document.getElementById('avgRead').textContent = fmtNum(Math.round(avgRead)); var html = ''; for (var i = 0; i < RAW.length && i < {top_n}; i++) {{ var d = RAW[i]; var rank = i + 1; var cardCls = 'account-card'; if (rank === 1) cardCls += ' top1'; else if (rank === 2) cardCls += ' top2'; else if (rank === 3) cardCls += ' top3'; var searchUrl = 'https://open.weixin.qq.com/qr/code?username=' + encodeURIComponent(d.accountId || d.accountName); var avatar = d.accountAvatar || ''; var accountName = d.accountName || '--'; var accountId = d.accountId || ''; var totalReadCount = fmtNum(d.totalReadCount); var headlineReadCount = fmtNum(d.headlineReadCount); var maxReadCount = fmtNum(d.maxReadCount); var totalLikeCount = fmtNum(d.totalLikeCount); var totalForwardCount = fmtNum(d.totalForwardCount); var totalInSeeCount = fmtNum(d.totalInSeeCount); var publishCount = d.publishCount || '-'; var compositeScore = d.compositeScore || 0; var rankHtml = ''; if (rank === 1) {{ rankHtml = '<div class="rank-num">🥇</div>'; }} else if (rank === 2) {{ rankHtml = '<div class="rank-num">🥈</div>'; }} else if (rank === 3) {{ rankHtml = '<div class="rank-num">🥉</div>'; }} else {{ rankHtml = '<div class="rank-num">' + rank + '</div>'; }} html += '<div class="' + cardCls + '" data-href="' + searchUrl + '">' + '<div class="row1">' + rankHtml + (avatar ? '<img class="account-avatar export-hide" src="' + avatar + '" referrerPolicy="no-referrer" onerror="this.style.display=\\'none\\'">' : '') + '<div class="account-info">' + '<div class="account-name"><a href="' + searchUrl + '" target="_blank" onclick="event.stopPropagation()">' + accountName + '</a>' + '<span class="publish-tag">发布' + publishCount + '</span>' + '</div>' + '<div class="account-id">ID: ' + accountId + ' | 综合评分: <b style="color:#07c160">' + compositeScore + '</b></div>' + '</div>' + '</div>' + '<div class="row2">' + '<div class="data-item"><span class="data-label">总阅读</span><span class="data-value">' + totalReadCount + '</span></div>' + '<div class="data-item"><span class="data-label">头条阅读</span><span class="data-value">' + headlineReadCount + '</span></div>' + '<div class="data-item"><span class="data-label">最高阅读</span><span class="data-value">' + maxReadCount + '</span></div>' + '<div class="data-item"><span class="data-label">点赞</span><span class="data-value">' + totalLikeCount + '</span></div>' + '<div class="data-item"><span class="data-label">在看</span><span class="data-value">' + totalInSeeCount + '</span></div>' + '<div class="data-item"><span class="data-label">转发</span><span class="data-value">' + totalForwardCount + '</span></div>' + '</div>' + '</div>'; }} document.getElementById('accountList').innerHTML = html; }})(); function _hideAvatarsForExport() {{ var imgs = document.querySelectorAll('img.export-hide'); imgs.forEach(function(img) {{ img.setAttribute('data-original-src', img.src); img.style.display = 'none'; }}); }} function _restoreAvatarsAfterExport() {{ var imgs = document.querySelectorAll('img.export-hide'); imgs.forEach(function(img) {{ var origSrc = img.getAttribute('data-original-src'); if (origSrc) {{ img.src = origSrc; img.removeAttribute('data-original-src'); }} img.style.display = ''; }}); }} function exportImage() {{ var btn = document.querySelector('.btn-export-img'); btn.textContent = '生成中...'; btn.style.pointerEvents = 'none'; var target = document.getElementById('pdfContent'); _hideAvatarsForExport(); html2canvas(target, {{ scale: 2, useCORS: true, allowTaint: true, backgroundColor: '#f5f5f5', logging: false, windowWidth: target.scrollWidth, windowHeight: target.scrollHeight }}).then(function(canvas) {{ _restoreAvatarsAfterExport(); var link = document.createElement('a'); link.download = 'gzh_power_account_' + new Date().toISOString().slice(0,10) + '.png'; link.href = canvas.toDataURL('image/png'); link.click(); btn.textContent = '导出图片'; btn.style.pointerEvents = ''; }}).catch(function(err) {{ _restoreAvatarsAfterExport(); alert('图片生成失败:' + err.message); btn.textContent = '导出图片'; btn.style.pointerEvents = ''; }}); }} function exportPdf() {{ var btn = document.querySelector('.btn-export-pdf'); btn.textContent = '生成中...'; btn.style.pointerEvents = 'none'; var target = document.getElementById('pdfContent'); _hideAvatarsForExport(); html2canvas(target, {{ scale: 2, useCORS: true, allowTaint: true, backgroundColor: '#f5f5f5', logging: false, windowWidth: target.scrollWidth, windowHeight: target.scrollHeight }}).then(function(canvas) {{ _restoreAvatarsAfterExport(); var imgData = canvas.toDataURL('image/png', 1.0); var pdf = new jspdf.jsPDF('l', 'mm', 'a4'); var pdfW = pdf.internal.pageSize.getWidth(); var pdfH = pdf.internal.pageSize.getHeight(); var margin = 8; var contentW = pdfW - margin * 2; var contentH = pdfH - margin * 2; var imgW = contentW; var imgH = (canvas.height * imgW) / canvas.width; if (imgH > contentH) {{ imgH = contentH; imgW = (canvas.width * imgH) / canvas.height; }} var imgX = (pdfW - imgW) / 2; var imgY = (pdfH - imgH) / 2; pdf.addImage(imgData, 'PNG', imgX, imgY, imgW, imgH); var now = new Date(); var dateStr = now.getFullYear() + String(now.getMonth()+1).padStart(2,'0') + String(now.getDate()).padStart(2,'0') + '_' + String(now.getHours()).padStart(2,'0') + String(now.getMinutes()).padStart(2,'0'); pdf.save('gzh_power_account_' + dateStr + '.pdf'); btn.textContent = '导出 PDF'; btn.style.pointerEvents = ''; }}).catch(function(err) {{ _restoreAvatarsAfterExport(); alert('PDF 生成失败:' + err.message); btn.textContent = '导出 PDF'; btn.style.pointerEvents = ''; }}); }} </script> </body> </html>''' # Replace js_data placeholder html = html.replace("{js_data}", js_data) return html if __name__ == "__main__": import argparse parser = argparse.ArgumentParser(description='生成公众号热门账号推荐榜HTML页面') parser.add_argument('--rank_type', type=str, default='day', choices=['day', 'week', 'month'], help='榜单类型') parser.add_argument('--rank_date', type=str, help='查询日期,格式 YYYY-MM-DD') parser.add_argument('--category', type=str, default=None, help='分类名称') parser.add_argument('--keyword', type=str, help='用户输入的关键词,用于自动匹配分类') parser.add_argument('--output', type=str, help='输出文件路径') parser.add_argument('--top', type=int, default=50, help='显示条数,默认50') parser.add_argument('--input_json_file', type=str, default=None, help='预取数据JSON文件路径(来自gzh_growth_fetcher输出),指定后不再调用API') args = parser.parse_args() if args.input_json_file: # 使用预取数据,不调用 API if not os.path.isfile(args.input_json_file): print(f"错误: 文件不存在: {args.input_json_file}", file=sys.stderr) sys.exit(1) try: with open(args.input_json_file, 'r', encoding='utf-8') as f: fetched = json.load(f) except Exception as e: print(f"错误: 读取文件失败: {e}", file=sys.stderr) sys.exit(1) raw_data = fetched.get("raw_data", []) if not raw_data: print("错误: 输入文件中无 raw_data 数据", file=sys.stderr) sys.exit(1) account_list = raw_data_to_account_list(raw_data) rank_type = fetched.get("rank_type", "day") rank_date = fetched.get("rank_date", "") category = fetched.get("category", "人文资讯") rank_type_label = {"day": "日榜", "week": "周榜", "month": "月榜"}.get(rank_type, "日榜") if args.keyword or args.category: print("注意: 已指定 --input_json_file,忽略 --keyword/--category 参数", file=sys.stderr) result = { "fetch_time": fetched.get("update_time", ""), "rank_type": rank_type, "rank_date": rank_date, "category": category, "time_range": _get_data_time_range(rank_type, rank_date), "account_list": account_list } print(f"使用预取数据生成HTML(不调用API)...", file=sys.stderr) print(f"榜单类型: {rank_type_label}", file=sys.stderr) print(f"查询日期: {rank_date}", file=sys.stderr) print(f"分类:{category}", file=sys.stderr) else: # 原有逻辑:调用 API 获取数据 # 处理分类 if args.keyword and not args.category: category = match_category(args.keyword) print(f"根据关键词【{args.keyword}】匹配到分类:【{category}】", file=sys.stderr) elif args.category: category = args.category else: category = "人文资讯" # 处理日期 rank_date, is_auto, reminder = get_query_date(args.rank_type, args.rank_date) rank_type_label = {"day": "日榜", "week": "周榜", "month": "月榜"}.get(args.rank_type, "日榜") print(f"正在获取公众号综合实力{rank_type_label}数据...", file=sys.stderr) print(f"当前时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", file=sys.stderr) if reminder: print(f"提醒: {reminder}", file=sys.stderr) print(f"榜单类型: {rank_type_label}", file=sys.stderr) print(f"查询日期: {rank_date}", file=sys.stderr) print(f"分类:{category}", file=sys.stderr) result = fetch_gzh_growth(rank_type=args.rank_type, rank_date=rank_date, category=category) html = generate_html(result, top_n=args.top) if args.output: output_path = args.output else: rank_short = {"day": "日", "week": "周", "month": "月"}.get(result.get("rank_type", args.rank_type), "日") timestamp_str = datetime.now().strftime("%Y%m%d")[-8:] + f"{datetime.now().strftime('%H%M%S')}" filename = f"公众号综合实力{rank_short}_{result.get('category', category)}_{timestamp_str}.html" output_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), filename) with open(output_path, 'w', encoding='utf-8') as f: f.write(html) print(f"已生成:{output_path}", file=sys.stderr) print(f"共 {len(result['account_list'])} 条{rank_type_label}数据,展示TOP{args.top}", file=sys.stderr) -
gzh_growth_fetcher.py 33.1 KB
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """公众号热门账号推荐榜数据获取脚本 通过HTTP请求获取公众号热门账号推荐榜单数据,支持日榜/周榜/月榜,22个分类查询。 用法: python scripts/gzh_growth_fetcher.py --rank_type day --keyword 科技 --top_n 50 python scripts/gzh_growth_fetcher.py --rank_type week --category 科技数码 python scripts/gzh_growth_fetcher.py --rank_type month --category 总排名 --top_n 50 python scripts/gzh_growth_fetcher.py --list_categories """ import argparse import json import os import sys import pathlib from datetime import datetime, timedelta from urllib.parse import quote import requests # ===== 常量 ===== API_URL = "https://redfox.hk/story/api/cozeSkill/getGzhCozeSkillDataIndex" SOURCE = "公众号综合实力账号榜-GitHub" CATEGORIES = [ "乐活生活", "人文资讯", "企业品牌", "体育娱乐", "健康养生", "创投商业", "学术研究", "情感心理", "房产楼市", "搞笑幽默", "教育考试", "文摘精选", "旅游出行", "时尚潮流", "民生资讯", "汽车交通", "知识百科", "科技数码", "美容美体", "美食餐饮", "职场发展", "财富理财", "总排名" ] RANK_TYPE_MAP = { "day": "日榜", "week": "周榜", "month": "月榜" } # 榜单更新时间规则 # 日榜:每日17:30更新昨日数据 # 周榜:每周一17:30更新上周数据 # 月榜:每月3号23:00更新上月数据 DAY_UPDATE_HOUR = 17 DAY_UPDATE_MINUTE = 30 MONTH_UPDATE_DAY = 3 MONTH_UPDATE_HOUR = 23 MONTH_UPDATE_MINUTE = 0 DATA_DESCRIPTION_TEMPLATE = "公众号综合实力{rank_label},基于阅读、点赞、转发、在看等多维数据综合排名\n数据统计时间周期:{time_range}" def get_data_time_range(rank_type, rank_date): """根据榜单类型和查询日期生成数据统计时间周期描述""" from datetime import datetime, timedelta import calendar date_obj = datetime.strptime(rank_date, "%Y-%m-%d") if rank_type == "day": return f"{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 # 日期查询范围限制 DAY_MAX_DAYS_BACK = 7 WEEK_MAX_WEEKS_BACK = 3 MONTH_MAX_MONTHS_BACK = 3 # ===== 日期计算 ===== def _is_after_day_update(now=None): """判断当前时间是否已过日榜/周榜更新时间(17:30)""" 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号23:00)""" if now is None: now = datetime.now() # 本月3号23:00 try: cutoff = now.replace(day=MONTH_UPDATE_DAY, hour=MONTH_UPDATE_HOUR, minute=MONTH_UPDATE_MINUTE, second=0, microsecond=0) except ValueError: # 2月没有30号等情况不会出现(3号一定存在) 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": # 日榜: 每日17:30更新昨日数据 # 17:30后 → 查昨天; 17:30前 → 查前天 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": # 周榜: 每周一17:30更新上周数据 # 周一17:30后 → 查本周一; 周一17:30前 → 查上周一 weekday = now.weekday() # 0=Monday this_monday = now - timedelta(days=weekday) if _is_after_day_update(now): # 17:30后 → 本周一 return this_monday.strftime("%Y-%m-%d") else: # 17:30前 → 上周一 last_monday = this_monday - timedelta(weeks=1) return last_monday.strftime("%Y-%m-%d") elif rank_type == "month": # 月榜: 每月3号23:00更新上月数据 # rankDate传上月1号 = 上月数据 # 3号23:00后 → 上月1号(=上月数据已更新); 3号23:00前 → 上上月1号(=上上月数据) if _is_after_month_update(now): # 已过3号23:00 → 上月1号 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: # 未过3号23:00 → 上上月1号 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": # 最近7天 earliest = now - timedelta(days=DAY_MAX_DAYS_BACK) return earliest.strftime("%Y-%m-%d") elif rank_type == "week": # 最近3周 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": # 最近3个月 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个月的月榜」,' '我将为您查询最接近您需求时间的{}数据⭐~' ).format(RANK_TYPE_MAP.get(rank_type, "日榜")) return { "original_date": user_date_str, "adjusted_date": adjusted, "is_adjusted": True, "reminder": reminder } def get_today_query_reminder(rank_type="day"): """当用户查询今日日榜但数据未更新时,生成提醒 Args: rank_type: day/week/month Returns: str: 提醒消息,不需要提醒时返回空字符串 """ 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 "" # ===== 认证 ===== def _get_redfox_api_key(): """三级认证回退获取 红狐Hub API Key 优先级: 1. 环境变量 REDFOX_API_KEY 2. Shell 配置文件(.zshrc / .bashrc / PowerShell Profile) 3. 返回 None,由上层提示用户配置 Returns: str or None: API Key """ # 第一级:环境变量 api_key = os.getenv("REDFOX_API_KEY") if api_key and api_key.startswith("ak_"): return api_key # 第二级:Shell 配置文件 home = pathlib.Path.home() config_files = [] if sys.platform == "win32": # PowerShell Profile ps_dir = home / "Documents" / "WindowsPowerShell" if ps_dir.exists(): for f in ps_dir.glob("Microsoft.PowerShell_profile.ps1"): config_files.append(f) ps7_dir = home / "Documents" / "PowerShell" if ps7_dir.exists(): for f in ps7_dir.glob("Microsoft.PowerShell_profile.ps1"): config_files.append(f) # Git Bash / MSYS2 for name in [".bashrc", ".bash_profile", ".profile"]: p = home / name if p.exists(): config_files.append(p) else: for name in [".zshrc", ".bashrc", ".bash_profile", ".profile"]: p = home / name if p.exists(): config_files.append(p) for config in config_files: try: content = config.read_text(encoding="utf-8", errors="ignore") for line in content.splitlines(): line = line.strip() # 匹配 export REDFOX_API_KEY=xxx 或 set REDFOX_API_KEY=xxx if line.startswith("export REDFOX_API_KEY=") or line.startswith("set REDFOX_API_KEY="): parts = line.split("=", 1) if len(parts) == 2: key = parts[1].strip().strip('"').strip("'") if key and key.startswith("ak_"): return key # 匹配 PowerShell: $env:REDFOX_API_KEY = "xxx" if "$env:REDFOX_API_KEY" in line: parts = line.split("=", 1) if len(parts) == 2: key = parts[1].strip().strip('"').strip("'") if key and key.startswith("ak_"): return key except Exception: continue return None def _get_api_headers(): """获取API请求头,含X-API-KEY凭证 三级回退:环境变量 → Shell配置 → 报错提示 """ api_key = _get_redfox_api_key() if not api_key: raise ValueError( "未检测到 红狐Hub API Key。请按以下步骤配置:\n" "1. 访问 https://redfox.hk/?source=github 了解服务详情\n" "2. 前往 https://redfox.hk/login?source=github 注册账号(新用户获赠免费积分)\n" "3. 注册登录后在个人中心获取 API Key(格式 ak_xxxxxxxx)\n" "4. 设置环境变量:\n" " macOS/Linux: export REDFOX_API_KEY=<你的apikey>\n" " Windows PowerShell: $env:REDFOX_API_KEY = \"<你的apikey>\"\n" " 或告知我帮你自动配置到 Shell 配置文件中" ) return { "Content-Type": "application/json", "X-API-KEY": api_key, } # ===== 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: dict: API返回的完整数据 """ if rank_date is None: rank_date = get_latest_query_date(rank_type) params = { "rankType": rank_type, "rankDate": rank_date, "category": category, "source": SOURCE, } headers = _get_api_headers() try: response = requests.get(API_URL, params=params, headers=headers, timeout=30) if response.status_code >= 400: raise ValueError(f"HTTP请求失败: {response.status_code}, {response.text}") result = response.json() except requests.exceptions.RequestException as e: raise ValueError(f"请求失败: {str(e)}") if result.get("code") != 2000: raise ValueError(f"API返回错误: code={result.get('code')}, msg={result.get('msg', '未知错误')}") return result # ===== 分类匹配 ===== CATEGORY_KEYWORDS = { "总排名": ["总排名", "综合", "全部", "热门", "推荐", "随便", "总榜", "整体"], "乐活生活": ["乐活", "生活", "日常", "生活方式", "生活日常", "好物推荐"], "人文资讯": ["人文", "资讯", "文化", "历史", "哲学", "人文社科"], "企业品牌": ["企业", "品牌", "公司", "商业品牌", "品牌营销"], "体育娱乐": ["体育", "娱乐", "运动", "健身", "篮球", "足球", "综艺", "明星"], "健康养生": ["健康", "养生", "保健", "中医", "调理", "减肥", "瘦身"], "创投商业": ["创投", "商业", "投资", "创业", "融资", "商业模式"], "学术研究": ["学术", "研究", "论文", "科研", "学报"], "情感心理": ["情感", "心理", "恋爱", "婚姻", "情绪", "心理咨询"], "房产楼市": ["房产", "楼市", "买房", "房价", "地产", "租房", "装修"], "搞笑幽默": ["搞笑", "幽默", "段子", "吐槽", "沙雕", "表情包"], "教育考试": ["教育", "考试", "培训", "考研", "考公", "留学", "英语", "学习"], "文摘精选": ["文摘", "精选", "美文", "散文", "故事", "文章精选"], "旅游出行": ["旅游", "出行", "旅行", "攻略", "景点", "酒店", "度假"], "时尚潮流": ["时尚", "潮流", "穿搭", "服饰", "OOTD", "ootd", "搭配"], "民生资讯": ["民生", "社会", "热点", "时事", "政策", "民生新闻"], "汽车交通": ["汽车", "交通", "车", "新能源", "电动车", "买车"], "知识百科": ["知识", "百科", "科普", "常识", "冷知识"], "科技数码": ["科技", "数码", "手机", "电脑", "智能", "AI", "互联网", "软件", "硬件"], "美容美体": ["美容", "美体", "护肤", "化妆", "美妆", "彩妆"], "美食餐饮": ["美食", "餐饮", "做饭", "烹饪", "餐厅", "探店", "食谱"], "职场发展": ["职场", "工作", "求职", "面试", "跳槽", "升职", "加薪", "简历"], "财富理财": ["财富", "理财", "财经", "基金", "股票", "保险", "财务", "赚钱"], } def match_category(keyword): """根据关键词匹配分类 Args: keyword: 搜索关键词 Returns: str or None: 匹配到的分类名,多个匹配时返回列表 """ # 精确匹配分类名 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 get_comprehensive_score(item): """获取综合评分(满分100),直接采用接口返回的comprehensiveScore字段 Args: item: 单条数据 Returns: float: 综合评分(0-100) """ try: return round(float(item.get("comprehensiveScore", 0)), 1) except (ValueError, TypeError): return 0.0 def format_ranking_table(data_list, top_n=50, start=1): """格式化榜单为Markdown表格(严格遵循core_workflow.md标准模板) 表格字段: 排名 | 账号名称 | 综合评分(满分100) | 发布数/文章数 | 总阅读数 | 头条阅读数 | 最高阅读数 | 总点赞 | 总在看 | 总转发 Args: data_list: API返回的data数组 top_n: 显示前N条 start: 起始排名(1-based),用于"查看更多"时从TOP21开始 Returns: str: Markdown表格字符串 """ # start为1时从第1条开始,start为21时从第21条开始 items = data_list[start-1:start-1+top_n] header = "| 排名 | 账号名称 | 综合评分(满分100) | 发布数/文章数 | 总阅读数 | 头条阅读数 | 最高阅读数 | 总点赞 | 总在看 | 总转发 |" separator = "|:---:|:---|---:|:---:|---:|---:|---:|---:|:---:|---:|" rows = [] for idx, item in enumerate(items): # 使用实际索引位置判断奖牌,避免API返回的rankPosition重复 display_rank = start + idx name = item.get("accountName", "-") account_id = item.get("accountId", "") name_link = f"[{name}](https://open.weixin.qq.com/qr/code?username={quote(account_id)})" score = get_comprehensive_score(item) publish = item.get("publishCount", "-") total_read = format_number(item.get("totalReadCount", 0)) headline_read = format_number(item.get("headlineReadCount", 0)) max_read = format_number(item.get("maxReadCount", 0)) like_count = format_number(item.get("totalLikeCount", 0)) in_see_count = format_number(item.get("totalInSeeCount", 0)) forward_count = format_number(item.get("totalForwardCount", 0)) rank_medals = {1: "🥇", 2: "🥈", 3: "🥉"} if display_rank in rank_medals: # TOP3: 账号名称加粗,综合评分/总阅读/头条阅读/最高阅读/总点赞/总转发加粗 row = f"| {rank_medals[display_rank]} | **{name_link}** | **{score}** | {publish} | **{total_read}** | **{headline_read}** | **{max_read}** | **{like_count}** | **{in_see_count}** | **{forward_count}** |" else: # 非TOP3: 账号名称不加粗,但综合评分/总阅读/头条阅读/最高阅读/总点赞/总转发仍需加粗 row = f"| {display_rank} | {name_link} | **{score}** | {publish} | **{total_read}** | **{headline_read}** | **{max_read}** | **{like_count}** | **{in_see_count}** | **{forward_count}** |" rows.append(row) return "\n".join([header, separator] + rows) # 功能询问文案模板(一字不改,遵循core_workflow.md) FEATURE_PROMPT = """技能还支持以下功能: 1️⃣ 支持对以上数据进行分析。其中包含**内容生态变化分析**、**热点影响力量化**、**蓝海赛道挖掘**三种分析角度,方便您了解公众号阅读数据或获取运营参考时使用。 2️⃣ 可生成公众号风格的html文件包。支持**导出图片**、**导出PDF**,方便阅读、保存和分享。 3️⃣ 支持订阅每日/周/月的公众号账号最新排名,订阅后将定时推送给您(日榜将在每日17点30更新/周榜将在每周一17:30准时更新/月榜将在每月3号23:00准时推送)。直接回复"订阅日/周/月榜"即可订阅相关榜单,或者"全部订阅"将会为您推送全部类型的榜单。 回复数字即可~""" def format_full_output(rank_label, category, update_time, data_description, time_range, reminder, data_list, top_n=50, start=1): """格式化完整的榜单输出(严格遵循core_workflow.md标准模板) 输出顺序:标题 → 数据说明 → reminder → 表格 → 功能询问文案 Args: rank_label: 榜单标签(日榜/周榜/月榜) category: 分类名 update_time: 更新时间 data_description: 数据说明文本 time_range: 数据统计时间周期 reminder: 日期提醒(可为空) data_list: API返回的data数组 top_n: 显示前N条 start: 起始排名 Returns: str: 完整的Markdown输出 """ parts = [] # 标题(二级标题) parts.append(f"## 公众号热门账号推荐榜{rank_label} - {category} (更新时间:{update_time})") parts.append("") # 数据说明(斜体,在标题后、表格前) parts.append(f"*公众号综合实力{rank_label},基于阅读、点赞、转发、在看等多维数据综合排名*") parts.append(f"*数据统计时间周期:{time_range}*") parts.append("") # reminder(如有) if reminder: parts.append(reminder) parts.append("") # 榜单表格 parts.append(format_ranking_table(data_list, top_n, start)) parts.append("") # 功能询问文案(一字不改) parts.append(FEATURE_PROMPT) parts.append("") parts.append("另外红狐配套全量数据库可提供完整详实数据,如需了解采购方案,可发送邮件至 redfoxdata@proton.me 对接咨洵") return "\n".join(parts) def format_analysis(data_list, rank_type="day", top_n=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"] for i, item in enumerate(items): name = item.get("accountName", "未知") score = get_comprehensive_score(item) total_read = item.get("totalReadCount", 0) headline_read = item.get("headlineReadCount", 0) max_read = item.get("maxReadCount", 0) like_count = item.get("totalLikeCount", 0) forward_count = item.get("totalForwardCount", 0) in_see_count = item.get("totalInSeeCount", 0) publish = item.get("publishCount", "-") lines.append(f"**第{i+1}名:{name}(综合评分:{score})**") lines.append(f"- 风格:总阅读{format_number(total_read)},头条阅读{format_number(headline_read)},最高阅读{format_number(max_read)}") lines.append(f"- 特征:发布{publish},获赞{format_number(like_count)}次,在看{format_number(in_see_count)}次,转发{format_number(forward_count)}次") # 分析建议 try: total_int = int(total_read) if total_read else 0 except (ValueError, TypeError): total_int = 0 try: like_int = int(like_count) if like_count else 0 except (ValueError, TypeError): like_int = 0 interaction_rate = f"{like_int / total_int * 100:.2f}%" if total_int and total_int > 0 else "N/A" lines.append(f"- 建议:互动率{interaction_rate},{'内容传播力强,可作为竞品研究标杆' if score >= 80 else '内容稳定产出,关注互动提升空间'}") lines.append("") return "\n".join(lines) def get_update_time_label(rank_type, rank_date): """获取榜单更新时间标签 Args: rank_type: day/week/month rank_date: 查询日期 Returns: str: 更新时间标签 """ if rank_type == "day": # 日榜:查询日期的次日17:30 d = datetime.strptime(rank_date, "%Y-%m-%d") update_time = d + timedelta(days=1) return f"{update_time.strftime('%Y-%m-%d')} 17:30" elif rank_type == "week": # 周榜:查询周一的下一个周一17:30 d = datetime.strptime(rank_date, "%Y-%m-%d") next_monday = d + timedelta(weeks=1) return f"{next_monday.strftime('%Y-%m-%d')} 17:30" elif rank_type == "month": # 月榜:查询月份的次月3号23:00 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')} 23: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数据") parser.add_argument("--input_json_file", type=str, default=None, help="预取数据JSON文件路径(来自本脚本之前的输出),指定后不再调用API") args = parser.parse_args() # 使用预取数据模式 if args.input_json_file: if not os.path.isfile(args.input_json_file): print(f"错误: 文件不存在: {args.input_json_file}", file=sys.stderr) sys.exit(1) try: with open(args.input_json_file, 'r', encoding='utf-8') as f: fetched = json.load(f) except Exception as e: print(f"错误: 读取文件失败: {e}", file=sys.stderr) sys.exit(1) raw_data = fetched.get("raw_data", []) if not raw_data: print("错误: 输入文件中无 raw_data 数据", file=sys.stderr) sys.exit(1) rank_type = fetched.get("rank_type", "day") rank_date = fetched.get("rank_date", "") category = fetched.get("category", "人文资讯") rank_label = fetched.get("rank_label", RANK_TYPE_MAP.get(rank_type, "日榜")) update_time = fetched.get("update_time", "") reminder = fetched.get("reminder", "") print(f"使用预取数据重新处理(不调用API)...", file=sys.stderr) print(f"榜单类型: {rank_label}", file=sys.stderr) print(f"查询日期: {rank_date}", file=sys.stderr) print(f"分类: {category}", file=sys.stderr) # 从预取数据重新格式化 data_list = raw_data top_n = min(args.top_n, len(data_list)) time_range = get_data_time_range(rank_type, rank_date) output = { "status": "success", "rank_type": rank_type, "rank_label": rank_label, "rank_date": rank_date, "category": category, "data_description": fetched.get("data_description", ""), "total_count": fetched.get("total_count", len(data_list)), "top_n": top_n, "update_time": update_time, "reminder": reminder, "ranking_table": format_ranking_table(data_list, args.top_n, args.start), "analysis": format_analysis(data_list, rank_type), "raw_data": data_list, "formatted_markdown": format_full_output( rank_label, category, update_time, fetched.get("data_description", ""), time_range, reminder, data_list, args.top_n, args.start ), } # 输出JSON到stdout,确保Windows下GBK终端也能正确输出 json_str = json.dumps(output, ensure_ascii=False) try: sys.stdout.write(json_str + "\n") sys.stdout.flush() except UnicodeEncodeError: # Windows GBK终端无法编码emoji等字符时,回退到UTF-8编码 sys.stdout.buffer.write(json_str.encode('utf-8', errors='replace') + b"\n") sys.stdout.buffer.flush() return # 列出分类 if args.list_categories: print("可用分类列表:", file=sys.stderr) for i, cat in enumerate(CATEGORIES, 1): print(f" {i}. {cat}", file=sys.stderr) 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)}", file=sys.stderr) return elif isinstance(matched, list): print(f"关键词'{args.keyword}'匹配到多个分类:{', '.join(matched)},请指定更精确的分类名", file=sys.stderr) return else: category = matched print(f"根据关键词【{args.keyword}】匹配到分类:【{category}】", file=sys.stderr) # 确定查询日期 rank_label = RANK_TYPE_MAP.get(args.rank_type, "日榜") print(f"正在获取公众号综合实力{rank_label}数据...", file=sys.stderr) print(f"当前时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", file=sys.stderr) 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}", file=sys.stderr) print(f"提醒: {reminder}", file=sys.stderr) 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}", file=sys.stderr) print(f"榜单类型: {rank_label}", file=sys.stderr) print(f"查询日期: {rank_date}", file=sys.stderr) print(f"分类: {category}", file=sys.stderr) print("-" * 60, file=sys.stderr) try: result = fetch_ranking_data( rank_type=args.rank_type, rank_date=rank_date, category=category, ) except Exception as e: print(f"错误: {e}", file=sys.stderr) return data_list = result.get("data", []) if not data_list: print("未获取到数据", file=sys.stderr) return # 原始输出 if args.raw: print(json.dumps(result, ensure_ascii=False, indent=2)) return # 格式化输出 # 综合评分直接采用接口返回的comprehensiveScore字段 scored_data = [] for item in data_list[:args.top_n]: item_copy = dict(item) item_copy["compositeScore"] = get_comprehensive_score(item) scored_data.append(item_copy) update_time = get_update_time_label(args.rank_type, rank_date) time_range = get_data_time_range(args.rank_type, rank_date) output = { "status": "success", "rank_type": args.rank_type, "rank_label": rank_label, "rank_date": rank_date, "category": category, "data_description": DATA_DESCRIPTION_TEMPLATE.format(rank_label=rank_label, time_range=time_range), "total_count": len(data_list), "top_n": min(args.top_n, len(data_list)), "update_time": update_time, "reminder": reminder, "ranking_table": format_ranking_table(data_list, args.top_n, args.start), "analysis": format_analysis(data_list, args.rank_type), "raw_data": scored_data, "formatted_markdown": format_full_output( rank_label, category, update_time, DATA_DESCRIPTION_TEMPLATE.format(rank_label=rank_label, time_range=time_range), time_range, reminder, data_list, args.top_n, args.start ), } # 输出JSON到stdout,确保Windows下GBK终端也能正确输出 json_str = json.dumps(output, ensure_ascii=False) try: sys.stdout.write(json_str + "\n") sys.stdout.flush() except UnicodeEncodeError: # Windows GBK终端无法编码emoji等字符时,回退到ASCII编码 sys.stdout.buffer.write(json_str.encode('utf-8', errors='replace') + b"\n") sys.stdout.buffer.flush() if __name__ == "__main__": main()
-
-
README.en.md 7 KB
# WeChat Top Accounts / wechat-top-account --- ## Introduction Quickly access the WeChat Official Account comprehensive ranking TOP 50, helping operators and brands understand the reading and engagement data of top accounts in their niche — supporting operational benchmarking and competitor tracking. **Core Value** - Covers TOP 50 rankings across 23 vertical categories, with flexible switching between daily, weekly, and monthly charts - A comprehensive scoring system based on multiple metrics including reads, likes, in-views, and shares, with a maximum score of 100 - Automatically outputs three types of in-depth analysis: content ecosystem shifts, hot topic impact, and blue-ocean niche discovery - One-click generation of a visual report page with image and PDF export support - Subscribe to daily, weekly, or monthly rankings for automated delivery on each update **Who It's For** - 📱 **WeChat Official Account Operators** — Understand top account performance in your niche to optimize topic selection and publishing cadence - 🏢 **MCN Operations Teams** — Monitor the competitive landscape across managed accounts and generate regular data reports - 🏷️ **Brand Marketing Managers** — Track competitor account rankings and engagement shifts to adjust content strategy - 🚀 **Content Entrepreneurs** — Evaluate competition levels and reading demand across candidate niches to identify blue-ocean opportunities --- ## Features ### Core Functions - **Multi-Dimensional Ranking Query**: Query TOP 50 across 23 categories by daily, weekly, or monthly chart — keywords automatically match the right category - **Comprehensive Scoring System**: A composite score (max 100) based on post count, reads, likes, in-views, shares, and more - **In-Depth Analysis**: Three analytical perspectives: content ecosystem changes, hot topic impact quantification, and blue-ocean niche discovery - **Visual Report Export**: One-click generation of a visual ranking page with image and PDF export support - **Scheduled Subscription Push**: Subscribe to daily, weekly, or monthly rankings for automatic delivery on each update cycle ### Highlights - **Scheduled Updates**: Daily chart refreshes at 17:30, weekly chart every Monday at 17:30, monthly chart on the 3rd at 23:00 - **Controlled Lookback**: Daily charts span the past 7 days, weekly the past 3 weeks, monthly the past 3 months — out-of-range requests auto-adjust with a prompt - **Data Consistency**: The ranking table and visual report share the same data source, with identical order and content - **On-Demand Expansion**: The ranking table outputs directly without confirmation; analysis, reports, and subscriptions are user-initiated --- ## 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`). - Register an account at [RedFoxHub](https://redfox.hk?source=github) to obtain your `REDFOX_API_KEY`. - Configure the device environment variable `REDFOX_API_KEY` before using this skill. - Before providing a key, confirm its source, scope, expiration, and whether it supports reset/revocation. - Never hardcode or expose the key in plaintext in code, prompts, logs, or output files. --- ## Usage Guide Just describe your need in plain language — no commands to memorize. ### Quick Reference | Intent | Example Phrase | Result | | ------------------------------- | -------------------------------------- | ---------------------------------------------------------------------------------------- | | Query daily chart by category | "Show the tech category daily ranking" | Outputs the Tech & Digital daily TOP 50 with composite scores and engagement metrics | | Query weekly chart by category | "This week's food category ranking" | Outputs the Food & Dining weekly TOP 50 with time range noted | | Query monthly chart by category | "Finance category monthly ranking" | Outputs the Finance monthly TOP 50 | | Get in-depth analysis | (After chart output) reply "1" | Outputs three analysis types: content ecosystem, hot topic impact, and blue-ocean niches | | Generate visual report | (After chart output) reply "2" | Generates a visual page exportable as image or PDF | | Subscribe to updates | "Subscribe to weekly ranking" | Sets up automated weekly delivery of the latest chart | | Subscribe to all | "Subscribe to all" | Enables daily, weekly, and monthly chart subscriptions at once | ### Sample Output After querying "Tech category daily ranking", you will receive: 1. **Ranking Table**: Rank, account name (clickable), composite score, post count, total reads, headline reads, peak reads, total likes, total in-views, total shares 2. **Extension Prompt**: Reply "1" for in-depth analysis, "2" for a visual report, "3" or say "Subscribe to daily/weekly/monthly" to enable scheduled delivery --- ## Use Cases | Scenario | Role | Example Query | Benefit | | -------------------------- | ----------------------- | ------------------------------------------ | -------------------------------------------------------------------------------------- | | Daily operations reference | Account Operator | "Show tech category daily ranking" | Compare posting frequency and engagement rates to benchmark and optimize topic cadence | | Competitor tracking | Brand Marketing Manager | "This week's FMCG category ranking" | Track competitor ranking shifts and content direction to adjust your own strategy | | Niche evaluation | Content Entrepreneur | "Compare food and travel monthly rankings" | Assess competition levels across candidate niches and pick a blue-ocean direction | | Regular data reporting | MCN Operations | "Subscribe to monthly ranking" | Export monthly visual reports with consistent and traceable data | --- ## Key Data Notes | Chart | Update Time | Data Range | | ------- | -------------------------- | -------------------------------- | | Daily | Every day at 17:30 | Yesterday's data, past 7 days | | Weekly | Every Monday at 17:30 | Last week's data, past 3 weeks | | Monthly | 3rd of each month at 23:00 | Last month's data, past 3 months | Covers 23 vertical categories, including Overall, Tech & Digital, Food & Dining, Finance, Education, Fashion, Health & Wellness, Emotional & Psychology, Career Development, and more. Just enter a keyword to automatically match the right category. -
README.md 5.5 KB
# 公众号热门账号推荐 / wechat-top-account --- ## 简介 快速获取微信公众号综合实力排行榜 TOP50,帮助运营者和品牌方掌握同赛道头部账号的阅读与互动数据,支撑运营对标与竞品跟踪。 **核心价值** - 覆盖 23 个垂直分类的 TOP50 榜单,支持日榜、周榜、月榜自由切换 - 综合评分体系基于阅读、点赞、在看、转发等多项指标,满分 100 分 - 自动输出内容生态变化、热点影响、蓝海赛道三类深度解读 - 一键生成可视化页面报告,支持导出图片和 PDF - 支持订阅日榜、周榜、月榜,按更新时间自动推送 **适用对象** - 📱 **公众号运营者** — 了解赛道头部账号表现,优化选题与发布节奏 - 🏢 **MCN 机构运营** — 统一监控旗下账号竞争格局,生成定期数据汇报 - 🏷️ **品牌营销经理** — 跟踪竞品公众号排名与互动变化,调整内容策略 - 🚀 **内容创业者** — 评估候选赛道竞争度与阅读需求,选择蓝海方向 --- ## 功能特性 ### 核心功能 - **多维排行查询**:按日榜、周榜、月榜查询 23 个分类的 TOP50,输入关键词自动匹配分类 - **综合评分体系**:基于发布数、阅读量、点赞、在看、转发等多维度综合评分,满分 100 - **深度分析解读**:提供内容生态变化、热点影响力量化、蓝海赛道挖掘三种分析视角 - **可视化报告导出**:一键生成榜单可视化页面,支持导出图片和 PDF - **定时订阅推送**:支持订阅日榜、周榜、月榜,按更新时间自动推送最新数据 ### 特色亮点 - **定时更新**:日榜每日 17:30、周榜每周一 17:30、月榜每月 3 日 23:00 自动刷新 - **有限回溯**:日榜近 7 天、周榜近 3 周、月榜近 3 个月,超出范围自动调整并提示 - **数据一致**:榜单表格与可视化报告共用同一数据来源,顺序与内容完全一致 - **按需扩展**:榜单直接输出无需确认,分析、报告、订阅由用户自主选择 --- ## 密钥获取与安全说明 - 本技能需要使用环境变量:`REDFOX_API_KEY`。 - `REDFOX_API_KEY` 由 [红狐 hub](https://redfox.hk/settings/api-keys?source=github) (`https://redfox.hk?source=github`)提供。 - 请前往 [红狐 hub](https://redfox.hk?source=github) 注册账号,获取 `REDFOX_API_KEY`。 - 配置设备环境变量 `REDFOX_API_KEY` 后使用本技能。 - 在提供密钥前,请先确认密钥来源、可用范围、有效期及是否支持重置/撤销。 - 禁止在代码、提示词、日志或输出文件中硬编码/明文暴露密钥。 --- ## 使用指南 直接用自然语言描述需求,无需记忆命令。 ### 常用说法速查 | 意图 | 示例话术 | 效果 | | -------------- | -------------------- | --------------------------------------------------- | | 查询分类日榜 | 查询科技类公众号日榜 | 输出科技数码分类日榜 TOP50,含综合评分与互动指标 | | 查询分类周榜 | 本周美食类公众号排名 | 输出美食餐饮周榜 TOP50,标注数据时间周期 | | 查询分类月榜 | 财经类公众号月榜 | 输出财经类月榜 TOP50 | | 获取深度分析 | (榜单输出后回复)1 | 基于 TOP50 输出内容生态、热点影响、蓝海赛道三类分析 | | 生成可视化报告 | (榜单输出后回复)2 | 生成可导出图片和 PDF 的榜单页面 | | 订阅推送 | 订阅周榜 | 设置每周定时推送最新榜单 | | 全部订阅 | 全部订阅 | 同时订阅日榜、周榜、月榜 | ### 输出示例 查询「科技类公众号日榜」后,你将收到: 1. **榜单表格**:排名、账号名称(可点击跳转)、综合评分、发布数、总阅读、头条阅读、最高阅读、总点赞、总在看、总转发 2. **扩展功能提示**:回复 1 获取深度分析、回复 2 生成可视化报告、回复 3 或说「订阅日/周/月榜」开启定时推送 --- ## 使用场景 | 场景 | 角色 | 示例问法 | 收益 | | ------------ | ------------ | -------------------- | ------------------------------------------------ | | 日常运营参考 | 公众号运营者 | 查询科技类公众号日榜 | 对比发布频率与互动率,明确对标账号,优化选题节奏 | | 竞品追踪 | 品牌营销经理 | 本周快消类公众号排名 | 跟踪竞品排名波动与内容发力方向,调整自身策略 | | 赛道研判 | 内容创业者 | 对比美食和旅游类月榜 | 评估候选赛道竞争度,选择蓝海方向切入 | | 定期数据汇报 | MCN 机构运营 | 订阅月榜 | 按月导出可视化报告,数据口径统一可追溯 | --- ## 重要数据说明 | 榜单 | 更新时间 | 数据范围 | | ---- | --------------- | ------------------- | | 日榜 | 每日 17:30 | 昨日数据,近 7 天 | | 周榜 | 每周一 17:30 | 上周数据,近 3 周 | | 月榜 | 每月 3 日 23:00 | 上月数据,近 3 个月 | 共覆盖 23 个垂直分类,包括总排名、科技数码、美食餐饮、财经、教育考试、时尚潮流、健康养生、情感心理、职场发展等。输入关键词即可自动匹配对应分类。 -
SKILL.md 13.7 KB
--- name: wechat-top-account description: 公众号热门账号推荐快速获取微信公众号综合实力排行榜TOP50,支持按日榜/周榜/月榜及垂直领域筛选,展示综合评分以及详细数据。支持深度分析单个账号、导出HTML可视化报告、订阅日/周/月榜推送。当用户需要查询公众号实力排行、按时间或领域筛选、查看互动指标、获取榜单解读、导出图表或接收定期推送时使用。 --- # 公众号热门账号推荐 ## 简介 公众号热门账号推荐是一款微信公众号综合实力排行查询工具,帮助用户快速获取同赛道头部账号的阅读与互动数据,支撑运营对标与竞品跟踪。 通过对话或命令行,你可以: - 📊 查询 23 个垂直分类的 TOP50 榜单,支持日榜、周榜、月榜切换 - 📈 查看综合评分(满分 100)及发布数、阅读分布、点赞、在看、转发等指标 - 🔍 获取内容生态、热点影响、蓝海赛道三类数据解读 - 📄 导出 HTML 可视化报告(支持图片/PDF) - 🔔 订阅日/周/月榜,按更新时间自动推送 适用于公众号运营者、MCN 机构、品牌营销人员及需跟踪公众号竞争格局的内容团队。 ## 功能特性 ### 🎯 核心功能 - **📊 多维排行查询**:按日榜、周榜、月榜查询 23 个分类 TOP50,输入关键词自动匹配分类 - **📈 综合评分体系**:基于阅读、点赞、在看、转发等指标计算综合评分,满分 100 - **🔍 深度分析**:提供内容生态变化、热点影响力量化、蓝海赛道挖掘三种解读视角 - **📄 HTML 报告**:生成可视化榜单页面,支持导出图片和 PDF - **🔔 定时订阅**:支持订阅日榜、周榜、月榜,按更新时间自动推送 ### ✨ 特色亮点 - **⚡ 定时更新**:日榜每日 17:30、周榜每周一 17:30、月榜每月 3 日 23:00 自动刷新 - **📅 有限回溯**:日榜近 7 天、周榜近 3 周、月榜近 3 个月,超出范围自动调整并提示 - **🔗 数据一致**:榜单表格与 HTML 报告共用同一 API,顺序与内容完全一致 - **🔒 API 接入**:通过红狐Hub API Key 调用 ## 鉴权 ### 获取 API Key 1. 访问 [红狐Hub 官网](https://redfox.hk/?source=github) 了解服务详情 2. 前往 [注册页面](https://redfox.hk/login?source=github) 注册账号 3. **新注册用户将获赠免费积分**,可立即开始使用 API 服务 4. 注册登录后,在个人中心获取 API Key,格式为 `ak_xxxxxxxx` ### 配置 API Key `REDFOX_API_KEY` 从环境变量获取,格式 `ak_xxxxxxxx`。脚本采用三级认证回退: | 优先级 | 来源 | 说明 | |---|---|---| | 1 | 环境变量 `REDFOX_API_KEY` | 直接读取当前会话的环境变量 | | 2 | Shell 配置文件 | 自动读取 `~/.zshrc` / `~/.bashrc` / `~/.bash_profile` / PowerShell Profile 中的配置 | | 3 | 用户输入 | 若以上均未检测到,提示用户手动配置 | **macOS / Linux**: ```bash echo 'export REDFOX_API_KEY=<你的apikey>' >> ~/.zshrc source ~/.zshrc ``` **Windows**(PowerShell): ```powershell [Environment]::SetEnvironmentVariable("REDFOX_API_KEY", "<你的apikey>", "User") ``` 或将以下内容写入 PowerShell Profile: ```powershell $env:REDFOX_API_KEY = "<你的apikey>" ``` 配置后需重启终端。验证:`echo $REDFOX_API_KEY`(macOS/Linux)或 `$env:REDFOX_API_KEY`(Windows PowerShell)。 ## 一键安装 ### 前置条件 - Python 3.8 及以上版本 - `requests` 库:`pip install requests` - 红狐Hub API Key,格式 `ak_xxxxxxxx` ### 安装方式 #### SkillHub 1. 前往 [SkillHub](https://skillhub.cn) 2. 搜索 **wechat-top-account** 或 **公众号热门账号推荐** 3. 安装说明 - 可通过「通过对话安装」方式安装 - 如未安装 SkillHub 商店,先安装商店再安装 skill - 支持下载 Zip 包到本地,通过本地导入方式安装 #### ClawHub 1. 前往 [ClawHub](https://clawhub.ai) 2. 搜索 **wechat-top-account** 或 **公众号热门账号推荐** 3. 安装说明 - 支持使用 CLI 命令安装:`openclaw skills install wechat-top-account` 或 `clawhub install wechat-top-account` - 支持下载 skill 包到本地并导入安装 - 安装成功后可在对应客户端直接使用 安装完成后,请先完成 [鉴权](#鉴权) 配置 API Key,重启终端即可在对话中发起查询。 #### 方式二:命令行独立使用 无需安装技能包,配置好 API Key 后可直接运行 `scripts/` 下脚本: ```bash pip install requests python scripts/gzh_growth_fetcher.py --list_categories ``` ### 环境变量配置 | 变量名 | 必填 | 说明 | |---|---|---| | `REDFOX_API_KEY` | 是 | 红狐Hub API 访问密钥,格式 `ak_xxxxxxxx` | 详细获取与配置步骤见上方 [鉴权](#鉴权) 章节。 ## 使用指南 ### 基础使用 #### 1. 发起榜单查询 在对话中输入查询需求,系统自动识别榜单类型和分类: > 用户:查询科技类公众号日榜 > > 助手:输出科技数码分类日榜 TOP50 表格(含综合评分、阅读与互动指标),并附功能询问文案。 #### 2. 查看榜单结果 榜单数据直接输出,无需确认。每条结果包含排名、账号名称(可跳转)、综合评分、发布数/文章数、总阅读、头条阅读、最高阅读、总点赞、总在看、总转发。 > 用户:本周美食类公众号排名 > > 助手:输出美食餐饮周榜 TOP50,标注数据时间周期与更新时间。 #### 3. 选择扩展功能 榜单输出后,可按需选择后续服务: > 用户:1 > > 助手:基于 TOP50 数据输出内容生态变化、热点影响、蓝海赛道三类分析。 回复 `1` 获取分析、`2` 生成 HTML 报告、`3` 或「订阅日/周/月榜」开启定时推送。 ### 高级使用 #### 1. 指定分类与日期 通过 `--category` 或 `--keyword` 精确匹配分类,`--rank_date` 指定查询日期(超出范围自动调整): ```bash # 首次查询时必须同时保存到临时文件(唯一一次API调用) python scripts/gzh_growth_fetcher.py --rank_type week --category "美食餐饮" --top_n 50 > /tmp/gzh_result.json 2>/dev/null python scripts/gzh_growth_fetcher.py --rank_type month --keyword "财经" --rank_date 2026-04-01 --top_n 50 > /tmp/gzh_result.json 2>/dev/null ``` #### 2. 生成 HTML 可视化报告 用户选择 HTML 功能后,使用已保存的数据生成可导出图片/PDF 的页面(不调用API): ```bash python scripts/gen_gzh_html.py --input_json_file /tmp/gzh_result.json --top 50 ``` #### 3. 获取深度分析解读 用户确认后,基于当前 TOP50 全量数据输出三段式分析:内容生态变化、热点影响力量化、蓝海赛道挖掘。 #### 4. 订阅定时推送 用户回复「订阅日榜」「订阅周榜」「订阅月榜」或「全部订阅」,系统将按对应更新时间推送最新榜单。 完整参数、输出模板与日期校验逻辑见 [核心工作流程](references/core_workflow.md)。 ### 常用命令速查 | 命令 | 功能 | |---|---| | `python scripts/gzh_growth_fetcher.py --rank_type day --keyword "科技" --top_n 50 > /tmp/gzh_result.json 2>/dev/null` | 科技类日榜 TOP50(首次查询,同时保存文件) | | `python scripts/gzh_growth_fetcher.py --rank_type week --category "美食餐饮" --top_n 50 > /tmp/gzh_result.json 2>/dev/null` | 美食餐饮周榜 TOP50(首次查询,同时保存文件) | | `python scripts/gzh_growth_fetcher.py --rank_type month --keyword "财经" --top_n 50 > /tmp/gzh_result.json 2>/dev/null` | 财经类月榜 TOP50(首次查询,同时保存文件) | | `python scripts/gzh_growth_fetcher.py --list_categories` | 列出全部可用分类 | | `python scripts/gen_gzh_html.py --input_json_file /tmp/gzh_result.json --top 50` | 用预取数据生成 HTML(不调用API) | | `python scripts/gzh_growth_fetcher.py --input_json_file /tmp/gzh_result.json` | 用预取数据重新分析(不调用API) | ## 使用场景 ### 场景一:日常运营参考 **角色**:公众号运营者 **需求**:了解所属赛道头部账号的内容产出与互动表现 **使用方式**: 1. 查询所属分类日榜或周榜 2. 对比发布频率、单篇阅读与互动率 3. 选择分析功能提取内容策略参考 **预期收益**:明确对标账号,优化选题与发布节奏 --- ### 场景二:竞品追踪 **角色**:品牌营销经理 **需求**:跟踪竞品公众号排名与互动变化 **使用方式**: 1. 按周查询竞品所在分类榜单 2. 关注排名波动与阅读/转发变化 3. 订阅周榜定时接收更新 **预期收益**:及时掌握竞品内容发力方向,调整自身策略 --- ### 场景三:赛道研判 **角色**:内容创业者 **需求**:评估候选赛道的竞争度与阅读需求 **使用方式**: 1. 查询多个候选分类的月榜数据 2. 选择蓝海赛道分析功能 3. 对比上榜账号数量与平均阅读/评分 **预期收益**:选择竞争度低、需求旺盛的方向切入 --- ### 场景四:定期数据汇报 **角色**:MCN 机构运营人员 **需求**:为内部复盘或客户汇报提供可视化数据 **使用方式**: 1. 订阅日榜和周榜保持数据更新 2. 按月导出 HTML 报告(图片/PDF) 3. 结合分析功能撰写复盘结论 **预期收益**:提升汇报效率,数据口径统一可追溯 ## 项目架构 ### 目录结构 ``` wechat-top-account/ ├── SKILL.md # 产品说明文档 ├── references/ │ └── core_workflow.md # 核心工作流程(模板、逻辑、步骤) ├── scripts/ │ ├── gzh_growth_fetcher.py # 榜单数据抓取 │ └── gen_gzh_html.py # HTML 可视化报告生成 └── assets/ # 静态资源 ``` ### 技术栈 | 项目 | 说明 | |---|---| | 运行环境 | Python 3.8+ | | 核心依赖 | `requests` | | 数据来源 | 红狐Hub API | | 部署方式 | SkillHub / ClawHub / 命令行脚本 | ### 核心模块 | 模块 | 职责 | |---|---| | `gzh_growth_fetcher.py` | 通过 红狐Hub API 获取榜单数据,支持关键词匹配分类、日期校验与三级认证回退 | | `gen_gzh_html.py` | 将榜单数据渲染为可导出图片和 PDF 的 HTML 页面 | | `core_workflow.md` | 输出格式模板、Checklist、日期计算逻辑、完整操作步骤 | ### 数据更新规则 | 榜单 | 更新时间 | 数据范围 | |---|---|---| | 日榜 | 每日 17:30 | 昨日数据,近 7 天 | | 周榜 | 每周一 17:30 | 上周数据,近 3 周 | | 月榜 | 每月 3 号 23:00 | 上月数据,近 3 个月 | ## 常见问答 ### 安装相关问题 **Q1:未检测到 API Key 怎么办?** 按 [鉴权](#鉴权) 章节步骤注册 红狐Hub 并配置 `REDFOX_API_KEY` 环境变量。新注册用户有免费积分。 **Q2:如何验证配置已生效?** 执行 `echo $REDFOX_API_KEY`,确认输出格式为 `ak_xxxxxxxx`。若未生效,检查终端是否已重启。 **Q3:支持哪些部署方式?** 支持 SkillHub、ClawHub 平台安装,也可直接运行 `scripts/` 下 Python 脚本独立调用。 --- ### 使用相关问题 **Q4:支持哪些分类?** 共 23 个分类:总排名、乐活生活、人文资讯、企业品牌、体育娱乐、健康养生、创投商业、学术研究、情感心理、房产楼市、搞笑幽默、教育考试、文摘精选、旅游出行、时尚潮流、民生资讯、汽车交通、知识百科、科技数码、美容美体、美食餐饮、职场发展、财富理财。 **Q5:数据可回溯多久?** 日榜近 7 天、周榜近 3 周、月榜近 3 个月。超出范围时系统自动调整至最近可用数据。 **Q6:榜单与 HTML 报告数据是否一致?** 两者共用同一 API 接口,数据完全一致。 **Q7:数据多久更新一次?** 日榜每日 17:30、周榜每周一 17:30、月榜每月 3 日 23:00 更新。 --- ### 故障排除 **Q8:查询返回空数据?** 确认分类名称准确(可用 `--list_categories` 列出全部分类),检查查询日期在有效范围内。 **Q9:HTML 生成失败?** 确认已安装 `requests` 库且 API Key 有效,查看脚本错误输出定位原因。 --- ### 安全与许可 **Q10:API Key 如何保管?** `REDFOX_API_KEY` 仅通过环境变量读取,请勿写入代码仓库或公开分享。Key 泄露可能导致积分被消耗。 **Q11:数据来源与使用范围?** 榜单数据由 红狐Hub 平台提供,仅供运营分析、竞品跟踪等合法商业用途,请遵守平台服务条款。 ## 核心工作流程 输出格式模板、数据校验规则、日期计算逻辑、操作步骤及使用示例详见 [references/core_workflow.md](references/core_workflow.md)。 执行任务时须严格遵循 core_workflow.md 中的: - 格式优先级声明与分阶段输出规则 - 输出前必检 Checklist - 榜单标准模板与按需输出模板 - 榜单类型判断与分类匹配规则 - 日榜 / 周榜 / 月榜日期校验逻辑 - 数据获取与格式化输出流程 - API 调用次数上限(同一轮查询仅允许 1 次 API 调用) ## 注意事项 - 热门账号推荐榜直接输出,无需确认;分析、HTML、订阅须用户选择后方可生成 - 榜单表格与 HTML 报告须使用同一数据源,禁止修改接口返回顺序 - **同一轮查询(榜单+分析+HTML)全过程只允许调用 1 次 API 接口**,首次获取数据时必须同时保存到临时文件,后续操作一律使用 `--input_json_file` 读取,禁止重复调用 API - 未收到明确查询指令时,禁止自动调用脚本或输出榜单内容 - 禁止折叠输出 - 脚本输出的 `formatted_markdown` 字段已包含完整的标准模板格式,直接原样输出即可,禁止自行拼装或修改格式
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.