
Xiaohongshu Search
- 263 installs
- 316 repo stars
- Updated August 4, 2026
- redfox-data/redfox-community
Searches trending Xiaohongshu notes by keyword and ranks results by a data-driven popularity score.
About
Runs keyword searches over Xiaohongshu and returns popular notes ranked by an engagement score. Creators use it to discover trends and gather content inspiration.
- Keyword search returning trending note data
- Results ranked by a data-based popularity score
Xiaohongshu Search by the numbers
- 263 all-time installs (skills.sh)
- +17 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #879 of 1,879 Marketing & SEO skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/redfox-data/redfox-community --skill xiaohongshu-searchAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 263 |
|---|---|
| repo stars | ★ 316 |
| Last updated | August 4, 2026 |
| Repository | redfox-data/redfox-community ↗ |
What it does
Searches trending Xiaohongshu notes by keyword and ranks results by a data-driven popularity score.
Files
小红书爆款笔记查询
1. 简介
小红书热门笔记搜索工具,支持按关键词搜索小红书热门爆款笔记,并基于相关性、热度、时效三维评分智能排序推荐。同时提供热门笔记推荐和细分赛道引导,助力创作者、品牌方和 MCN 机构发现热门趋势、获取创作灵感。注意:本工具仅在主 Agent 中执行,不派发给子 Agent。
2. 功能特性
- 🔍 关键词智能搜索 — 支持关键词精确搜索、多关键词组合(逗号分隔)、全站热门查询(空关键词)
- 📊 三维评分排序 — 有关键词时按相关性(满分10分)、热度(满分3分)、时效(满分2分)加权计算总分(满分15分),全站热门按互动数排序
- 🧠 精细意图理解 — 优先从用户描述中提取细分方向词,识别泛化词并自动推荐 10 个细分方向
- ⏱️ 灵活时间范围 — 默认查询最近7天,数据不足时自动扩展时间范围(1天→3天→7天→30天),每日早上7点更新
- 🔥 热门笔记推荐 — 结果较少时自动展示近期热门推荐笔记和热门话题标签
- 📈 细分赛道引导 — 每次查询后主动推荐 10 个相关细分方向,帮助用户深入探索
- 🏷️ 拓词推荐 — 脚本返回 relatedSearches 字段,自动展示相关搜索建议
- 📩 定时订阅推送 — 支持创建日历订阅任务,到达设定时间自动推送最新热门笔记
- 📄 HTML 报告生成 — 自动生成
{keyword}_热门数据.html可视化文件 - 🛡️ 强数据说明 — 热门笔记收录标准为互动数1000+,顶部展示数据说明和排序依据
3. 一键安装
鉴权
获取 API Key
请前往 红狐hub 获取API KEY
配置 API Key
方案1: 以OpenClaw为例,将REDFOX_API_KEY添加到~/.openclaw/openclaw.json中:
{ "env": { "REDFOX_API_KEY": "ak_xxxx..." } }方案2: 终端配置
export REDFOX_API_KEY="ak_xxxx..."依赖安装
本 Skill 使用 Python 3 标准库,无需额外安装第三方依赖。确保系统中已安装 Python 3.x 即可。
环境变量配置
| 环境变量 | 说明 | 是否必填 | 获取方式 |
|---|---|---|---|
REDFOX_API_KEY | 红狐数据 API Key | 是 | 红狐hub |
4. 使用指南
⚠️ 核心执行规则(必须遵守)
1. 泛化词必须先询问再查询:当识别到泛化词时,绝对禁止直接调用脚本,必须先输出细分词推荐并等待用户选择后再执行查询 2. 正确执行顺序:关键词提取 → 判断是否泛化词 → 是泛化词则询问用户 → 用户回复后再调用脚本 3. 强制等待规则:输出细分词推荐后,必须停止执行,等待用户下一轮对话回复「拓展」或「不拓展」,不得在同一次对话中继续执行任何脚本调用
常见泛化词: 泛词:抽象层级高、覆盖范围广的概括性词汇,无具体场景/属性修饰,行业分类等,可包含多个子类。特征:①语义上为上位概念(如"美妆"包含"粉底液/口红";"运动"包含"跑步/瑜伽";如AI);②上下文中常搭配"领域""类型"等概括词(如"美妆领域""运动类型")。
常见具体词: 具体词:抽象层级低、指向明确的实例化词汇,含具体场景/属性修饰,属于某泛词的直接子类。特征:①语义上为下位概念(如"粉底液"是"美妆产品"子类;"生酮饮食"是"饮食方式"子类);②词语结构多含修饰成分(如"春日"→"春日穿搭";"生酮"→"生酮饮食")。
---
基础使用(3 步完成查询)
Step 1 — 提取关键词:从用户自然语言描述中提取搜索关键词。优先提取细分方向词(含具体场景/属性修饰),而非泛化大类词。
Step 2 — 调用脚本:
python scripts/fetch_xhs_hot_articles.py --keyword <关键词> --start-date <日期>- 有赛道关键词:
python scripts/fetch_xhs_hot_articles.py --keyword <关键词> --start-date <日期> - 无赛道关键词(查询全站热门):
python scripts/fetch_xhs_hot_articles.py --keyword "" --start-date <日期> - 多个关键词用逗号分隔:
python scripts/fetch_xhs_hot_articles.py --keyword "减脂餐,职场穿搭,健身" --start-date <日期> - 分页参数:
--page-num 1 --page-size 50
Step 3 — 查看结果:脚本返回结构化 JSON,按本指南规定的展示策略输出结果。
---
高级使用
用户意图理解(查询脚本前)
⚠️ 核心规则:应该语意理解,优先提取用户描述中的细分方向词,而非泛化的大类词
1. 判断用户是否提到赛道关键词:
- 无赛道关键词(如"最近的热门笔记有哪些"、"最近有什么热门内容"、"看看热门数据")→ 直接调用脚本,关键词传空字符串
"",查询全站热门 - 有赛道关键词 → 继续提取和判断
2. 提取精确搜索关键词(仅当用户提到赛道时执行):
- 分析用户描述:从用户自我介绍或需求描述中提取明确的细分领域词
- 示例分析:
- 用户输入:"我是一个文艺类自媒体万粉小红书博主,平时会发小众电影审美积累、书评、乐评、港台文化等相关内容,帮我找电影领域热门话题"
- 分析结果:用户提到的细分方向 = 小众电影、书评、乐评、港台文化
- 将前文场景和"电影"相关,得到细分词 = 小众电影、港台电影、电影乐评
- 搜索关键词:小众电影、港台电影、电影乐评
- ❌ 错误做法:只提取泛化词「电影」去搜索
3. 关键词类型判断(仅当提取到关键词时执行):
- 细分词/垂直赛道(含具体场景/属性修饰的词,如"职场穿搭"、"减脂餐"、"小个子穿搭")→ 直接搜索,无需拓展询问
- 泛化词/分类(纯大类词,如"穿搭"、"美食"、"美妆",无任何修饰)→ 执行拓展策略
- 判断原则:有修饰词(场景/人群/风格/意图)= 细分词,直接搜索;无修饰词 = 泛化词,需要拓展
---
泛化词拓展策略
1. 泛化词处理流程(⚠️ 必须等待用户明确回复后再调用脚本!):
第一步:生成细分词(禁止调用脚本搜索数据)
拓展词生成原则:
- 词的大小适中:词语不要加组合,避免过细(如"中产穿搭"太细,查不到数据)
- 必须覆盖不同场景:趋势词、人群词、场景词、意图词各2-3个
输出示例:
我识别到「中产」是较大的分类,已查询近期热门趋势,推荐以下细分方向:
老钱、轻奢、品质生活、松弛感、高级感穿搭、体面、法式穿搭、律师、医生、品质家居
回复「拓展」将同时搜索这10个词,回复「不拓展」将继续搜索「中产」第二步:等待用户回复
- ❌ 禁止:用户未回复时调用脚本
- ✅ 正确:只等待用户明确回复「拓展」或「不拓展」后再执行
第三步:根据用户明确回复执行
- 用户回复「拓展」 → 调用脚本搜索10个细分词
- 用户回复「不拓展」或「继续」 → 调用脚本搜索原关键词
- 用户未回复或回复其他内容 → 识别对应意图
---
时间范围与数据查询
时间范围:
- 数据库只包含昨天至30天前的数据
- "最近"的默认定义:最近7天(startDate = 今天 - 7天)
- 日期计算(将用户表达转换为 startDate):
- 今天:直接用昨天日期,startDate = 昨天
- 最近/近7天:startDate = 今天 - 7天
- 近N天:startDate = 今天 - N天
- 示例:用户说"近15天" → startDate = 今天 - 15天
数据不足时的自动调整(⚠️ 优先扩展时间,禁止换词!):
- 处理原则:数据不足时,只能扩展时间范围,不能更换或拓展关键词
- 调整顺序:按以下顺序自动扩展时间范围
1. 近1天 → 近3天 2. 近3天 → 近7天 3. 近7天 → 近30天
- 告知用户:自动调整时告知用户:"该关键词近X天数据较少,已自动扩展时间范围至近Y天"
- 禁止行为:❌ 不可因为数据不足就更换关键词、推荐其他词或触发泛化词拓展流程
超出范围或未更新数据的道歉说明:
- 用户说"今天/今日"时:回答"非常抱歉,今天的数据暂未更新,已为您展示最近可用的数据"
- 用户要求的时间超出30天时:回答"非常抱歉,当前仅支持最近30天的数据,已为您展示最接近的数据"
输出文件:
- 筛选后推荐数据:
{keyword}_热门数据.html
---
前置说明(在展示数据前必须告知用户)
- 数据说明:热门笔记范围为互动数1000以上的文章,每日早上7点更新昨日数据。文章互动数据截止为入库时间,不是实时数据,入库后互动数据可能持续增长。
- 排序说明(有关键词搜索时):根据相关性(满分10分)、热度(满分3分)、时效(满分2分)三个维度加权计算,总分共15分
- 排序说明(全站热门/无关键词时):按互动数排序,无评分字段
---
数据展示策略(核心)
⚠️ 强制输出规则:
- ✅ 必须严格按照本步骤规定的格式输出
- ❌ 禁止在输出前添加任何分析或解读
- ❌ 禁止自作主张给建议或方案
- ❌ 禁止询问用户的真实目的或需求
- ✅ 直接读取脚本返回的JSON数据,按照对应策略输出即可
数据字段说明:
- articles:正常笔记数据(主要展示内容)
- latestHotArticles:推荐热门笔记(辅助内容,默认展示10条,表格不含评分字段)
- hotTopics:热门话题(接口返回,仅供参考,不在对话中展示)
A. articles数量 ≥ 10条
展示内容:
1. 时间范围说明:必须告知用户查询的时间范围,如"📅 查询时间范围:5月8日 - 5月19日" 2. 正常笔记数据(有关键词时按totalScore降序排序,全站热门时按互动数排序) 3. 拓词推荐(relatedSearches)
Markdown表格格式:
⚠️ 表格字段顺序必须严格按以下顺序展示:
有关键词时: | 笔记标题 | 作者 | 互动数 | 发布时间 | 相关性 | 热度 | 时效 | 总分 |
全站热门时(无评分字段): | 笔记标题 | 作者 | 互动数 | 发布时间 |
注意:总分字段需要加粗显示(使用**分数**格式)
示例(有关键词): 📅 查询时间范围:5月8日 - 5月19日
| 笔记标题 | 作者 | 互动数 | 发布时间 | 相关性 | 热度 | 时效 | 总分 |
|---|---|---|---|---|---|---|---|
| 职场新人必看:5个让你快速融入团队的技巧 | 职场成长社 | 10.0w | 2026-05-15 | 9.8 | 3.0 | 2.0 | 14.8 |
示例(全站热门): 📅 查询时间范围:5月8日 - 5月19日
| 笔记标题 | 作者 | 互动数 | 发布时间 |
|---|---|---|---|
| 热门笔记标题 | 作者名 | 10.0w | 2026-05-15 |
---
🔤 拓词推荐:职场沟通、职场晋升、打工人
B. articles数量 < 10条但 > 0
展示内容:
1. 时间范围说明:必须告知用户查询的时间范围,如"📅 查询时间范围:5月8日 - 5月19日" 2. 提示信息:"💡当前关键词当前时间段仅找到 X 条结果,您可以尝试拓展词或者拓展时间,我们还为您推荐了近期的热门笔记" 3. 正常笔记数据 4. 拓词推荐(relatedSearches) 5. 推荐热门笔记(latestHotArticles,带"推荐热门笔记"标题分区,默认展示10条) 6. 推荐热门话题
Markdown格式示例:
📅 爆款笔记收录原则为互动数1000以上的文章, 查询时间范围:5月8日 - 5月19日
当前关键词当前时间段仅找到 3 条结果:
有关键词时:
| 笔记标题 | 作者 | 互动数 | 发布时间 | 相关性 | 热度 | 时效 | 总分 |
|---|---|---|---|---|---|---|---|
| 笔记1 | 作者1 | 10.0w | 2026-05-15 | 9.8 | 3.0 | 2.0 | 14.8 |
全站热门时(无评分字段):
| 笔记标题 | 作者 | 互动数 | 发布时间 |
|---|---|---|---|
| 笔记1 | 作者1 | 10.0w | 2026-05-15 |
🔤 拓展词推荐:职场沟通、职场晋升、打工人
💡我们为您推荐了近期的热门笔记供参考,或许对您有帮助:
⚠️ 推荐热门笔记表格不需要评分字段,格式为:
| 笔记标题 | 作者 | 互动数 | 发布时间 |
|---|---|---|---|
| 热门笔记1 | 作者A | 8.5w | 2026-05-14 |
| 热门笔记2 | 作者B | 6.2w | 2026-05-13 |
📈 您还可以尝试搜索以下热门赛道::
穿搭、美食、彩妆、影视、职场、萌宠、家居、旅行、交通、兴趣、科技、互联网、医疗保健、星座情感、婚庆婚礼、拍摄、教育、亲子育儿、个人护理、潮流鞋包、生活、科学探索、新闻资讯、运动
C. articles数量 = 0
⚠️ 必须严格按照以下格式输出,禁止自作主张给建议或分析
展示内容:
**🔍抱歉,爆款笔记收录原则为互动数1000以上的文章,该搜索词在查询时间范围(5月8日 - 5月19日)内太小众,未找到与"XXX"直接相关的内容,你可以尝试用更短/宽泛的关键词重试。**
**推荐搜索词**:从脚本返回的relatedSearches字段中提取推荐词,以加粗形式展示;
💡我们为您推荐了近期的热门笔记供参考,或许对您有帮助:
⚠️ 推荐热门笔记表格不需要评分字段,格式为:
| 笔记标题 | 作者 | 互动数 | 发布时间 |
|---------|------|--------|----------|
| [热门笔记1](url) | [作者A](https://www.xiaohongshu.com/user/profile/xxx) | 8.5w | 2026-05-14 |
| [热门笔记2](url) | [作者B](https://www.xiaohongshu.com/user/profile/xxx) | 6.2w | 2026-05-13 |
📈 您还可以尝试搜索以下热门赛道:
穿搭、美食、彩妆、影视、职场、萌宠、家居、旅行、交通、兴趣、科技、互联网、医疗保健、星座情感、婚庆婚礼、拍摄、教育、亲子育儿、个人护理、潮流鞋包、生活、科学探索、新闻资讯、运动输出规则:
- ✅ 必须直接输出上述格式内容
- ❌ 禁止添加额外的分析或建议
- ❌ 禁止解释为什么搜不到数据
- ❌ 禁止主动提供其他搜索方案
- ❌ 禁止询问用户的真实目的
---
展示逻辑(分页)
当articles数量 > 10条时: 1. 初始默认只展示前10条数据 2. 必须提示用户:
💡 当前共找到 X 条相关笔记(X为实际返回条数),已展示前10条。是否需要查看全部?3. 等待用户回复:
- 用户回复"是"/"查看全部"/"全部" → 展示全部数据
- 用户回复"否"/"不用"/"不需要" → 不展示更多
展示全部数据时的格式:
📊 全部结果(共X条,X为实际返回条数):
有关键词时:
| 笔记标题 | 作者 | 互动数 | 发布时间 | 相关性 | 热度 | 时效 | **总分** |
全站热门时:
| 笔记标题 | 作者 | 互动数 | 发布时间 |
| ...(全部数据)...---
订阅服务询问(必须执行)
当articles数量 > 0时,结果输出完成后必须询问:
📬 订阅服务
1️⃣ 是否需要订阅当前搜索条件笔记,订阅后将定时推送给您?
2️⃣ 暂不需要处理用户回复:
- 用户选择1️⃣ → 使用
calendar_create工具创建日程,订阅当前搜索条件 - 用户选择2️⃣ → 结束当前对话
订阅实现步骤:
1. 告知数据更新时间并询问推送时间:
📅 数据更新时间:每日早上7点更新昨日数据
请告诉我您希望推送的具体时间~2. 用户选择后,调用 `calendar_create` 工具:
- title:
小红书热门笔记订阅:{关键词} - description:记录当前搜索参数(关键词、时间范围)
- start_time:根据用户选择的时间设置
- remind_type:设置为定期提醒
- 其他参数使用当前查询参数
3. 订阅成功后提示:
✅ 订阅创建成功!
📌 订阅信息:
- 关键词:{关键词}
- 时间范围:{当前时间范围}
- 推送时间:{用户选择的时间}
- 数据更新:每日早上7点更新昨日数据
到达设定时间后,将自动为您推送最新的小红书热门笔记。⚠️ 强制规则:
- ✅ 必须在结果输出完成后立即询问
- ✅ 用户选择订阅时,必须使用
calendar_create工具 - ✅ 参数必须使用当前查询参数(关键词、时间范围等)
- ❌ 禁止跳过此步骤
- ❌ 禁止在展示结果前询问
---
推荐细分赛道(⚠️ 展示数据后必须执行)
核心规则:展示完热门数据后,必须主动询问用户是否需要查询更具体的细分赛道
1. 推荐细分词生成:
- 基于当前查询的关键词,生成10个相关的细分方向词
- 生成原则:
- 词的大小适中:避免过细(查不到数据)或过泛(范围太大)
- 覆盖不同维度:场景词、人群词、风格词、意图词各2-3个
- 参考数据表现:优先推荐近期热度较高的方向
2. 输出格式:
以上是「{当前关键词}」的热门数据。
如需深入了解某个细分方向,可以从以下赛道中选择:
{细分词1}、{细分词2}、{细分词3}、{细分词4}、{细分词5}、 {细分词6}、{细分词7}、{细分词8}、{细分词9}、{细分词10}
回复具体关键词,我将为您查询该赛道的热门笔记。3. 示例输出:
以上是「减脂餐」的热门数据。
如需深入了解某个细分方向,可以从以下赛道中选择:
早餐减脂、减脂便当、低卡晚餐、减脂零食、学生党减脂、一周食谱、减脂沙拉、减脂主食、控糖减脂、懒人减脂餐
回复具体关键词,我将为您查询该赛道的热门笔记。4. 特殊情况处理:
- 如果用户查询的是非常细分的词(如"减脂餐一周食谱"),可不再推荐更细的方向;已经拓展过的词不需要再询问拓展
- 如果用户查询的是空关键词(全站热门),直接使用热门赛道列表推荐,格式如下:
以上是全站热门数据。
如需深入了解某个细分赛道,可以从以下热门赛道中选择:
穿搭、美食、彩妆、影视、职场、萌宠、家居、旅行、交通、兴趣、科技、互联网、医疗保健、星座情感、婚庆婚礼、拍摄、教育、亲子育儿、个人护理、潮流鞋包、生活、科学探索、新闻资讯、运动
回复具体关键词,我将为您查询该赛道的热门笔记。---
输出前自检【必须执行】
在输出前,逐项检查输出格式的每一个字段是否完整:
- [] 意图识别:是否准确识别用户搜索意图?
- [] 笔记数据:是否如实展示脚本返回的所有筛选结果(max_items=10时展示10条),每条包含序号(加粗)、笔记标题、作品链接、作者名、作者链接、发布时间、互动数、推荐理由(加粗)?
- [] HTML卡片:是否包含html卡片?
- [] 推荐细分词:是否列出10个细分词?
如有任何字段遗漏或不完整,必须补齐后再输出。
---
常用命令速查表
| 场景 | 命令 |
|---|---|
| 关键词搜索(默认近7天) | python scripts/fetch_xhs_hot_articles.py --keyword "关键词" --start-date <日期> |
| 全站热门 | python scripts/fetch_xhs_hot_articles.py --keyword "" --start-date <日期> |
| 多关键词搜索 | python scripts/fetch_xhs_hot_articles.py --keyword "词1,词2,词3" --start-date <日期> |
| 分页查询 | python scripts/fetch_xhs_hot_articles.py --keyword "关键词" --start-date <日期> --page-num 1 --page-size 50 |
| 生成 HTML 报告 | 脚本自动生成 {keyword}_热门数据.html |
| 订阅创建 | 回复1️⃣后使用 calendar_create 工具创建定时任务 |
5. 使用场景
场景一:小红书博主寻找选题灵感
- 角色:小红书内容创作者
- 需求:想了解近期"减脂餐"领域有哪些热门笔记,为下一篇笔记找选题方向
- 使用方式:输入细分关键词「减脂餐」,系统返回高评分热门笔记列表,展示后自动推荐 10 个细分方向(如"早餐减脂""减脂便当"等)
- 预期收益:通过三维评分排序快速锁定高价值选题,同时获取细分赛道灵感,提升内容产出质量
场景二:品牌方市场调研
- 角色:品牌市场人员
- 需求:了解"穿搭"赛道的整体热门趋势,但不确定具体细分方向
- 使用方式:输入泛化词「穿搭」后,系统推荐 10 个细分方向(如"小个子穿搭""法式穿搭""职场穿搭"等),回复「拓展」批量搜索所有方向
- 预期收益:一次性覆盖多个细分领域,全面掌握赛道热度分布,为品牌内容营销策略提供数据支撑
场景三:MCN 机构达人内容规划
- 角色:MCN 机构内容总监
- 需求:需要一次性查看多个领域(减脂餐、健身、职场穿搭)的热门笔记,规划旗下达人矩阵内容排期
- 使用方式:使用逗号分隔多关键词
--keyword "减脂餐,健身,职场穿搭"进行跨领域查询 - 预期收益:一个命令覆盖多个品类,高效获取跨领域热门趋势,提升内容规划效率
场景四:每日热门笔记定时推送
- 角色:小红书运营人员
- 需求:希望每天上午自动收到特定赛道的热门笔记列表,持续追踪趋势
- 使用方式:搜索关键词后选择「1️⃣ 订阅」,设置推送时间,创建日历定时任务
- 预期收益:无需手动重复搜索,每日定时获取最新热门笔记,持续跟踪赛道动态,保持创作敏感度
6. 项目架构
目录结构
xiaohongshu-search/
├── SKILL.md # Skill 定义与使用文档(本文件)
├── scripts/
│ └── fetch_xhs_hot_articles.py # 核心搜索脚本,调用红狐 API 获取小红书热门笔记
└── references/
└── xhs_hot_article_format.md # 数据字段格式参考文档技术栈
| 组件 | 技术 | 说明 |
|---|---|---|
| 运行环境 | Python 3.x | 标准库,无第三方依赖 |
| 数据接口 | 红狐 API (Redfox) | 通过 REDFOX_API_KEY 鉴权 |
| 输出格式 | JSON (stdout) + HTML (文件) | JSON 通过 stdout 输出供 AI 解析,HTML 为可视化报告文件 |
| 展示格式 | Markdown 表格 | AI 代理将 JSON 渲染为表格展示 |
| 执行限制 | 仅主 Agent | 不在子 Agent 中执行 |
核心模块说明
| 模块 | 路径 | 功能 |
|---|---|---|
| 搜索脚本 | scripts/fetch_xhs_hot_articles.py | 调用红狐 API 获取小红书热门笔记,支持 --keyword、--start-date、--page-num、--page-size 参数,自动生成 HTML 报告 |
| 数据格式参考 | references/xhs_hot_article_format.md | 详细说明接口返回的数据字段格式和输出规范 |
| SKILL 定义 | SKILL.md | 定义 Skill 元数据、意图理解规则、泛化词拓展策略、展示策略、订阅逻辑、自检清单 |
资源索引
- 核心脚本:
scripts/fetch_xhs_hot_articles.py— 调用红狐 API 获取小红书热门笔记数据并生成 HTML 报告 - 参考文档:
references/xhs_hot_article_format.md— 何时读取:需要了解数据字段格式和输出规范时
7. 常见问答
安装
Q: 需要安装哪些依赖? A: 本工具使用 Python 3 标准库,无需额外安装第三方依赖。确保系统已安装 Python 3.x 即可。
Q: 如何获取 API Key? A: 请访问 红狐hub 注册并获取 API Key,按本文"一键安装"章节配置环境变量。
使用
Q: 泛化词和细分词有什么区别? A: 泛化词是抽象层级高、覆盖范围广的概括性词汇(如"美妆""穿搭"),无具体场景/属性修饰;细分词含有具体修饰成分(如"粉底液""小个子穿搭")。泛化词会触发拓展策略,细分词直接搜索。
Q: 数据的时间范围是什么? A: 数据库仅包含昨天至30天前的数据。默认查询最近7天,数据不足时自动扩展时间(1天→3天→7天→30天)。每日早上7点更新昨日数据。
Q: 为什么有的表格有评分字段、有的没有? A: 有关键词搜索时按 totalScore(相关性+热度+时效)排序,展示评分字段;全站热门按互动数排序,无评分字段。推荐热门笔记表格也不含评分。
Q: 如何查看超过 10 条的完整结果? A: 当结果超过 10 条时,系统会提示「是否需要查看全部?」。回复「是」「查看全部」或「全部」即可展示完整数据。
Q: 为什么每次查询后都会推荐细分赛道? A: 这是设计功能 — 帮助用户从当前查询结果中进一步发现更精准的细分方向,深入挖掘数据价值。如果查询的已是细分词,系统会智能判断是否跳过推荐。
故障排除
Q: 搜索结果为空怎么办? A: 热门笔记收录标准为互动数1000+。如果关键词太小众或时间范围太短,可能无数据。系统会自动扩展时间范围,也可从 relatedSearches 推荐的搜索词中选择重试。
Q: 提示"今天的数据暂未更新"? A: 数据库每日早上7点更新昨日数据,当天数据尚未入库。系统会自动展示最近可用的数据。
Q: 脚本执行报错? A: 常见原因:(1) REDFOX_API_KEY 未配置或已过期;(2) Python 版本低于 3.x;(3) 网络连接问题。请逐一排查。
安全许可
Q: API Key 如何安全存储? A: 推荐使用方案 1(配置到 openclaw.json 的 env 字段中),避免在终端历史中泄露。请勿将 API Key 硬编码在脚本中或上传到公开仓库。
Q: 数据来源和版权? A: 数据来源于红狐 API 收录的小红书公开笔记。笔记版权归原作者所有,本工具仅供学习和内容创作参考使用。
Q: 为什么要在主 Agent 中执行? A: 小红书搜索工具涉及完整的意图理解、泛化词拓展和数据展示策略,流程复杂且依赖全局上下文判断,仅在主 Agent 中执行以确保行为一致和规则完整。
Xiaohongshu Trending Note Search
---
Introduction
A Xiaohongshu trending note search tool that helps you quickly find trending notes with 1,000+ engagements, gain creative inspiration, and stay on top of content trends.
Core Value
Continuously indexing trending notes with 1,000+ engagements from Xiaohongshu across the web over the past 30 days, updated at 7 AM daily with yesterday's data. Articles are ranked by a weighted score across relevance, popularity, and timeliness, bringing together top trending content across all categories. Quickly find quality benchmark content across tracks, easily reference trending creative approaches from peers — no more scouring multiple sources for material. A one-stop solution for your daily topic research needs.
Who it's for
- 🎯 Brands — Ride trending topics for seeding content
- 🏢 MCNs — Discover high-engagement potential accounts
- ✍️ Creators — Find benchmark samples and creative inspiration
- 📊 Content ops — Weekly topic selection and competitor analysis
---
Core capabilities
- Keyword search: Enter a track keyword to fetch the most matching Xiaohongshu trending notes
- Data-scored ranking: Weighted scoring across relevance, popularity, and timeliness to recommend optimal notes
- Smart generic-keyword expansion: Recognizes generic keywords and recommends 10 niche directions; retrieves only after user confirmation
- Niche track suggestions: After displaying results, proactively suggests deeper niche directions for step-by-step exploration
- Subscription push: Supports creating calendar subscriptions by keyword for daily push of latest trending notes
Highlights
- Flexible time window: Last 1/3/7/30 days; auto-extends when data is thin and states clearly
- Dual output format: Markdown cards + local HTML visualization file (
{keyword}_trending_data.html)
---
API key source and security
- This skill requires the environment variable:
REDFOX_API_KEY. REDFOX_API_KEYis issued by Redfox Hub (https://redfox.hk) for API authentication.- Before providing the key, confirm its source, available scope, validity period, and whether reset/revocation is supported.
- Do not hard-code or expose the key in plaintext within code, prompts, logs, or output files.
---
Prerequisites
Register a Redfox Hub account to obtain REDFOX_API_KEY
- Get REDFOX_API_KEY (apply at Redfox Hub)
Environment variables
| Variable | Required | Notes |
|---|---|---|
REDFOX_API_KEY | Yes | API access key |
macOS (zsh)
Append one line to the end of ~/.zshrc (replace the value in quotes with your key):
export REDFOX_API_KEY="your_api_key_here"Then run:
source ~/.zshrcWindows (PowerShell)
- Current terminal only: Takes effect immediately after run, no other commands needed; lost when the window is closed.
$env:REDFOX_API_KEY = "your_api_key_here"- Persist to user environment: After running
setx, the current PowerShell window still won't have the variable; you need to close and reopen the terminal (or restart Cursor / VS Code, etc.) for the new window to readREDFOX_API_KEY.
setx REDFOX_API_KEY "your_api_key_here"---
Usage guide
Just describe your needs in natural language — no need to memorize any command format. When you enter a generic keyword, niche directions will be suggested for you to choose from.
Quick phrase reference
| Intent | Example phrase | Result |
|---|---|---|
| Search by keyword | "Find trending notes on fat-loss meals" | Recognize keyword → query → TOP10 trending notes + HTML report |
| Site-wide trending | "Show me what's trending on Xiaohongshu" | Empty keyword site-wide query → trending leaderboard |
| Search by time range | "Workplace fashion trending in the last 15 days" | Filter by specified time window → trending data with date range |
| Generic keyword expansion | "Search food trending notes" | Detect generic keyword → suggest 10 niche directions → await your confirmation |
| Niche keyword direct search | "Petite fashion trending notes" | Recognize niche keyword → direct search → precise results |
Output example
📅 Query period: May 8 – May 19
| Note title | Author | Engagement | Published | Relevance | Popularity | Timeliness | Total |
|---|---|---|---|---|---|---|---|
| 5 tips for new hires to fit in quickly | Career Growth Hub | 10.0w | 2026-05-15 | 9.8 | 3.0 | 2.0 | 14.8 |
🔤 Related searches: Workplace communication, Career advancement, Office worker
📬 Subscription 1️⃣ Would you like to subscribe to notes matching the current search criteria for scheduled push? 2️⃣ Not needed
---
Use cases
| Scenario | Role | Need | How to use |
|---|---|---|---|
| Cold-start topics for new accounts | Creator | Can't find copyable trending samples | Keyword + last 7–30 days query |
| Brand milestone seeding | Brand operator | Need trending topics and format references before campaigns | Category keyword search; export HTML for internal review |
| Pre-sign screening | MCN scout | Want high-engagement trajectory, not one-off luck | Multiple samples in a fixed window for comparison |
| Pitches and weekly reports | Content planner | Client wants "trends + samples" one-pager | Structured summary + HTML; conclusions must match data window |
---
Important data notes
Update schedule and data lookback
| Data type | Update time | Lookback range |
|---|---|---|
| Trending notes | Updated at 7 AM daily with yesterday's data | Yesterday – 30 days ago |
Supported tracks
Fashion, Food, Makeup, Film & TV, Workplace, Pets, Home, Travel, Transportation, Hobbies, Tech, Internet, Healthcare, Astrology & Relationships, Wedding, Photography, Education, Parenting, Personal Care, Trendy Shoes & Bags, Lifestyle, Science, News, Sports
Inclusion criteria and data timeliness
Trending notes inclusion threshold is articles with 1,000+ engagements. Note engagement data is captured at indexing time and is not real-time; engagement may continue to grow after indexing. Displayed data reflects the indexing snapshot.
Scoring rules
Keyword searches are sorted by composite score across three weighted dimensions:
| Dimension | Max score | Description |
|---|---|---|
| Relevance | 10 pts | Title keyword, content topic, and search term match |
| Popularity | 3 pts | Likes / saves / comments / shares / total engagement |
| Timeliness | 2 pts | Proximity of publish date to query time |
Site-wide trending (no keyword) is sorted by engagement count with no scoring fields.
小红书爆款笔记查询 / xiaohongshu-search
---
简介
小红书爆款笔记查询工具,帮你快速找到互动数 1000 以上的热门笔记,获取创作灵感,把握内容趋势。
核心价值
基于小红书全网持续收录近 30 天互动数 1000 以上的热门笔记,每日早上 7 点更新昨日数据。根据相关性、热度、时效三维加权评分,汇聚全领域热门好文。快速查找各赛道优质对标内容,轻松参考同行热门创作思路,不用再四处搜集素材,一站式满足日常选题参考需求。
适用对象
- 🎯 品牌方 — 蹭热点做种草内容
- 🏢 MCN 机构 — 挖掘高互动潜力号
- ✍️ 自媒体创作者 — 找对标样本、获取创作灵感
- 📊 内容运营 — 周报选题与竞品分析
---
核心功能
- 关键词搜索:输入赛道关键词,获取最匹配的小红书热门笔记数据
- 数据评分排序:按相关性、热度、时效三维度加权评分,推荐最优笔记
- 泛化词智能拓展:识别泛化词后推荐 10 个细分方向,用户确认后再检索
- 推荐细分赛道:展示结果后主动推荐更深细分方向,引导逐级下探
- 订阅推送:支持按关键词创建日程订阅,每日定时推送最新热门笔记
---
密钥来源与安全说明
- 本技能需要使用环境变量:
REDFOX_API_KEY。 REDFOX_API_KEY由 红狐 hub (https://redfox.hk)签发,用于其接口鉴权。- 在提供密钥前,请先确认密钥来源、可用范围、有效期及是否支持重置/撤销。
- 禁止在代码、提示词、日志或输出文件中硬编码/明文暴露密钥。
---
前置条件
注册红狐 hub 账号获取 REDFOX_API_KEY
- 获取 REDFOX_API_KEY(前往 红狐 hub 申请)
环境变量配置
| 变量名 | 必填 | 说明 |
|---|---|---|
REDFOX_API_KEY | 是 | API 访问密钥 |
macOS(zsh)
在 ~/.zshrc 末尾添加一行(将引号内换成你的密钥):
export REDFOX_API_KEY="your_api_key_here"保存后执行:
source ~/.zshrcWindows(PowerShell)
- 仅本次终端有效:执行后立刻生效,无需再跑别的命令;关掉窗口后失效。
$env:REDFOX_API_KEY = "your_api_key_here"- 写入用户环境变量(持久):执行
setx后,当前这个 PowerShell 窗口里仍然没有该变量,需要 关闭并重新打开 终端(或重启 Cursor / VS Code 等),新开的窗口里才会读到REDFOX_API_KEY。
setx REDFOX_API_KEY "your_api_key_here"---
使用指南
直接用自然语言描述你的需求,无需记忆任何命令格式。输入泛化词时会自动推荐细分方向供你选择。
常用说法速查
| 意图 | 示例话术 | 效果 |
|---|---|---|
| 按关键词查热门 | 「查减脂餐的热门笔记」 | 识别关键词 → 查询 → TOP10 热门笔记 + HTML 报告 |
| 全站热门 | 「看看小红书最近热门」 | 空关键词全站查询 → 全站热门榜单 |
| 按时间范围查 | 「近15天穿搭热门」 | 按指定时间窗口筛选 → 带日期范围的热门数据 |
| 泛化词拓展 | 「查美食热门」 | 识别泛化词 → 推荐 10 个细分方向 → 等待你确认 |
| 细分词直查 | 「小个子穿搭热门」 | 识别细分词 → 直接搜索 → 精准结果 |
输出示例
📅 查询时间范围:5月8日 - 5月19日
| 笔记标题 | 作者 | 互动数 | 发布时间 | 相关性 | 热度 | 时效 | 总分 |
|---|---|---|---|---|---|---|---|
| 职场新人必看:5个让你快速融入团队的技巧 | 职场成长社 | 10.0w | 2026-05-15 | 9.8 | 3.0 | 2.0 | 14.8 |
🔤 拓词推荐:职场沟通、职场晋升、打工人
📬 订阅服务 1️⃣ 是否需要订阅当前搜索条件笔记,订阅后将定时推送给您? 2️⃣ 暂不需要
---
使用场景
| 场景 | 角色 | 需求描述 | 使用方式 |
|---|---|---|---|
| 新号冷启动选题 | 自媒体创作者 | 找不到可复制的热门样本 | 关键词 + 近 7~30 天查询 |
| 品牌节点种草 | 品牌运营 | 活动前需要热点与形式参考 | 品类关键词检索;导出 HTML 做内审 |
| 签约前筛号 | MCN 星探 | 要高互动潜力轨迹而非偶然爆款 | 固定窗口多次抽样对比 |
| 竞标与周报 | 内容策划 | 客户要「趋势+样本」一页纸结论 | 结构化摘要 + HTML;结论严格对应数据窗口 |
---
重要数据说明
更新时间与数据回溯
| 数据类型 | 更新时间 | 可回溯范围 |
|---|---|---|
| 热门笔记 | 每日早上 7 点更新昨日数据 | 昨天 ~ 30天前 |
支持赛道
穿搭、美食、彩妆、影视、职场、萌宠、家居、旅行、交通、兴趣、科技、互联网、医疗保健、星座情感、婚庆婚礼、拍摄、教育、亲子育儿、个人护理、潮流鞋包、生活、科学探索、新闻资讯、运动
收录原则与数据时效
热门笔记收录范围为互动数 1000 以上的文章。笔记互动数据截止为入库时间,不是实时数据;入库后互动数据可能持续增长,展示数据为入库时快照。
排序规则
有关键词搜索时按综合评分排序,三个维度加权:
| 维度 | 满分 | 说明 |
|---|---|---|
| 相关性 | 10 分 | 标题关键词、正文主题与搜索词匹配度 |
| 热度 | 3 分 | 点赞/收藏/评论/分享/互动总数 |
| 时效 | 2 分 | 发布时间距查询时间的近度 |
全站热门(无关键词)时按互动数排序,无评分字段。
小红书热门笔记数据格式说明
概览
本文档定义了小红书热门笔记搜索脚本 fetch_xhs_hot_articles.py 的输入输出格式规范。
输入格式
脚本参数
python scripts/fetch_xhs_hot_articles.py --keyword <关键词> [选项]| 参数 | 必填 | 说明 | 默认值 |
|---|---|---|---|
--keyword | 是 | 搜索关键词 | - |
--max-items | 否 | 最多展示数量 | 10 |
--output-format | 否 | 输出格式:text、json 或 html | html |
--output-file | 否 | 输出文件路径 | 关键词_热门数据.html |
--start-date | 否 | 开始日期,格式 yyyy-MM-dd | - |
--end-date | 否 | 结束日期,格式 yyyy-MM-dd | - |
--page-num | 否 | 页码 | 1 |
--page-size | 否 | 每页条数 | 50 |
--debug | 否 | 调试模式,打印原始API响应 | False |
API 接口
请求参数
{
"keyword": "女士护肤",
"pageNum": 1,
"pageSize": 10,
"startDate": "",
"endDate": "",
"source": "小红书爆款笔记洞察-GitHub"
}| 参数 | 类型 | 说明 |
|---|---|---|
keyword | string | 搜索关键词 |
pageNum | int | 页码,从1开始 |
pageSize | int | 每页条数,最大50 |
startDate | string | 开始日期,格式 yyyy-MM-dd |
endDate | string | 结束日期,格式 yyyy-MM-dd |
source | string | 固定值:"小红书爆款笔记洞察-GitHub" |
响应格式
{
"code": 2000,
"data": {
"articles": [...],
"hotTopics": [],
"keyword": "女士护肤",
"latestHotArticles": [],
"pageNum": 1,
"pageSize": 50,
"relatedSearches": [],
"tips": null,
"total": 23813
},
"msg": "成功"
}输出格式
作品数据字段(完整)
每条文章包含以下字段:
作品基本信息
| 字段名 | 类型 | 说明 |
|---|---|---|
id | string | 作品ID(唯一标识) |
title | string | 作品标题 |
desc | string | 作品描述/正文 |
createTime | string | 发布时间(格式:YYYY-MM-DD HH:MM:SS) |
cover | string | 封面图URL |
shareInfoLink | string | 作品链接 |
作者信息
| 字段名 | 类型 | 说明 |
|---|---|---|
authorId | string | 作者ID |
authorNickname | string | 作者名称 |
authorFans | int | 粉丝数 |
作者主页链接拼接规则:
https://www.xiaohongshu.com/user/profile/{authorId}互动数据
| 字段名 | 类型 | 说明 |
|---|---|---|
likedCount | int | 点赞数 |
collectedCount | int | 收藏数 |
commentsCount | int | 评论数 |
sharedCount | int | 分享数 |
interactiveCount | int | 互动总数 |
评分数据
| 字段名 | 类型 | 说明 |
|---|---|---|
popularityScore | float | 热度分数 |
recencyScore | float | 时效分数 |
relevanceScore | float | 相关性分数 |
totalScore | float | 总分 |
JSON 输出示例
{
"keyword": "女士护肤",
"total": 23813,
"pageNum": 1,
"pageSize": 50,
"items": [
{
"noteId": "69dcbb56000000001d01ae3c",
"title": "防晒换个思路,别再只看女士护肤品了!",
"desc": "#防晒#油痘#高夫#高夫防晒#肤感#油皮#物理防晒#高夫小蓝盾",
"authorId": "62bec245000000001902de0f",
"authorNickname": "可口可粒",
"authorFans": 196831,
"createTime": "2026-04-13 18:30:55",
"noteLink": "https://www.xiaohongshu.com/explore/69dcbb56000000001d01ae3c",
"authorLink": "https://www.xiaohongshu.com/user/profile/62bec245000000001902de0f",
"interactiveCount": 2560,
"likedCount": 1753,
"collectedCount": 719,
"commentsCount": 88,
"sharedCount": 44,
"totalScore": 11.5,
"relevanceScore": 10.0,
"popularityScore": 1.0,
"recencyScore": 0.5
}
]
}评分说明
数据评分由接口直接返回,无需计算:
| 字段名 | 说明 |
|---|---|
totalScore | 综合评分(主排序依据) |
popularityScore | 热度分数 |
relevanceScore | 相关性分数 |
recencyScore | 时效性分数 |
排序规则:按 totalScore 降序排列。
常见错误处理
| 错误 | 原因 | 解决方案 |
|---|---|---|
缺少 API Key 配置 | 未配置凭证 | 配置 COZE_REDFORX_XHS_API 环境变量 |
HTTP请求失败: 状态码 401 | API Key 无效 | 检查 API Key 是否正确 |
API 错误: xxx | 接口返回错误 | 检查请求参数是否正确 |
#!/usr/bin/env python3
"""
小红书热门笔记搜索脚本(支持 HTML 卡片布局输出)
基于红狐数据API,支持关键词搜索、分页、时间筛选
"""
import sys
import argparse
import json
import os
import urllib.request
import urllib.error
def parse_count(value):
"""解析数量,支持 "17w+"、"1.5w" 格式"""
if value is None:
return 0
if isinstance(value, int):
return value
value_str = str(value).replace('+', '').replace(',', '').strip()
# 处理 "w" 或 "W"(万)
if 'w' in value_str.lower():
value_str = value_str.lower().replace('w', '')
try:
return int(float(value_str) * 10000)
except:
return 0
try:
return int(float(value_str))
except:
return 0
def fuzzy_count(value):
"""对5000+的互动数做模糊处理,5000以下保留原始数值"""
if value is None:
return '--'
num = parse_count(value)
if num <= 0:
return '--'
if num < 5000:
return str(num)
if num < 10000:
return '5000+'
# 1万以上:以万为单位,向下取整
wan = num // 10000
return f'{wan}w+'
def fetch_xhs_hot_notes(keyword: str, debug: bool = False, max_retries: int = 3,
start_date: str = None, end_date: str = None,
page_num: int = 1, page_size: int = 50):
"""调用接口获取小红书热门笔记数据"""
# 从环境变量读取 API Key
api_key = os.environ.get("REDFOX_API_KEY", "").strip()
if not api_key:
print("❌ 错误:未找到 REDFOX_API_KEY 环境变量。", file=sys.stderr)
print("请在 Coze 平台的环境变量配置中添加 REDFOX_API_KEY,或在本地 shell 配置文件(如 ~/.zshrc / ~/.bashrc)中添加:", file=sys.stderr)
print(" export REDFOX_API_KEY=your_api_key_here", file=sys.stderr)
sys.exit(1)
# 构建请求
url = "https://redfox.hk/story/api/xhs/search/search"
headers = {
"Content-Type": "application/json",
"X-API-KEY": api_key
}
payload = {
"keyword": keyword,
"pageNum": page_num,
"pageSize": page_size,
"startDate": start_date or "",
"endDate": end_date or "",
"source": "小红书爆款笔记洞察-GitHub"
}
import time
last_error = None
for attempt in range(max_retries):
try:
if debug:
print(f"\n=== DEBUG: 第 {attempt + 1} 次尝试 ===", file=sys.stderr)
print(f"请求参数: {json.dumps(payload, ensure_ascii=False)}", file=sys.stderr)
body = json.dumps(payload, ensure_ascii=False).encode("utf-8")
req = urllib.request.Request(url, data=body, headers=headers, method="POST")
with urllib.request.urlopen(req, timeout=30) as resp:
status_code = resp.status
raw = resp.read().decode("utf-8")
if debug:
print(f"状态码: {status_code}", file=sys.stderr)
print(f"响应长度: {len(raw)} 字节", file=sys.stderr)
data = json.loads(raw)
# 检查返回码
if data.get("code") != 2000:
raise Exception(f"API 错误: {data.get('msg', '未知错误')}")
result_data = data.get("data", {})
if debug:
print("=== DEBUG: API 返回的 data 字段键 ===", file=sys.stderr)
print(json.dumps(list(result_data.keys()), ensure_ascii=False, indent=2), file=sys.stderr)
print(f"总条数: {result_data.get('total', 0)}", file=sys.stderr)
articles = result_data.get("articles", [])
return {
"keyword": result_data.get("keyword", keyword),
"articles": articles,
"total": len(articles),
"pageNum": result_data.get("pageNum", page_num),
"pageSize": result_data.get("pageSize", page_size),
"hotTopics": result_data.get("hotTopics", []),
"relatedSearches": result_data.get("relatedSearches", []),
"latestHotArticles": result_data.get("latestHotArticles", [])
}
except urllib.error.HTTPError as e:
last_error = f"HTTP请求失败: 状态码 {e.code}, {e.read().decode('utf-8', errors='ignore')[:200]}"
if debug:
print(f" 错误: {last_error[:100]}", file=sys.stderr)
if attempt < max_retries - 1:
time.sleep(2 ** attempt)
continue
except urllib.error.URLError as e:
last_error = f"请求失败: {str(e.reason)}"
if debug:
print(f" 错误: {last_error[:100]}", file=sys.stderr)
if attempt < max_retries - 1:
time.sleep(2 ** attempt)
continue
except Exception as e:
last_error = str(e)
if debug:
print(f" 错误: {str(e)[:100]}", file=sys.stderr)
if attempt < max_retries - 1:
time.sleep(2 ** attempt)
continue
raise Exception(f"{last_error}(已尝试 {max_retries} 次)")
def get_cover_urls(data, max_items=10):
"""提取所有封面图URL"""
urls = []
articles = data.get("articles", [])[:max_items]
for item in articles:
cover_url = item.get('cover', '')
note_id = item.get('id', '')
title = (item.get('title', '') or item.get('desc', ''))[:30]
if cover_url and note_id:
urls.append({
'title': title,
'note_id': note_id,
'cover_url': cover_url,
'link': item.get('shareInfoLink', f"https://www.xiaohongshu.com/explore/{note_id}")
})
return urls
def get_top_articles(data, max_items=10):
"""
获取文章列表(按接口原始返回顺序,截取前 max_items 条)
"""
articles = data.get("articles", [])[:max_items]
return articles
def format_as_html(data: dict, max_items: int = 10, start_date: str = None):
"""
格式化输出热门笔记数据(HTML 卡片布局)
"""
from datetime import datetime
keyword = data.get("keyword", "")
total = data.get("total", 0)
is_full_site = not keyword or keyword.strip() == ""
def process_title(item):
"""处理标题"""
title = item.get('title', '')
if not title or title.strip() == '':
desc = item.get('desc', '')
if desc:
title = desc.replace('\n', ' ').replace('\r', ' ').strip()[:30]
if len(desc) > 30:
title = title + '...'
if not title or title.strip() == '':
title = '无标题'
title = title.replace('<', '<').replace('>', '>').replace('"', '"')
return title
def format_time(item):
"""格式化发布时间"""
create_time = item.get('createTime', '')
if create_time:
try:
month = int(create_time[5:7])
day = int(create_time[8:10])
return f"{month}月{day}日"
except:
pass
return '--'
def generate_card(item, idx):
"""生成单个卡片 HTML"""
note_id = item.get('id', '')
author_id = item.get('authorId', '')
author_name = item.get('authorNickname', '未知')
fans = item.get('authorFans', 0)
title = process_title(item)
pub_time = format_time(item)
interactive_count = fuzzy_count(item.get('interactiveCount', 0))
like_count = fuzzy_count(item.get('likedCount', 0))
collect_count = fuzzy_count(item.get('collectedCount', 0))
# 作品链接
note_link = item.get('shareInfoLink') or f"https://www.xiaohongshu.com/explore/{note_id}"
# 作者主页链接
author_link = f"https://www.xiaohongshu.com/user/profile/{author_id}" if author_id else "#"
relevance_score = item.get('relevanceScore', 0)
popularity_score = item.get('popularityScore', 0)
recency_score = item.get('recencyScore', 0)
total_score = item.get('totalScore', 0)
# 评分标签(全站热门时不展示)
scores_html = ''
if not is_full_site:
scores_html = f'''
<div class="card-scores">
<span class="score-tag relevance">相关性 {relevance_score}</span>
<span class="score-tag popularity">热度 {popularity_score}</span>
<span class="score-tag recency">时效 {recency_score}</span>
</div>
'''
card_html = f'''
<div class="card">
<div class="card-title-row">
<span class="card-index">{idx + 1}.</span>
<a href="{note_link}" class="card-title" target="_blank">{title}</a>
</div>
<div class="card-meta">
<a href="{author_link}" class="author-link" target="_blank">{author_name}({fuzzy_count(fans)}粉)</a>
<span class="meta-divider">·</span>
<span class="pub-time">发布日期:{pub_time}</span>
</div>
{scores_html}
<div class="card-stats">
<span class="interaction-count">🔥 {interactive_count}互动</span>
<span class="detail-stats">👍{like_count} ⭐{collect_count}</span>
<a href="{note_link}" class="view-note-btn" target="_blank">查看作品 ↗</a>
</div>
</div>
'''
return card_html
# 获取数据(接口原始顺序)
top_items = get_top_articles(data, max_items)
latest_hot_items = data.get("latestHotArticles", [])[:10]
# 主列表为空时的提示
no_articles_hint = ''
if not top_items:
no_articles_hint = '''
<div class="no-data-hint">
<p>未查询到相关热门笔记,建议更换关键词重试。</p>
</div>
'''
cards_html = ''.join([generate_card(item, idx) for idx, item in enumerate(top_items)]) if top_items else ''
# 推荐热门笔记区域(latestHotArticles,仅在有关键词文章时额外展示)
latest_hot_html = ''
if latest_hot_items:
latest_cards = ''.join([generate_card(item, idx) for idx, item in enumerate(latest_hot_items)])
latest_hot_html = f'''
<div class="section-header">
<h2>近期热门笔记推荐</h2>
</div>
<div class="card-grid">
{latest_cards}
</div>
'''
time_range = f"近30天" if not start_date else f"从{start_date}起"
html_content = f'''<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>小红书热门笔记数据分析报告</title>
<style>
* {{
margin: 0;
padding: 0;
box-sizing: border-box;
}}
body {{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background-color: #f5f5f5;
padding: 16px;
color: #333;
}}
.container {{
max-width: 1200px;
margin: 0 auto;
}}
.report-header {{
background: linear-gradient(135deg, #ff2442 0%, #ff6b81 100%);
color: white;
padding: 20px 24px;
border-radius: 12px;
margin-bottom: 20px;
}}
.report-header h1 {{
font-size: 20px;
margin-bottom: 8px;
}}
.report-header .keyword {{
font-size: 14px;
opacity: 0.9;
}}
.report-header .total {{
font-size: 12px;
opacity: 0.8;
margin-top: 4px;
}}
.card-grid {{
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 16px;
}}
.card {{
background: white;
border-radius: 12px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
transition: transform 0.2s, box-shadow 0.2s;
display: flex;
flex-direction: column;
gap: 10px;
}}
.card:hover {{
transform: translateY(-2px);
box-shadow: 0 4px 16px rgba(0,0,0,0.12);
}}
.card-title-row {{
border-bottom: 1px solid #f0f0f0;
padding-bottom: 10px;
display: flex;
align-items: flex-start;
gap: 6px;
}}
.card-index {{
font-size: 15px;
font-weight: 700;
color: #ff2442;
min-width: 20px;
}}
.card-title {{
font-size: 15px;
font-weight: 700;
color: #1a1a1a;
text-decoration: none;
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
transition: color 0.2s;
}}
.card-title:hover {{
color: #ff2442;
}}
.card-meta {{
font-size: 13px;
color: #999;
padding: 8px 0;
}}
.author-link {{
color: #666;
text-decoration: none;
transition: color 0.2s;
}}
.author-link:hover {{
color: #ff2442;
}}
.meta-divider {{
margin: 0 6px;
}}
.pub-time {{
color: #999;
}}
.card-scores {{
display: flex;
gap: 8px;
flex-wrap: wrap;
}}
.score-tag {{
font-size: 12px;
padding: 2px 8px;
border-radius: 10px;
font-weight: 500;
}}
.score-tag.relevance {{
background: #e8f5e9;
color: #2e7d32;
}}
.score-tag.popularity {{
background: #fff3e0;
color: #e65100;
}}
.score-tag.recency {{
background: #e3f2fd;
color: #1565c0;
}}
.card-stats {{
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
margin: 0 -16px;
background: linear-gradient(135deg, #fff5f5, #fff);
}}
.interaction-count {{
font-size: 14px;
font-weight: 600;
color: #ff2442;
}}
.detail-stats {{
font-size: 12px;
color: #666;
}}
.view-note-btn {{
color: #ff2442;
text-decoration: none;
font-size: 14px;
font-weight: 500;
transition: opacity 0.2s;
}}
.view-note-btn:hover {{
opacity: 0.7;
text-decoration: underline;
}}
.data-note {{
text-align: center;
color: #999;
font-size: 12px;
margin-top: 20px;
padding: 12px;
background: white;
border-radius: 8px;
}}
.section-header {{
margin-top: 24px;
margin-bottom: 16px;
padding: 12px 16px;
background: linear-gradient(135deg, #ff6b81 0%, #ff2442 100%);
border-radius: 8px;
}}
.section-header h2 {{
color: white;
font-size: 16px;
font-weight: 600;
}}
</style>
</head>
<body>
<div class="container">
<div class="report-header">
<h1>小红书热门笔记数据分析报告</h1>
<div class="keyword">关键词:{keyword} | 时间范围:{time_range}</div>
<div class="total">共找到 {total} 条相关笔记</div>
</div>
{no_articles_hint}
<div class="card-grid">
{cards_html}
</div>
{latest_hot_html}
<div class="data-note">
数据来源:小红书热门笔记搜索,每日更新最新热门内容<br>
备注:互动数据为入库快照,实时数据可能持续增长
</div>
</div>
</body>
</html>'''
return html_content
def format_as_json(data: dict, max_items: int = 10):
"""
格式化输出 JSON 格式(供智能体分析生成推荐理由)
"""
top_items = get_top_articles(data, max_items)
keyword = data.get('keyword', '')
is_full_site = not keyword or keyword.strip() == ""
latest_hot_items = data.get("latestHotArticles", [])[:10]
result = []
for item in top_items:
note_id = item.get('id', '')
item_data = {
'noteId': note_id,
'title': item.get('title', '') or item.get('desc', '')[:50],
'desc': item.get('desc', ''),
'authorId': item.get('authorId', ''),
'authorNickname': item.get('authorNickname', ''),
'authorFans': fuzzy_count(item.get('authorFans', 0)),
'createTime': item.get('createTime', ''),
'noteLink': item.get('shareInfoLink') or f"https://www.xiaohongshu.com/explore/{note_id}",
'authorLink': f"https://www.xiaohongshu.com/user/profile/{item.get('authorId', '')}" if item.get('authorId') else '',
'interactiveCount': fuzzy_count(item.get('interactiveCount', 0)),
'likedCount': fuzzy_count(item.get('likedCount', 0)),
'collectedCount': fuzzy_count(item.get('collectedCount', 0)),
'commentsCount': fuzzy_count(item.get('commentsCount', 0)),
'sharedCount': fuzzy_count(item.get('sharedCount', 0)),
}
# 有关键词时才输出评分字段
if not is_full_site:
item_data['totalScore'] = item.get('totalScore', 0)
item_data['relevanceScore'] = item.get('relevanceScore', 0)
item_data['popularityScore'] = item.get('popularityScore', 0)
item_data['recencyScore'] = item.get('recencyScore', 0)
result.append(item_data)
# 格式化推荐热门笔记(latestHotArticles,无评分字段)
latest_hot_result = []
for item in latest_hot_items:
note_id = item.get('id', '')
latest_hot_result.append({
'noteId': note_id,
'title': item.get('title', '') or item.get('desc', '')[:50],
'authorNickname': item.get('authorNickname', ''),
'authorFans': fuzzy_count(item.get('authorFans', 0)),
'createTime': item.get('createTime', ''),
'noteLink': item.get('shareInfoLink') or f"https://www.xiaohongshu.com/explore/{note_id}",
'authorLink': f"https://www.xiaohongshu.com/user/profile/{item.get('authorId', '')}" if item.get('authorId') else '',
'interactiveCount': fuzzy_count(item.get('interactiveCount', 0)),
'likedCount': fuzzy_count(item.get('likedCount', 0)),
'collectedCount': fuzzy_count(item.get('collectedCount', 0)),
})
return {
'keyword': data.get('keyword', ''),
'total': data.get('total', 0),
'pageNum': data.get('pageNum', 1),
'pageSize': data.get('pageSize', 50),
'isFullSite': is_full_site,
'items': result,
'latestHotArticles': latest_hot_result,
'relatedSearches': data.get('relatedSearches', [])
}
def main():
"""主函数"""
parser = argparse.ArgumentParser(description='小红书热门笔记搜索工具')
parser.add_argument('--keyword', required=True, help='搜索关键词')
parser.add_argument('--max-items', type=int, default=10,
help='最多展示数量(默认10条)')
parser.add_argument('--output-format', choices=['json', 'html'],
default='json', help='输出格式(默认json输出到stdout,html输出到文件)')
parser.add_argument('--output-file', type=str, default=None,
help='输出文件路径(默认:关键词_热门数据.html)')
parser.add_argument('--start-date', type=str, default=None,
help='开始日期,格式 yyyy-MM-dd')
parser.add_argument('--end-date', type=str, default=None,
help='结束日期,格式 yyyy-MM-dd')
parser.add_argument('--page-num', type=int, default=1,
help='页码(默认1)')
parser.add_argument('--page-size', type=int, default=50,
help='每页条数(默认50)')
parser.add_argument('--debug', action='store_true', help='启用调试模式')
parser.add_argument('--max-retries', type=int, default=3,
help='最大重试次数(默认3次)')
args = parser.parse_args()
try:
data = fetch_xhs_hot_notes(
keyword=args.keyword,
debug=args.debug,
max_retries=args.max_retries,
start_date=args.start_date,
end_date=args.end_date,
page_num=args.page_num,
page_size=args.page_size
)
# 生成 JSON 数据(始终输出到 stdout,供智能体读取)
json_data = format_as_json(data, max_items=args.max_items)
# 输出 JSON 到 stdout(智能体从此读取结构化数据)
print(json.dumps(json_data, ensure_ascii=False, indent=2))
# 同时生成 HTML 文件
html_content = format_as_html(data, max_items=args.max_items, start_date=args.start_date)
keyword_safe = args.keyword.replace('"', '').replace(' ', '_') or '全站热门'
html_file = args.output_file or f"{keyword_safe}_热门数据.html"
with open(html_file, 'w', encoding='utf-8') as f:
f.write(html_content)
# 统计信息输出到 stderr
print(f"✓ HTML 结果已保存到: {html_file}", file=sys.stderr)
print(f"✓ 关键词: {args.keyword}", file=sys.stderr)
print(f"✓ 总条数: {json_data['total']} 条", file=sys.stderr)
print(f"✓ 筛选结果: {len(json_data['items'])} 条", file=sys.stderr)
print(f"✓ 推荐热门笔记: {len(json_data.get('latestHotArticles', []))} 条", file=sys.stderr)
# 输出封面图URL供后续分析
cover_urls = get_cover_urls(data, max_items=5)
if cover_urls:
print(f"\n=== 封面图URL(用于风格分析)===", file=sys.stderr)
for i, item in enumerate(cover_urls, 1):
print(f"{i}. {item['title']}: {item['cover_url']}", file=sys.stderr)
except Exception as e:
print(f"❌ 错误: {str(e)}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()