抓取微博、抖音、小红书热榜,输出统一结构的 JSON 快照。仓库自带两条发布链路:
- 实时:用仓库内置的 Cloudflare Worker 每 10 分钟触发一次 GitHub Actions,把最新榜单发布到 GitHub Pages。
- 归档:每小时把快照、日报和 README 提交回仓库,仓库本身就是一份可检索的热搜历史。
实时 JSON:https://solarhell.github.io/hot-search-crawler/latest.json
数据更新于 2026-11-14 08:24(Asia/Shanghai),展示今日榜单前 10。
- 老板假装扫了500余次付款码 — 114.9万 · 热
- 产妇求剖腹产遭拒孩子脑瘫丈夫发声 — 82万 · 热
- 主人提醒邻居别给猫按电梯 — 67.1万
- APEC时隔12年再次来到中国 — 66.2万
- 美全国范围实施地面停飞 — 65.7万 · 新
- 白小纯因为青稚这几句话一直憋眼泪 — 51.3万 · 综艺
- 飞秒手术全球1000万例中国750万例 — 51万
- 黄仁勋因私德丑闻离场只是表象 — 50.4万 · 新
- 王晶回应麻将女王票房 — 49.4万
- 华为五界顺序调整 — 49.1万
- 美国航班全国性地面停飞 — 1198.1万
- 阿根廷vs乌拉圭 — 1167.6万
- 手工敲虾到底有多好吃 — 1110.4万
- 解锁APEC限定“寻宝”路线 — 1110万
- 油价下调 加满一箱油少花约26元 — 1110万
- 课本里的逍遥游有画面了 — 1100.9万
- 用一次旅行来敬重自己 — 1015.4万
- EDG官宣CHICHOO离队 — 1011.8万
- 2027研究生报考人数347万 — 915.4万
- 国务院安委会约谈11个市级政府 — 905.2万
- 用万能旅行拍照姿势美美出片 — 947.5万 · 热
- 耗时三年拍下古诗词里的中国 — 934.8万 · 热
- 我拍到了海鸥雨 — 903.2万
- 超日常美食教程速来get — 886万
- 定格这一刻的日照金山 — 871.1万
- 你可以永远相信赛里木湖的美景 — 783.1万 · 热
- 拼豆上也可以作画了 — 749.8万 · 热
- 我的家庭旅行更像是打副本 — 737.1万
- 原来古诗词里的河南真的存在 — 727.8万 · 热
- 蒸出了奶香爆米花馒头 — 716.2万 · 热
- 三个平台,一种结构:微博、抖音、小红书统一为
rank / title / url / hot / tag,平台特有字段放进extra。 - 实时与归档分离:高频运行只发布 Pages 的
latest.json,不写 git;每小时才落一份归档(最新归档快照为data/latest-hourly.json),避免仓库历史膨胀。 - 过滤广告:微博接口里混在
realtime中的推广位(is_ad=1)会被剔除,不占榜单名次。 - 限流友好:识别小红书
300013限流码,不连续重试;普通失败按 2s → 4s → 8s 指数退避。 - 零数据库依赖:默认写文件,可直接用 GitHub Pages、对象存储或任意静态服务消费。
- 可持续归档:小时快照 + 按天重建的
summary.json+ 每日 Markdown 归档,重复运行或补抓都不会污染统计。 - 常驻模式:
daemon子命令按间隔运行,连续失败时自动退避到 15 分钟。
需要 Go 1.27+。
git clone https://github.com/solarhell/hot-search-crawler.git
cd hot-search-crawler
# 抓取一次,并更新 README 里的今日热榜
go run ./cmd/hot-search-crawler crawl --update-readme
# 只看看当前数据,不落盘
go run ./cmd/hot-search-crawler crawl --dry-run
# 只生成实时 JSON(用于 Pages 发布,不写小时快照/日报)
go run ./cmd/hot-search-crawler crawl --live --data dist也可以直接安装:
go install github.com/solarhell/hot-search-crawler/cmd/hot-search-crawler@latest构建二进制:
go build -o bin/hot-search-crawler ./cmd/hot-search-crawler
./bin/hot-search-crawler crawldata/
2026-10-30/
09.json # 每小时一次完整抓取(含各平台成功/失败状态)
10.json
summary.json # 当天聚合:最高热度、最好名次、在榜次数、首次/最后出现
latest-hourly.json # 最近一次小时归档快照;实时数据见 GitHub Pages
archives/
2026-10-30.md # 当天热榜 Markdown 归档
README.md # 标记区间内的今日热榜
小时快照:
{
"schema_version": 1,
"generated_at": "2026-10-30T09:32:05+08:00",
"timezone": "Asia/Shanghai",
"boards": [
{
"platform": "weibo",
"name": "微博",
"ok": true,
"fetched_at": "2026-10-30T09:32:05+08:00",
"elapsed_ms": 126,
"items": [
{
"rank": 1,
"title": "TES战胜BLG",
"url": "https://s.weibo.com/weibo?q=TES%E6%88%98%E8%83%9CBLG",
"hot": 1748429,
"tag": "新"
}
]
}
]
}抓取失败的平台也会保留在快照里,ok 为 false,error 写原因,items 为空数组。单个平台失败不会影响其他平台。
小红书等平台的热度是 "947.5w" 这样的文本,hot 会归一化成 9475000,同时用 hot_text 保留原始值。
| 平台 | 接口 | 热度字段 | 备注 |
|---|---|---|---|
| 微博 | weibo.com/ajax/side/hotSearch |
num |
使用 Chrome TLS 指纹;过滤 is_ad=1 |
| 抖音 | aweme.snssdk.com/aweme/v1/hot/search/list/ |
hot_value |
只取 word_list,不混入“实时上升热点” |
| 小红书 | edith.xiaohongshu.com/api/sns/v1/search/hot_list |
score(947.5w) |
复用固定 iOS 设备指纹,可能被限流 |
小红书没有公开热榜接口,internal/source/xiaohongshu.go 中的请求头来自客户端抓包。同一设备指纹被间歇性拒绝(300013)是常态,程序会按限流处理并保留其他平台的数据。指纹可能随客户端升级失效,届时需要重新抓包,或用 --sources weibo,douyin 暂时关闭小红书。
hot-search-crawler crawl [flags] 抓取一次
hot-search-crawler daemon [flags] 常驻定时抓取
hot-search-crawler sources 列出内置平台
hot-search-crawler version 显示版本
常用参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
--sources |
weibo,douyin,xiaohongshu |
启用的平台,逗号分隔 |
--top |
20 |
每个平台保留的条数 |
--data |
data |
小时快照目录 |
--archives |
archives |
每日 Markdown 归档目录 |
--readme |
README.md |
README 路径 |
--update-readme |
false |
把今日热榜写入 README |
--live |
false |
只写实时 latest.json,不生成小时快照/日报/README |
--dry-run |
false |
只输出快照 JSON,不写文件 |
--timeout |
25s |
单次请求超时 |
--retries |
2 |
普通失败的重试次数(限流不重试) |
--interval |
30m |
仅 daemon:抓取间隔 |
--debug |
false |
输出调试日志 |
crawl 在至少一个平台成功时返回 0;全部失败返回 1,但失败快照仍会写入,便于排查。daemon 不会因单轮失败退出。
| 数据 | 频率 | 发布位置 | 触发方式 |
|---|---|---|---|
| 实时快照 | 每 10 分钟 | GitHub Pages latest.json |
Cloudflare Worker → live.yml;GitHub schedule :13/:43 兜底 |
| 小时归档 | 每小时 | data/、archives/、README |
Cloudflare Worker :23 + GitHub schedule :47 兜底 |
GitHub Actions 自带的 schedule 是 best-effort:2026 年实测常见延迟 1.5–4.5 小时,低活跃仓库的每小时任务实际每天只跑 4–8 次,丢跑不会留下记录。因此实时链路改为 Cloudflare Worker 调用 workflow_dispatch,部署步骤见 Cloudflare Worker 文档;cron-job.org 和 systemd timer 的替代方案见 外部定时器文档。
高频运行只发布 Pages,不写 git,原因是单次快照约 24KB,10 分钟一次会让仓库一年增长数百 MB;归档仍保持每小时一份,兼顾历史价值和仓库体积。
仓库内置三个工作流:
live.yml:由 Cloudflare Worker 每 10 分钟触发,抓取后只把latest.json部署到 GitHub Pages,不产生 git 提交;GitHubschedule在:13/:43做低频兜底。crawl.yml:每小时归档,写入小时快照、日报和 README。Cloudflare Worker 在:23触发,GitHub 自带schedule在:47兜底;如果当前小时已有快照则自动跳过,避免重复提交。test.yml:只在代码变更时运行go vet和go test -race,数据提交不会触发。
第一次使用需要:
- 在 Settings → Actions → General 确认 Workflow permissions 为 Read and write permissions。
- 在 Settings → Pages 把 Source 设为 GitHub Actions(或运行一次
live.yml,按提示授权)。 - 按 Cloudflare Worker 部署文档 创建 fine-grained PAT 并部署 Worker;也支持 cron-job.org 或自己的 systemd timer,见 外部定时器文档。
cd deploy/cloudflare-worker
npm install
npx wrangler login
npx wrangler secret put GITHUB_TOKEN
npm run deployWorker 的两个 cron 会分别触发 live.yml(每 10 分钟)和 crawl.yml(每小时 :23)。完整说明和失败告警配置见 deploy/cloudflare-worker/README.md。
如果 GitHub Runner 访问国内接口不稳定,可以在 workflow 中给抓取步骤设置 HTTPS_PROXY,surf 会读取标准代理环境变量:
- name: Crawl
env:
HTTPS_PROXY: ${{ secrets.HTTPS_PROXY }}
run: go run ./cmd/hot-search-crawler crawl --update-readme实现 internal/source.Source:
type Source interface {
ID() string
Name() string
Fetch(ctx context.Context, limit int) ([]Item, error)
}然后在 internal/source/registry.go 的 All() 里注册。解析逻辑建议拆成纯函数并用 testdata/ 中的真实报文做单元测试,HTTP 请求、超时、重试和落盘由框架统一处理。
- 每次从小时文件重建日报:
summary.json不是增量合并,而是读取当天所有HH.json重新聚合。这样重复运行、手动补抓、删除异常快照都不会让统计漂移。 - 广告与空标题在计数前过滤:
--top限制的是有效条目数,不会被推广位占掉名额。 - 限流不重试:小红书
300013的RetryAfter是 10 分钟,连续敲门只会延长封禁;普通网络错误才走指数退避。 - 固定北京时间:文件名和日报统一用 UTC+8,避免服务器时区不同导致目录漂移。
本项目仅用于学习和技术研究。热搜数据归各平台所有,请遵守目标网站的 robots.txt、服务条款和当地法律法规;不要高频请求、绕过付费能力或用于商业用途。因使用本项目产生的任何后果由使用者自行承担。