toutiao-search
今日头条爆款内容查询 — 输入关键词搜索今日头条最新作品(图文/视频),支持按阅读量/时间排序、限定时间范围,终端表格展示 + CSV 导出 + 交互式 HTML 报告。当用户需要搜索今日头条热门内容、追踪头条热点、分析爆款文章时使用。
Install
npx skills add https://github.com/redfox-data/redfox-community/tree/main/skills/toutiao-search
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
今日头条爆款内容查询 / toutiao-search
简介
输入关键词就能搜到今日头条刚发布的作品,能按阅读量或发布时间排序、限定最近几小时内内容,给到你的都是平台刚更新的新鲜内容,不会拿旧缓存数据糊弄你。
核心价值
- 内容新鲜不滞后:每次搜索都直接拉取平台最新发布的作品,只给刚发的内容,不用老旧存档、缓存数据。
- 筛选方式灵活好用:能按阅读量或发布时间排序,还能限定最近几小时内内容,自由搭配条件查找。
- 图文视频都能搜:图文和视频作品一网打尽,想看哪种都能找到,不会遗漏任何类型。
- 翻页查看不遗漏:结果多的时候可以一页页翻着看,不遗漏任何一条内容。
- 数据指标一目了然:阅读量、点赞、评论、转发等互动数据直观展示,还能自动导出 CSV 和交互式 HTML 报告,下载即用。
适用对象
- 🔍 内容创作者 — 快速了解今日头条上同赛道最新趋势与热门选题,获取创作灵感。
- 📊 运营 / 数据分析 — 追踪关键词相关内容在头条的数据表现,掌握流量趋势。
- 🏢 品牌 / MCN — 监控竞品与行业关键词在今日头条的最新内容表现,辅助策略决策。
功能特性
核心功能
- 关键词搜索:输入任意关键词搜索今日头条最新爆款作品。
- 双排序切换:按阅读量(
views)或发布时间(time)降序排列,快速定位高质量或最新内容。 - 时间过滤:
--hours N限定最近 N 小时内的新鲜内容,拒绝过期数据。 - 视频筛选:
--video-only只返回视频类作品,精准找视频爆款。 - 翻页浏览:
--pages N控制翻页次数,每页约 10 条,默认 3 页。 - 终端表格输出:标题、作者、阅读、点赞、评论、转发、发布时间、类型一览无余。
- CSV 自动导出:UTF-8 BOM 编码 CSV,Excel 直接打开不乱码。
- HTML 交互报告:深色主题,图文/视频标签区分,作品卡片点击跳转原文,支持页内搜索。
密钥获取与安全说明
- 本技能需要使用环境变量:
REDFOX_API_KEY。 REDFOX_API_KEY由 红狐 hub 提供。- 请前往 红狐 hub 注册账号,获取
REDFOX_API_KEY。 - 配置设备环境变量
REDFOX_API_KEY后使用本技能。 - 在提供密钥前,请先确认密钥来源、可用范围、有效期及是否支持重置/撤销。
- 禁止在代码、提示词、日志或输出文件中硬编码/明文暴露密钥。
使用指南
直接用自然语言描述需求,无需记忆命令。
常用说法速查
| 意图 | 示例话术 | 效果 |
|---|---|---|
| 搜某个话题内容 | 「搜一下今日头条 AI 相关内容」 | 按阅读量排序搜索并展示结果 |
| 指定排序方式 | 「按最新发布搜索大模型相关作品」 | 切换为按发布时间排序后搜索 |
| 限定时间范围 | 「搜索最近 24 小时新能源相关作品」 | 设置 --hours 24,只返回一天内作品 |
| 只看视频内容 | 「搜一下测评类的视频」 | 开启 --video-only,只返回视频作品 |
| 翻页查看更多 | 「多翻几页」/「看更多结果」 | 增加 --pages 翻页获取更多数据 |
| 导出数据 | 「导出 CSV」/「生成 HTML 报告」 | 自动生成 CSV 和交互式 HTML 报告 |
输出示例
查询完成后,你将看到类似以下格式的表格:
📊 关键词「AI」查询到 30 条作品(排序:按阅读量 | 时间范围:不限 | 第 1-3 页),以下是详细数据:
| # | 标题 | 作者 | 阅读 | 点赞 | 评论 | 转发 | 发布时间 | 类型 |
|---|---|---|---|---|---|---|---|---|
| 1 | 某某爆款文章标题最多三十字... | 某某作者 | 12w | 3.2k | 412 | 89 | 2026-06-30 14:30 | 图文 |
| 2 | 另一个热门视频标题展示... | 另一位作者 | 8.5w | 1.5k | 230 | 56 | 2026-06-30 12:00 | 视频 |
📄 CSV 和 HTML 报告已保存至
~/Downloads/QoderToutiaoSearch/。
(数据以终端表格形式展示,CSV 和 HTML 同时自动保存。)
使用场景
| 场景 | 角色 | 示例问法 | 收益 |
|---|---|---|---|
| 热点话题追踪 | 内容创作者 | 「搜一下今日头条 AI 相关内容」 | 第一时间掌握头条平台最新话题动向 |
| 爆款内容分析 | 运营 / 数据 | 「搜大模型,按阅读量排序」 | 快速找到高阅读量爆款,研究爆文规律 |
| 竞品内容监控 | 品牌运营 | 「搜品牌关键词最近 24 小时内容」 | 追踪竞品在头条的最新投放与表现 |
| 选题灵感采集 | 创作者 | 「搜新能源,导出 CSV 分析」 | 批量获取数据找选题方向,用数据说话 |
| 视频内容挖掘 | MCN | 「搜测评,只看视频类」 | 精准筛选视频爆款,评估内容方向 |
Skill manifest
今日头条爆款内容查询
输入关键词搜索今日头条最新作品(图文/视频),终端表格展示 + CSV 导出 + 交互式 HTML 报告。
需先配置 API Key,通过环境变量 REDFOX_API_KEY 或 --api-key 参数传入。 搜索范围:今日头条平台最新发布的作品,实时拉取,无缓存延迟。
使用场景
当你需要执行以下任务时,应优先使用本技能:
| 场景 | 示例 |
|---|---|
| 热点话题追踪 | 搜 "AI" 了解今日头条上最新的 AI 相关内容 |
| 爆款内容分析 | 搜 "大模型" 找出阅读量最高的爆款文章 |
| 竞品内容监控 | 搜品牌关键词,追踪竞品在今日头条的投放内容 |
| 选题灵感采集 | 搜 "新能源" 批量导出 CSV 做数据分析找选题 |
| 舆情快速了解 | 搜某事件关键词,快速了解头条平台讨论风向 |
| 视频内容挖掘 | 搜 "测评" 配合 --video-only 筛选视频类爆款 |
使用方法
# 基础搜索
python3 "$SKILL_PATH/scripts/search.py" "关键词"
# 按阅读量排序(默认按时间)
python3 "$SKILL_PATH/scripts/search.py" "AI" --sort views
# 限定最近 24 小时内发布的作品
python3 "$SKILL_PATH/scripts/search.py" "大模型" --hours 24
# 只查视频类作品
python3 "$SKILL_PATH/scripts/search.py" "测评" --video-only
# 多页翻页,获取更多结果
python3 "$SKILL_PATH/scripts/search.py" "新能源汽车" --pages 5
# 仅导出 CSV,不生成 HTML
python3 "$SKILL_PATH/scripts/search.py" "AI" --csv-only
# 不自动打开浏览器
python3 "$SKILL_PATH/scripts/search.py" "AI" --no-open
终端输出按指定排序方式(时间/阅读量)降序排列;结果较少或无结果时自动提示。
HTML 报告特性:深色主题 · 点击搜索按钮查询 · 作品卡片点击跳转原文 · 图文/视频标签区分 · 分页加载。
CSV / HTML 默认保存在 ~/Downloads/QoderToutiaoSearch/。
返回结果展示规范
向用户展示搜索结果时,必须使用以下完整字段列表,不得省略:
| 序号 | 字段 | 说明 |
|---|---|---|
| # | 排名序号 | 从 1 开始 |
| 标题 | 作品标题 | 最多显示 30 字,超出截断 |
| 作者 | 作者昵称 | — |
| 阅读量 | viewCount | 用千/万简写(如 3.2k、12w) |
| 点赞数 | likeCount | 同上 |
| 评论数 | commentCount | 同上 |
| 转发数 | repostCount | 同上 |
| 发布时间 | publishTime | 格式:YYYY-MM-DD HH:mm |
| 类型 | workType | 图文 / 视频 |
| 链接 | workUrl | 可点击跳转原文 |
表格示例:
| # | 标题 | 作者 | 阅读 | 点赞 | 评论 | 转发 | 发布时间 | 类型 |
|---|------|------|------|------|------|------|----------|------|
| 1 | 某某爆款文章标题... | 某某作者 | 3.2k | 412 | 89 | 56 | 2026-06-28 14:30 | 图文 |
参数说明
| 参数 | 说明 | 默认值 |
|---|---|---|
keyword |
搜索关键词(必填,位置参数) | — |
--sort |
排序方式:views / time |
views |
--hours |
限定最近 N 小时内发布的作品(0=不限) | 0 |
--pages |
翻页次数(每页约 10 条) | 3 |
--video-only |
只返回视频类作品 | — |
--output-dir |
输出目录 | ~/Downloads/QoderToutiaoSearch |
--api-key |
指定 API Key | — |
--no-open |
不自动打开浏览器 | — |
--csv-only |
仅生成 CSV,不生成 HTML | — |
--port |
HTML 本地服务端口 | 8767 |
API Key 配置
任选一种方式配置个人 Key:
| 方式 | 命令 |
|---|---|
| 环境变量(推荐) | export REDFOX_API_KEY=ak_你的密钥 |
| 命令行参数 | --api-key ak_你的密钥 |
| 配置文件 | echo '{"api_key":"ak_你的密钥"}' > ~/.qoder/apis/redfox.json |
注册地址:redfox.hk
功能特点
- 双接口深度查询:
searchWork搜索列表 +workDetail获取完整数据(阅读/点赞/评论/转发/分享) - 时间过滤:限定最近 N 小时内的新鲜内容,拒绝过期缓存
- 灵活排序:按发布时间或阅读量降序排列
- 视频筛选:
--video-only只找视频类爆款 - 终端表格:标题、作者、阅读、点赞、评论、转发、发布时间、链接、类型
- CSV 导出:自动生成 UTF-8 BOM 编码的 CSV
- HTML 交互报告:深色主题,图文/视频标签区分,支持页内搜索
- 本地代理服务:避免浏览器跨域限制,支持页内实时搜索
依赖
pip3 install requests
常见问题
Q:能搜到每条爆款吗? A:能保证每次的关键词都有作品。
Q:搜索范围覆盖多久之前的作品? A:目前提供近 1 年的作品,优先会覆盖近期发布的内容。
Q:为什么搜索后还要等待详情查询? A:搜索接口只返回基础信息,同时提供单作品补全阅读量、点赞、转发等完整指标,让数据更完整。
Q:搜索后如何保存数据? A:可以让 AI 为你导出 Excel,也可以让 AI 导出 HTML。
Q:额度用完怎么办? A:前往 redfox.hk 注册获取 Token。
Q:最多能获取多少条?
A:每页约 10 条,默认翻 3 页(约 30 条)。可通过 --pages 参数调整。
Files (redfox-community)
-
assets
-
report_template.html 29.3 KB · in bundle
-
-
scripts
-
search.py 31.6 KB
#!/usr/bin/env python3 """ 今日头条爆款内容查询 — 关键词搜索今日头条作品 ========================================= 搜索接口:POST /toutiao/searchWork(关键词搜索列表) 详情接口:POST /toutiao/workDetail(按 opusId 获取完整数据) Usage: python3 search.py "人工智能" python3 search.py "AI" --sort views python3 search.py "大模型" --hours 24 --video-only python3 search.py "新能源" --pages 5 --csv-only """ import argparse import csv import json import os import subprocess import sys import time import threading from datetime import datetime, timedelta from http.server import HTTPServer, SimpleHTTPRequestHandler from pathlib import Path from urllib.parse import urlparse, parse_qs from concurrent.futures import ThreadPoolExecutor, as_completed try: import requests HAS_REQUESTS = True except ImportError: HAS_REQUESTS = False # ─── 配置 ───────────────────────────────────────────────────────────────────────── SEARCH_API = "https://redfox.hk/story/api/toutiao/searchWork" DETAIL_API = "https://redfox.hk/story/api/toutiao/workDetail" CONFIG_DIR = Path.home() / ".qoder" / "apis" CONFIG_FILE = CONFIG_DIR / "redfox.json" ENV_KEY = "REDFOX_API_KEY" SOURCE = "今日头条爆款查询-GitHub" DEFAULT_OUTPUT_DIR = Path.home() / "Downloads" / "QoderToutiaoSearch" DEFAULT_PAGES = 3 DEFAULT_PORT = 8767 # ─── 终端颜色 ────────────────────────────────────────────────────────────────────── GREEN = "\033[92m" YELLOW = "\033[93m" RED = "\033[91m" CYAN = "\033[96m" BOLD = "\033[1m" RESET = "\033[0m" def info(msg): print(f"{GREEN}[✓]{RESET} {msg}") def warn(msg): print(f"{YELLOW}[!]{RESET} {msg}") def error(msg): print(f"{RED}[✗]{RESET} {msg}") def step(msg): print(f"{CYAN}[→]{RESET} {msg}") # ─── API Key 管理 ────────────────────────────────────────────────────────────────── def get_api_key(cli_key=None): if cli_key: return cli_key env_key = os.environ.get(ENV_KEY) if env_key: return env_key if CONFIG_FILE.exists(): try: data = json.loads(CONFIG_FILE.read_text()) key = data.get("api_key") if key: return key except (json.JSONDecodeError, OSError): pass return None # ─── 工具函数 ───────────────────────────────────────────────────────────────────── def ts_to_str(ts_str): """秒级时间戳字符串 → 可读时间""" try: ts = int(ts_str) return datetime.fromtimestamp(ts).strftime("%Y-%m-%d %H:%M:%S") except (ValueError, TypeError): return ts_str or "" def ts_to_date(ts_str): try: ts = int(ts_str) return datetime.fromtimestamp(ts).strftime("%Y-%m-%d") except (ValueError, TypeError): return "" def ts_to_dt(ts_str): try: ts = int(ts_str) return datetime.fromtimestamp(ts) except (ValueError, TypeError): return None def format_number(n): if n is None: return "0" n = int(n) if n >= 10000: return f"{n/10000:.1f}w" if n >= 1000: return f"{n/1000:.1f}k" return str(n) def work_type_str(is_video): return "视频" if is_video else "图文" # ─── 搜索接口 ───────────────────────────────────────────────────────────────────── def search_works(session, keyword, offset=0): """调用 searchWork 接口,返回 (items, has_next)""" payload = {"keyword": keyword, "offset": str(offset), "source": SOURCE} try: resp = session.post(SEARCH_API, json=payload, timeout=20) result = resp.json() except Exception as e: error(f"搜索请求失败: {e}") return None, False code = result.get("code") if code not in (200, 2000): msg = result.get("msg", "") if "调用次数" in msg: error(f"今日调用次数已达上限: {msg}") else: error(f"搜索接口错误 (code {code}): {msg}") return None, False data = result.get("data") or [] if not isinstance(data, list): return None, False has_next = False if data: has_next = data[0].get("hasNext", False) return data, has_next # ─── 详情接口 ───────────────────────────────────────────────────────────────────── def fetch_detail(session, opus_id): """调用 workDetail 接口,返回完整作品数据""" payload = {"opusId": str(opus_id)} try: resp = session.post(DETAIL_API, json=payload, timeout=15) result = resp.json() except Exception as e: return None code = result.get("code") if code not in (200, 2000): return None return result.get("data") def fetch_details_batch(session, opus_ids, max_workers=5): """并发获取多个作品详情""" results = {} def _fetch(oid): return oid, fetch_detail(session, oid) with ThreadPoolExecutor(max_workers=max_workers) as pool: futures = {pool.submit(_fetch, oid): oid for oid in opus_ids} for future in as_completed(futures): try: oid, detail = future.result() if detail: results[oid] = detail except Exception: pass return results # ─── 主搜索流程 ──────────────────────────────────────────────────────────────────── def run_search(session, keyword, max_pages=3, hours=0, video_only=False): """ 1. searchWork 按关键词翻页搜索 2. 收集所有 opusId 3. 并发调用 workDetail 获取完整数据 4. 过滤(时间/视频)+ 排序 """ all_items = [] seen_ids = set() offset = 0 page = 0 step(f"正在搜索「{keyword}」...") while page < max_pages: page += 1 items, has_next = search_works(session, keyword, offset=offset) if items is None: break new_items = [] for item in items: oid = item.get("opusId") if oid and oid not in seen_ids: seen_ids.add(oid) new_items.append(item) if not new_items: break all_items.extend(new_items) offset += 1 # searchWork 的 offset 是页码偏移(从0开始+1) step(f" 第 {page} 页: 获取 {len(new_items)} 条(累计 {len(all_items)} 条)") if not has_next: break time.sleep(0.3) if not all_items: return [] # 并发获取详情 opus_ids = [item.get("opusId") for item in all_items if item.get("opusId")] step(f"正在获取 {len(opus_ids)} 条作品详情...") details = fetch_details_batch(session, opus_ids, max_workers=5) info(f"成功获取 {len(details)} 条详情") # 合并搜索基础数据 + 详情数据 merged = [] for item in all_items: oid = item.get("opusId") detail = details.get(oid, {}) pub_ts = item.get("publishTime", "") entry = { "opusId": oid, "title": detail.get("title") or item.get("title") or "无标题", "authorName": detail.get("authorName") or item.get("nickname") or "未知作者", "publishTime": detail.get("publishTime") or ts_to_str(pub_ts), "publishTs": pub_ts, "workType": detail.get("workType", False), "hasVideo": detail.get("hasVideo", False), "workUrl": detail.get("workUrl") or item.get("opusUrl") or "", "viewCount": detail.get("viewCount", 0), "likeCount": detail.get("likeCount", 0), "commentCount": detail.get("commentCount") or item.get("commentNum", 0), "repostCount": detail.get("repostCount", 0), "shareCount": detail.get("shareCount", 0), "content": detail.get("content", ""), "imageList": detail.get("imageList", []), "duration": detail.get("duration", ""), } merged.append(entry) # 时间过滤 if hours > 0: cutoff = datetime.now() - timedelta(hours=hours) merged = [e for e in merged if (ts_to_dt(e["publishTs"]) or datetime.min) >= cutoff] if not merged: warn(f"最近 {hours} 小时内无相关作品") return [] # 视频筛选 if video_only: merged = [e for e in merged if e["workType"] or e["hasVideo"]] if not merged: warn("无视频类作品") return [] return merged # ─── 排序 ───────────────────────────────────────────────────────────────────────── def sort_results(items, sort_by="time"): if sort_by == "views": items.sort(key=lambda e: int(e.get("viewCount", 0) or 0), reverse=True) else: # time items.sort(key=lambda e: int(e.get("publishTs", 0) or 0), reverse=True) return items # ─── 终端表格 ────────────────────────────────────────────────────────────────────── def print_terminal_table(items, keyword, sort_by): if not items: print(f"\n{YELLOW}[✗] 关键词「{keyword}」暂无搜索结果{RESET}") return count = len(items) sort_label = "阅读量" if sort_by == "views" else "发布时间" print(f"\n{BOLD}{'=' * 130}{RESET}") print(f"{BOLD} 今日头条爆款查询 · 「{keyword}」· 共 {count} 条 · 按{sort_label}降序{RESET}") print(f"{BOLD}{'=' * 130}{RESET}") header = (f" {'序号':<4}{'标题':<28}{'作者':<12}{'类型':<5}" f"{'阅读':>8}{'点赞':>6}{'评论':>5}{'转发':>5}{'分享':>5}{'发布时间':<14}{'链接':<38}") print(f" {YELLOW}{'─' * 122}{RESET}") print(f" {YELLOW}{header}{RESET}") print(f" {YELLOW}{'─' * 122}{RESET}") for i, e in enumerate(items, 1): title = (e.get("title") or "无标题")[:26] if len(e.get("title", "")) > 26: title += ".." author = (e.get("authorName") or "-")[:10] if len(e.get("authorName", "")) > 10: author += ".." wtype = "视频" if (e.get("workType") or e.get("hasVideo")) else "图文" views = format_number(e.get("viewCount")) likes = format_number(e.get("likeCount")) comments = format_number(e.get("commentCount")) reposts = format_number(e.get("repostCount")) shares = format_number(e.get("shareCount")) pub = (e.get("publishTime") or "")[:16] url = (e.get("workUrl") or "")[:36] if len(e.get("workUrl", "")) > 36: url += ".." print(f" {i:<4}{title:<28}{author:<12}{wtype:<5}" f"{views:>8}{likes:>6}{comments:>5}{reposts:>5}{shares:>5}{pub:<14}{url:<38}") print(f" {YELLOW}{'─' * 122}{RESET}") if count <= 3: print(f" {YELLOW}💡 相关结果较少(仅 {count} 条),建议尝试更宽泛的关键词{RESET}") print() # ─── CSV 导出 ────────────────────────────────────────────────────────────────────── def export_csv(items, keyword, output_dir): output_dir = Path(output_dir) output_dir.mkdir(parents=True, exist_ok=True) date_str = datetime.now().strftime("%Y-%m-%d") filename = f"头条搜索_{keyword}_{date_str}.csv" filepath = output_dir / filename fieldnames = ["标题", "作者", "类型", "阅读数", "点赞数", "评论数", "转发数", "分享数", "发布时间", "作品链接", "正文摘要"] with open(filepath, "w", newline="", encoding="utf-8-sig") as f: writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() for e in items: content = (e.get("content") or "")[:200] writer.writerow({ "标题": e.get("title", ""), "作者": e.get("authorName", ""), "类型": work_type_str(e.get("workType") or e.get("hasVideo")), "阅读数": e.get("viewCount", 0), "点赞数": e.get("likeCount", 0), "评论数": e.get("commentCount", 0), "转发数": e.get("repostCount", 0), "分享数": e.get("shareCount", 0), "发布时间": e.get("publishTime", ""), "作品链接": e.get("workUrl", ""), "正文摘要": content, }) return filepath # ─── HTML 报告生成 ───────────────────────────────────────────────────────────────── def generate_html(items, keyword, api_key): template_path = Path(__file__).parent.parent / "assets" / "report_template.html" if template_path.exists(): template = template_path.read_text(encoding="utf-8") else: template = get_fallback_template() date_str = datetime.now().strftime("%Y-%m-%d") timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") initial_data = json.dumps(items, ensure_ascii=False) html = template html = html.replace("{{KEYWORD}}", keyword) html = html.replace("{{DATE}}", date_str) html = html.replace("{{TIMESTAMP}}", timestamp) html = html.replace("{{TOTAL_COUNT}}", str(len(items))) html = html.replace("{{INITIAL_DATA}}", initial_data) html = html.replace("{{API_KEY}}", api_key) html = html.replace("{{SEARCH_API}}", SEARCH_API) html = html.replace("{{DETAIL_API}}", DETAIL_API) html = html.replace("{{SOURCE}}", SOURCE) return html def get_fallback_template(): return '''<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>今日头条爆款查询 - {{KEYWORD}} | {{DATE}}</title> <style> *{margin:0;padding:0;box-sizing:border-box} :root{--bg:#0a0f0a;--bg-card:#141f14;--bg-card-hover:#1a2a1a;--text:#e8efe8; --text2:#8a9a8a;--text3:#556655;--accent:#F59E0B;--accent-glow:rgba(245,158,11,0.12); --accent-border:rgba(245,158,11,0.25);--border:rgba(255,255,255,0.05); --font:'Space Grotesk',-apple-system,sans-serif;--radius:12px;--radius-sm:8px} body{font-family:var(--font);background:var(--bg);color:var(--text);min-height:100vh} .main-header{text-align:center;padding:2.5rem 1.5rem 1.5rem;max-width:900px;margin:0 auto} .main-header h1{font-size:1.6rem;font-weight:700;color:var(--accent)} .main-header .subtitle{color:var(--text2);font-size:0.85rem;margin-top:0.3rem} .search-container{position:sticky;top:0;z-index:100;background:linear-gradient(180deg,var(--bg) 80%,transparent); padding:1rem 1.5rem 1.5rem;backdrop-filter:blur(12px)} .search-wrapper{max-width:640px;margin:0 auto;display:flex;gap:0.6rem} .search-input{flex:1;padding:0.75rem 1.2rem;border:1.5px solid var(--border);border-radius:var(--radius); background:#0f1a0f;color:var(--text);font-size:0.95rem;font-family:var(--font);outline:none;transition:all 0.2s} .search-input:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-glow)} .results-container{max-width:900px;margin:0 auto;padding:0.5rem 1.5rem 2rem} .results-grid{display:flex;flex-direction:column;gap:0.5rem} .article-card{display:flex;align-items:flex-start;gap:0.9rem;padding:0.9rem 1rem;background:var(--bg-card); border:1px solid var(--border);border-radius:var(--radius-sm);cursor:pointer;text-decoration:none;color:inherit;transition:all 0.15s} .article-card:hover{background:var(--bg-card-hover);border-color:var(--accent-border);transform:translateX(2px)} .card-avatar{width:48px;height:48px;min-width:48px;border-radius:var(--radius-sm);overflow:hidden; background:var(--accent-glow);border:1px solid var(--accent-border);display:flex;align-items:center; justify-content:center;font-size:1rem;font-weight:700;color:var(--accent);user-select:none} .card-avatar img{width:100%;height:100%;object-fit:cover} .card-content{flex:1;min-width:0} .card-title{font-size:0.92rem;font-weight:500;line-height:1.45;margin-bottom:0.35rem; display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden} .card-meta{display:flex;flex-wrap:wrap;align-items:center;gap:0.5rem 1rem;font-size:0.75rem;color:var(--text2)} .card-meta .author{color:#FBBF24;font-weight:500;max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} .card-meta .pub-time{color:var(--text3);margin-left:auto} .type-tag{display:inline-block;padding:0.1rem 0.4rem;border-radius:4px;font-size:0.65rem;font-weight:600} .type-tag.video{background:rgba(239,68,68,0.15);color:#F87171;border:1px solid rgba(239,68,68,0.3)} .type-tag.article{background:rgba(59,130,246,0.15);color:#60A5FA;border:1px solid rgba(59,130,246,0.3)} .status-message{text-align:center;padding:3rem 1rem;color:var(--text2);font-size:0.9rem} .load-more-bar{display:flex;justify-content:center;padding:1.5rem 1rem 3rem} .load-more-btn{padding:0.65rem 2.2rem;border:1.5px solid var(--border);border-radius:var(--radius); background:var(--bg-card);color:var(--text2);font-size:0.85rem;font-family:var(--font);cursor:pointer;transition:all 0.2s} .load-more-btn:hover{border-color:var(--accent-border);color:var(--accent)} .footer{text-align:center;padding:2rem 1rem;color:var(--text3);font-size:0.72rem;border-top:1px solid var(--border);max-width:900px;margin:0 auto} </style> <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap" rel="stylesheet"> </head> <body> <div class="main-header"><h1>今日头条爆款查询</h1><p class="subtitle">{{DATE}} | 关键词搜索头条最新作品</p></div> <div class="search-container"> <div class="search-wrapper"><input type="text" class="search-input" id="searchInput" placeholder="输入关键词搜索头条作品..." value="{{KEYWORD}}" autofocus></div> </div> <main class="results-container"> <div class="results-grid" id="resultsGrid"></div> <div class="status-message" id="statusMsg" style="display:none">搜索中...</div> <div class="load-more-bar" id="loadMoreBar" style="display:none"><button class="load-more-btn" onclick="loadMore()">加载更多</button></div> </main> <footer class="footer">Generated at {{TIMESTAMP}} by 今日头条爆款查询 Skill | Powered by redfox.hk</footer> <script> const INITIAL_DATA = {{INITIAL_DATA}}; const resultsGrid = document.getElementById('resultsGrid'); function fmt(n){if(!n)return '0';n=Number(n);if(n>=10000)return(n/10000).toFixed(1)+'w';if(n>=1000)return(n/1000).toFixed(1)+'k';return String(n)} function esc(s){const d=document.createElement('div');d.textContent=s||'';return d.innerHTML} function renderCard(e){ const c=document.createElement('a');c.className='article-card';c.href=e.workUrl||'#';c.target='_blank';c.rel='noopener noreferrer'; const isVideo=e.workType||e.hasVideo;const tag=isVideo?'<span class="type-tag video">视频</span>':'<span class="type-tag article">图文</span>'; const img=(e.imageList&&e.imageList[0])?`<img src="${e.imageList[0]}" alt="" loading="lazy">`:esc((e.title||'头').charAt(0)); c.innerHTML=`<div class="card-avatar">${img}</div><div class="card-content"><div class="card-title">${esc(e.title)}</div><div class="card-meta"><span class="author">${esc(e.authorName)}</span>${tag}<span>👁 ${fmt(e.viewCount)}</span><span>👍 ${fmt(e.likeCount)}</span><span>💬 ${fmt(e.commentCount)}</span><span>🔄 ${fmt(e.repostCount)}</span><span class="pub-time">${(e.publishTime||'').slice(0,16)}</span></div></div>`; return c} INITIAL_DATA.forEach(e=>resultsGrid.appendChild(renderCard(e))); </script> </body> </html>''' # ─── API 代理 HTTP 服务 ───────────────────────────────────────────────────────────── class ProxyHTTPHandler(SimpleHTTPRequestHandler): api_key = None def do_GET(self): parsed = urlparse(self.path) if parsed.path == "/api/search": self._handle_search(parsed) else: super().do_GET() def do_POST(self): parsed = urlparse(self.path) if parsed.path == "/api/search": self._handle_search_post() elif parsed.path == "/api/detail": self._handle_detail_post() else: self.send_error(404) def _handle_search(self, parsed): params = parse_qs(parsed.query) keyword = params.get("keyword", [""])[0] offset = params.get("offset", ["0"])[0] if not keyword: self._send_json({"code": -1, "msg": "missing keyword"}) return self._do_proxy_request(SEARCH_API, {"keyword": keyword, "offset": offset, "source": SOURCE}) def _handle_search_post(self): content_length = int(self.headers.get("Content-Length", 0)) body = self.rfile.read(content_length) if content_length else b"{}" try: req_data = json.loads(body) except json.JSONDecodeError: self._send_json({"code": -1, "msg": "invalid json"}) return keyword = req_data.get("keyword", "") offset = req_data.get("offset", "0") if not keyword: self._send_json({"code": -1, "msg": "missing keyword"}) return self._do_proxy_request(SEARCH_API, {"keyword": keyword, "offset": str(offset), "source": SOURCE}) def _handle_detail_post(self): content_length = int(self.headers.get("Content-Length", 0)) body = self.rfile.read(content_length) if content_length else b"{}" try: req_data = json.loads(body) except json.JSONDecodeError: self._send_json({"code": -1, "msg": "invalid json"}) return opus_id = req_data.get("opusId", "") if not opus_id: self._send_json({"code": -1, "msg": "missing opusId"}) return self._do_proxy_request(DETAIL_API, {"opusId": str(opus_id)}) def _do_proxy_request(self, url, payload): try: resp = requests.post(url, json=payload, headers={"Content-Type": "application/json", "X-API-KEY": self.api_key}, timeout=15) self._send_json(resp.json()) except Exception as e: self._send_json({"code": -1, "msg": str(e)}) def _send_json(self, data): body = json.dumps(data, ensure_ascii=False).encode("utf-8") self.send_response(200) self.send_header("Content-Type", "application/json; charset=utf-8") self.send_header("Access-Control-Allow-Origin", "*") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) def do_OPTIONS(self): self.send_response(204) self.send_header("Access-Control-Allow-Origin", "*") self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS") self.send_header("Access-Control-Allow-Headers", "Content-Type, X-API-KEY") self.end_headers() def log_message(self, format, *args): pass def start_server(output_dir, api_key, port=8767): ProxyHTTPHandler.api_key = api_key os.chdir(str(output_dir)) server = HTTPServer(("127.0.0.1", port), ProxyHTTPHandler) t = threading.Thread(target=server.serve_forever, daemon=True) t.start() info(f"本地服务已启动: http://127.0.0.1:{port}") return server # ─── 主流程 ──────────────────────────────────────────────────────────────────────── def main(): parser = argparse.ArgumentParser( description="今日头条爆款内容查询 — 关键词搜索今日头条作品", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: python3 search.py "人工智能" python3 search.py "AI" --sort views python3 search.py "大模型" --hours 24 --video-only python3 search.py "新能源" --pages 5 --csv-only """, ) parser.add_argument("keyword", nargs="?", default="", help="搜索关键词(必填)") parser.add_argument("--sort", default="views", choices=["time", "views"], help="排序方式: views(默认) / time") parser.add_argument("--hours", type=int, default=0, help="限定最近 N 小时内发布(0=不限,默认 0)") parser.add_argument("--pages", type=int, default=DEFAULT_PAGES, help=f"翻页次数(默认 {DEFAULT_PAGES})") parser.add_argument("--video-only", action="store_true", help="只返回视频类作品") parser.add_argument("--output-dir", help=f"输出目录(默认 ~/Downloads/QoderToutiaoSearch)") parser.add_argument("--api-key", help="API Key") parser.add_argument("--no-open", action="store_true", help="不自动打开浏览器") parser.add_argument("--csv-only", action="store_true", help="仅生成 CSV 文件") parser.add_argument("--port", type=int, default=DEFAULT_PORT, help=f"HTTP 服务端口(默认 {DEFAULT_PORT})") args = parser.parse_args() banner = f"""{CYAN}{BOLD} ╔══════════════════════════════════════════════╗ ║ 今日头条爆款查询 · Toutiao Search ║ ║ 关键词搜索 · 实时数据 · 交互式报告 ║ ╚══════════════════════════════════════════════╝{RESET} """ print(banner) if not HAS_REQUESTS: error("缺少 requests 库,请安装: pip3 install requests") sys.exit(1) api_key = get_api_key(cli_key=args.api_key) if not api_key: print(f"{RED}╔══════════════════════════════════════════════════════╗{RESET}") print(f"{RED}║ 未配置 API Key,请通过以下方式之一配置: ║{RESET}") print(f"{RED}║ ║{RESET}") print(f"{RED}║ export REDFOX_API_KEY=ak_你的密钥 ║{RESET}") print(f"{RED}║ python3 search.py --api-key ak_你的密钥 ║{RESET}") print(RED + "║ echo '{\"api_key\":\"ak_你的密钥\"}' > ~/.qoder/apis/redfox.json ║" + RESET) print(f"{RED}║ ║{RESET}") print(f"{RED}║ 注册获取 Key: https://redfox.hk/settings/api-keys ║{RESET}") print(f"{RED}╚══════════════════════════════════════════════════════╝{RESET}") sys.exit(1) if not args.keyword: try: args.keyword = input(f"{CYAN}请输入搜索关键词: {RESET}").strip() except (EOFError, KeyboardInterrupt): print() sys.exit(0) if not args.keyword: error("搜索关键词不能为空") sys.exit(1) keyword = args.keyword output_dir = args.output_dir or str(DEFAULT_OUTPUT_DIR) output_dir = os.path.expanduser(output_dir) session = requests.Session() session.headers.update({"Content-Type": "application/json", "X-API-KEY": api_key}) # ── 搜索 ── items = run_search(session, keyword, max_pages=args.pages, hours=args.hours, video_only=args.video_only) if items is None or len(items) == 0: print(f"\n{YELLOW}[✗] 抱歉,未找到与「{BOLD}{keyword}{RESET}{YELLOW}」相关的今日头条作品{RESET}") print(f" {YELLOW}建议:{RESET} 尝试更短的关键词") print(f" {YELLOW}建议:{RESET} 放宽时间限制(--hours 0)") warn(f"如需更多数据,请访问 https://redfox.hk/settings/api-keys?source=github") sys.exit(0) # ── 排序 ── items = sort_results(items, args.sort) # ── 终端表格 ── print_terminal_table(items, keyword, args.sort) # ── 统计 ── views_list = [int(e.get("viewCount", 0) or 0) for e in items] total_views = sum(views_list) max_views = max(views_list) if views_list else 0 video_count = sum(1 for e in items if e.get("workType") or e.get("hasVideo")) print(f" {BOLD}统计:{RESET} 共 {len(items)} 条 | 视频 {video_count} 条 | " f"总阅读 {format_number(total_views)} | 最高阅读 {format_number(max_views)}") # ── CSV ── step("导出 CSV ...") csv_path = export_csv(items, keyword, output_dir) info(f"CSV 已保存: {csv_path}") # ── HTML ── if not args.csv_only: step("生成 HTML 报告 ...") html_content = generate_html(items, keyword, api_key) date_str = datetime.now().strftime("%Y-%m-%d") html_filename = f"头条搜索_{keyword}_{date_str}.html" html_dir = Path(output_dir) html_dir.mkdir(parents=True, exist_ok=True) html_path = html_dir / html_filename html_path.write_text(html_content, encoding="utf-8") info(f"HTML 报告已保存: {html_path}") server = start_server(output_dir, api_key, args.port) url = f"http://127.0.0.1:{args.port}/{html_filename}" if not args.no_open: step("打开浏览器...") try: subprocess.run(["open", url], check=True) except Exception: print(f" 请手动打开: {url}") print(f"\n{GREEN}╔══════════════════════════════════════════════════╗{RESET}") print(f"{GREEN}║ ✓ 搜索完成! HTML 报告: {url}{RESET}") print(f"{GREEN}║ 按 Ctrl+C 停止本地服务 ║{RESET}") print(f"{GREEN}╚══════════════════════════════════════════════════╝{RESET}\n") try: while True: time.sleep(1) except KeyboardInterrupt: print(f"\n{YELLOW} [→] 服务已停止{RESET}") else: print(f"\n{GREEN}╔══════════════════════════════════════════════════╗{RESET}") print(f"{GREEN}║ ✓ 搜索完成! CSV: {csv_path}{RESET}") print(f"{GREEN}╚══════════════════════════════════════════════════╝{RESET}\n") if __name__ == "__main__": main()
-
-
README.en.md 4.9 KB
# Toutiao Trending Content Search / toutiao-search --- ## Overview Enter a keyword and instantly find the latest Toutiao works. Sort by view count or publish time, filter by recent hours, and see all the key data at a glance — only fresh platform content, no stale cache. **Key Benefits** - **Always Fresh**: Every search pulls the latest platform content — only freshly published works, no stale cache. - **Flexible Filtering**: Sort by view count or publish time, and filter by recent hours to narrow your search window. - **Articles & Videos Covered**: Search both articles and videos in one go, with a dedicated mode to find video hits exclusively. - **Full Pagination**: Browse through results page by page when there are many results — nothing gets missed. - **Rich Data at a Glance**: Views, likes, comments, and reposts displayed clearly, with auto-generated CSV and interactive HTML reports ready to download. **Who Is This For** - 🔍 **Content Creators** — Stay on top of trending topics and hot content in your niche on Toutiao for creative inspiration. - 📊 **Marketing / Data Analysts** — Track keyword performance and content trends across the Toutiao platform. - 🏢 **Brands / MCNs** — Monitor competitor and industry keyword coverage, supporting strategic decisions. --- ## Features ### Core Capabilities - **Keyword Search**: Search Toutiao's latest trending works by any keyword. - **Dual Sort Options**: Sort by view count (`views`) or publish time (`time`) in descending order — quickly find high-performing or fresh content. - **Time Filtering**: `--hours N` limits results to the last N hours, rejecting stale data. - **Video-Only Mode**: `--video-only` returns only video works for targeted video hit discovery. - **Pagination**: `--pages N` controls page count, ~10 results per page, 3 pages by default. - **Terminal Table Output**: Title, author, views, likes, comments, reposts, publish date, and content type at a glance. - **CSV Auto-Export**: UTF-8 BOM encoded CSV, opens directly in Excel without encoding issues. - **Interactive HTML Report**: Dark theme, article/video labels, clickable work cards linking to originals, with in-page search. --- ## API Key Setup - This skill requires the environment variable: `REDFOX_API_KEY`. - `REDFOX_API_KEY` is provided by [RedFox Hub](https://redfox.hk/settings/api-keys?source=github). - Register at [RedFox Hub](https://redfox.hk?source=github) to obtain your `REDFOX_API_KEY`. - Configure the `REDFOX_API_KEY` environment variable before using this skill. - Verify the key's source, scope, validity period, and reset/revoke options before use. - Never hardcode or expose the key in code, prompts, logs, or output files. --- ## How to Use Just describe what kind of content you're looking for in natural language. ### Quick Reference | Intent | Example | Result | | ------ | ------- | ------ | | Search by topic | "Search Toutiao for AI content" | Searches with default sorting (by views) and displays results | | Specify sort order | "Search for LLM works by latest published" | Switches to publish-time sorting | | Set time range | "Search new energy works from the last 24 hours" | Sets `--hours 24`, returns only recent content | | Video only | "Search for product review videos" | Enables `--video-only`, returns only video works | | Browse more pages | "Show more results" / "Next pages" | Increases `--pages` to fetch more data | | Export data | "Export CSV" / "Generate HTML report" | Auto-generates CSV and interactive HTML report | ### Example Output After searching, you'll see a table like this: > 📊 Keyword "**AI**" — **30 works** found (Sort: By views | Time: Unlimited | Pages 1-3): | # | Title | Author | Views | Likes | Comments | Reposts | Publish Date | Type | |---|-------|--------|-------|-------|----------|---------|-------------|------| | 1 | [Trending article title truncated to 30 chars...](link) | Author Name | 12w | 3.2k | 412 | 89 | 2026-06-30 14:30 | Article | | 2 | [Another hot video title displayed...](link) | Another Author | 8.5w | 1.5k | 230 | 56 | 2026-06-30 12:00 | Video | > 📄 CSV and HTML report saved to `~/Downloads/QoderToutiaoSearch/`. (Data displayed as a terminal table, with CSV and HTML auto-saved simultaneously.) --- ## Use Cases | Scenario | Role | Example | Benefit | | -------- | ---- | ------- | ------- | | Hot topic tracking | Content creator | "Search Toutiao for AI content" | Instantly grasp the latest topic trends on Toutiao | | Viral content analysis | Marketing / Data | "Search LLM, sort by views" | Quickly find high-view hits and study viral patterns | | Competitor monitoring | Brand operator | "Search brand keyword from the last 24 hours" | Track competitor coverage and performance on Toutiao | | Content ideation | Creator | "Search new energy, export CSV for analysis" | Batch-collect data to discover content angles | | Video discovery | MCN | "Search reviews, video only" | Precisely find video hits and evaluate content direction | -
README.md 5 KB
# 今日头条爆款内容查询 / toutiao-search --- ## 简介 输入关键词就能搜到今日头条刚发布的作品,能按阅读量或发布时间排序、限定最近几小时内内容,给到你的都是平台刚更新的新鲜内容,不会拿旧缓存数据糊弄你。 **核心价值** - **内容新鲜不滞后**:每次搜索都直接拉取平台最新发布的作品,只给刚发的内容,不用老旧存档、缓存数据。 - **筛选方式灵活好用**:能按阅读量或发布时间排序,还能限定最近几小时内内容,自由搭配条件查找。 - **图文视频都能搜**:图文和视频作品一网打尽,想看哪种都能找到,不会遗漏任何类型。 - **翻页查看不遗漏**:结果多的时候可以一页页翻着看,不遗漏任何一条内容。 - **数据指标一目了然**:阅读量、点赞、评论、转发等互动数据直观展示,还能自动导出 CSV 和交互式 HTML 报告,下载即用。 **适用对象** - 🔍 **内容创作者** — 快速了解今日头条上同赛道最新趋势与热门选题,获取创作灵感。 - 📊 **运营 / 数据分析** — 追踪关键词相关内容在头条的数据表现,掌握流量趋势。 - 🏢 **品牌 / MCN** — 监控竞品与行业关键词在今日头条的最新内容表现,辅助策略决策。 --- ## 功能特性 ### 核心功能 - **关键词搜索**:输入任意关键词搜索今日头条最新爆款作品。 - **双排序切换**:按阅读量(`views`)或发布时间(`time`)降序排列,快速定位高质量或最新内容。 - **时间过滤**:`--hours N` 限定最近 N 小时内的新鲜内容,拒绝过期数据。 - **视频筛选**:`--video-only` 只返回视频类作品,精准找视频爆款。 - **翻页浏览**:`--pages N` 控制翻页次数,每页约 10 条,默认 3 页。 - **终端表格输出**:标题、作者、阅读、点赞、评论、转发、发布时间、类型一览无余。 - **CSV 自动导出**:UTF-8 BOM 编码 CSV,Excel 直接打开不乱码。 - **HTML 交互报告**:深色主题,图文/视频标签区分,作品卡片点击跳转原文,支持页内搜索。 --- ## 密钥获取与安全说明 - 本技能需要使用环境变量:`REDFOX_API_KEY`。 - `REDFOX_API_KEY` 由 [红狐 hub](https://redfox.hk/settings/api-keys?source=github) 提供。 - 请前往 [红狐 hub](https://redfox.hk?source=github) 注册账号,获取 `REDFOX_API_KEY`。 - 配置设备环境变量 `REDFOX_API_KEY` 后使用本技能。 - 在提供密钥前,请先确认密钥来源、可用范围、有效期及是否支持重置/撤销。 - 禁止在代码、提示词、日志或输出文件中硬编码/明文暴露密钥。 --- ## 使用指南 直接用自然语言描述需求,无需记忆命令。 ### 常用说法速查 | 意图 | 示例话术 | 效果 | | ---- | -------- | ---- | | 搜某个话题内容 | 「搜一下今日头条 AI 相关内容」 | 按阅读量排序搜索并展示结果 | | 指定排序方式 | 「按最新发布搜索大模型相关作品」 | 切换为按发布时间排序后搜索 | | 限定时间范围 | 「搜索最近 24 小时新能源相关作品」 | 设置 `--hours 24`,只返回一天内作品 | | 只看视频内容 | 「搜一下测评类的视频」 | 开启 `--video-only`,只返回视频作品 | | 翻页查看更多 | 「多翻几页」/「看更多结果」 | 增加 `--pages` 翻页获取更多数据 | | 导出数据 | 「导出 CSV」/「生成 HTML 报告」 | 自动生成 CSV 和交互式 HTML 报告 | ### 输出示例 查询完成后,你将看到类似以下格式的表格: > 📊 关键词「**AI**」查询到 **30 条**作品(排序:按阅读量 | 时间范围:不限 | 第 1-3 页),以下是详细数据: | # | 标题 | 作者 | 阅读 | 点赞 | 评论 | 转发 | 发布时间 | 类型 | |---|------|------|------|------|------|------|----------|------| | 1 | [某某爆款文章标题最多三十字...](链接) | 某某作者 | 12w | 3.2k | 412 | 89 | 2026-06-30 14:30 | 图文 | | 2 | [另一个热门视频标题展示...](链接) | 另一位作者 | 8.5w | 1.5k | 230 | 56 | 2026-06-30 12:00 | 视频 | > 📄 CSV 和 HTML 报告已保存至 `~/Downloads/QoderToutiaoSearch/`。 (数据以终端表格形式展示,CSV 和 HTML 同时自动保存。) --- ## 使用场景 | 场景 | 角色 | 示例问法 | 收益 | | ---- | ---- | -------- | ---- | | 热点话题追踪 | 内容创作者 | 「搜一下今日头条 AI 相关内容」 | 第一时间掌握头条平台最新话题动向 | | 爆款内容分析 | 运营 / 数据 | 「搜大模型,按阅读量排序」 | 快速找到高阅读量爆款,研究爆文规律 | | 竞品内容监控 | 品牌运营 | 「搜品牌关键词最近 24 小时内容」 | 追踪竞品在头条的最新投放与表现 | | 选题灵感采集 | 创作者 | 「搜新能源,导出 CSV 分析」 | 批量获取数据找选题方向,用数据说话 | | 视频内容挖掘 | MCN | 「搜测评,只看视频类」 | 精准筛选视频爆款,评估内容方向 | -
SKILL.md 5.7 KB
--- name: toutiao-search description: 今日头条爆款内容查询 — 输入关键词搜索今日头条最新作品(图文/视频),支持按阅读量/时间排序、限定时间范围,终端表格展示 + CSV 导出 + 交互式 HTML 报告。当用户需要搜索今日头条热门内容、追踪头条热点、分析爆款文章时使用。 --- # 今日头条爆款内容查询 输入关键词搜索今日头条最新作品(图文/视频),终端表格展示 + CSV 导出 + 交互式 HTML 报告。 > 需先配置 API Key,通过环境变量 REDFOX_API_KEY 或 --api-key 参数传入。 > 搜索范围:今日头条平台**最新发布的作品**,实时拉取,无缓存延迟。 --- ## 使用场景 当你需要执行以下任务时,应优先使用本技能: | 场景 | 示例 | |------|------| | **热点话题追踪** | 搜 "AI" 了解今日头条上最新的 AI 相关内容 | | **爆款内容分析** | 搜 "大模型" 找出阅读量最高的爆款文章 | | **竞品内容监控** | 搜品牌关键词,追踪竞品在今日头条的投放内容 | | **选题灵感采集** | 搜 "新能源" 批量导出 CSV 做数据分析找选题 | | **舆情快速了解** | 搜某事件关键词,快速了解头条平台讨论风向 | | **视频内容挖掘** | 搜 "测评" 配合 --video-only 筛选视频类爆款 | --- ## 使用方法 ```bash # 基础搜索 python3 "$SKILL_PATH/scripts/search.py" "关键词" # 按阅读量排序(默认按时间) python3 "$SKILL_PATH/scripts/search.py" "AI" --sort views # 限定最近 24 小时内发布的作品 python3 "$SKILL_PATH/scripts/search.py" "大模型" --hours 24 # 只查视频类作品 python3 "$SKILL_PATH/scripts/search.py" "测评" --video-only # 多页翻页,获取更多结果 python3 "$SKILL_PATH/scripts/search.py" "新能源汽车" --pages 5 # 仅导出 CSV,不生成 HTML python3 "$SKILL_PATH/scripts/search.py" "AI" --csv-only # 不自动打开浏览器 python3 "$SKILL_PATH/scripts/search.py" "AI" --no-open ``` 终端输出按**指定排序方式**(时间/阅读量)降序排列;结果较少或无结果时自动提示。 HTML 报告特性:深色主题 · 点击搜索按钮查询 · 作品卡片点击跳转原文 · 图文/视频标签区分 · 分页加载。 CSV / HTML 默认保存在 `~/Downloads/QoderToutiaoSearch/`。 --- ## 返回结果展示规范 向用户展示搜索结果时,**必须**使用以下完整字段列表,不得省略: | 序号 | 字段 | 说明 | |------|------|------| | # | 排名序号 | 从 1 开始 | | 标题 | 作品标题 | 最多显示 30 字,超出截断 | | 作者 | 作者昵称 | — | | 阅读量 | viewCount | 用千/万简写(如 3.2k、12w) | | 点赞数 | likeCount | 同上 | | 评论数 | commentCount | 同上 | | 转发数 | repostCount | 同上 | | 发布时间 | publishTime | 格式:`YYYY-MM-DD HH:mm` | | 类型 | workType | 图文 / 视频 | | 链接 | workUrl | 可点击跳转原文 | **表格示例:** ``` | # | 标题 | 作者 | 阅读 | 点赞 | 评论 | 转发 | 发布时间 | 类型 | |---|------|------|------|------|------|------|----------|------| | 1 | 某某爆款文章标题... | 某某作者 | 3.2k | 412 | 89 | 56 | 2026-06-28 14:30 | 图文 | ``` --- ## 参数说明 | 参数 | 说明 | 默认值 | |------|------|--------| | `keyword` | 搜索关键词(必填,位置参数) | — | | `--sort` | 排序方式:`views` / `time` | `views` | | `--hours` | 限定最近 N 小时内发布的作品(0=不限) | `0` | | `--pages` | 翻页次数(每页约 10 条) | `3` | | `--video-only` | 只返回视频类作品 | — | | `--output-dir` | 输出目录 | `~/Downloads/QoderToutiaoSearch` | | `--api-key` | 指定 API Key | — | | `--no-open` | 不自动打开浏览器 | — | | `--csv-only` | 仅生成 CSV,不生成 HTML | — | | `--port` | HTML 本地服务端口 | `8767` | --- ## API Key 配置 任选一种方式配置个人 Key: | 方式 | 命令 | |------|------| | 环境变量(推荐) | `export REDFOX_API_KEY=ak_你的密钥` | | 命令行参数 | `--api-key ak_你的密钥` | | 配置文件 | `echo '{"api_key":"ak_你的密钥"}' > ~/.qoder/apis/redfox.json` | 注册地址:[redfox.hk](https://redfox.hk/settings/api-keys?source=github) --- ## 功能特点 - **双接口深度查询**:`searchWork` 搜索列表 + `workDetail` 获取完整数据(阅读/点赞/评论/转发/分享) - **时间过滤**:限定最近 N 小时内的新鲜内容,拒绝过期缓存 - **灵活排序**:按发布时间或阅读量降序排列 - **视频筛选**:`--video-only` 只找视频类爆款 - **终端表格**:标题、作者、阅读、点赞、评论、转发、发布时间、链接、类型 - **CSV 导出**:自动生成 UTF-8 BOM 编码的 CSV - **HTML 交互报告**:深色主题,图文/视频标签区分,支持页内搜索 - **本地代理服务**:避免浏览器跨域限制,支持页内实时搜索 --- ## 依赖 ```bash pip3 install requests ``` --- ## 常见问题 **Q:能搜到每条爆款吗?** A:能保证每次的关键词都有作品。 **Q:搜索范围覆盖多久之前的作品?** A:目前提供近 1 年的作品,优先会覆盖近期发布的内容。 **Q:为什么搜索后还要等待详情查询?** A:搜索接口只返回基础信息,同时提供单作品补全阅读量、点赞、转发等完整指标,让数据更完整。 **Q:搜索后如何保存数据?** A:可以让 AI 为你导出 Excel,也可以让 AI 导出 HTML。 **Q:额度用完怎么办?** A:前往 [redfox.hk](https://redfox.hk/settings/api-keys?source=github) 注册获取 Token。 **Q:最多能获取多少条?** A:每页约 10 条,默认翻 3 页(约 30 条)。可通过 `--pages` 参数调整。
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.