问答
怎么每小时抓一次微博热搜,存成历史数据?
简短回答
每小时调用一次微博热搜,把每一行连同抓取时间追加进文件或数据表。每次快照就是一次调用,$0.002,一个月每小时一次共 720 次,合计 $1.44。接口本身不保存历史,所以你的历史库从开始抓的那天算起。
做传播研究的人想统计一个话题在榜上待了几个小时,品牌团队想证明上周二晚上的活动冲到过第 3 名,都需要同一样东西:过去某个时刻的热搜榜长什么样。微博不提供这个,最实际的办法就是自己定时拍快照。微博热搜调一次就返回整张榜,所以一个每小时跑一次的定时任务、二十来行代码,就能攒出一份干净的时间序列。
不同采样频率的费用
| 采样频率 | 每月调用次数 | 每月费用 |
|---|---|---|
| 每小时一次 | 720 | $1.44 |
| 每 30 分钟一次 | 1,440 | $2.88 |
| 每 10 分钟一次 | 4,320 | $8.64 |
算话题在榜时长和最高排名,每小时一次就够了。那种上榜二十分钟就掉下去的话题,只有 10 分钟一次才抓得到。
放进 cron 的脚本
import csv, os, datetime, requests
hour = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H")
r = requests.get(
"https://api.okdata.online/v1/weibo/trends/list",
headers={
"Authorization": f"Bearer {os.environ['OKDATA_API_KEY']}",
"Idempotency-Key": f"weibo-hot-{hour}",
},
params={"fields": "rank,name,heat"},
timeout=30,
).json()
if r["code"] != "ok":
raise SystemExit(f"{r['code']}: {r['message']}")
taken = r["meta"]["fetched_at"]
with open("weibo_hot.csv", "a", newline="", encoding="utf-8") as f:
w = csv.writer(f)
for row in r["data"]["items"]:
w.writerow([taken, row["rank"], row["name"], row["heat"]])
crontab 里写一行 0 * * * * python3 /opt/weibo_hot.py 就行。幂等键按小时生成,同一个小时里 cron 万一跑了两次,第二次直接回放已经存好的榜单,不再扣费。
容易踩的坑
- 推广位被过滤掉的地方,排名会跳号。排名不连续不代表数据缺了。
- 有些行的
heat是 null。这一行照样存,排名依然有效。 - 调用失败(比如返回
upstream_busy)不扣费。一分钟后重试一次,别直接丢掉这一小时;实在没拿到的小时也记一笔,分析时才知道哪里有空档。 - 同一个话题在发酵过程中,名字常常会改几个字。统计在榜时长时,匹配要宽松一点。
什么时候换别的办法
- 需要开始抓之前的数据?自己拍快照没法补历史。GitHub 上有几个开源项目已经把热搜榜存了好几年,先看看它们的时间范围和采样频率是否符合你的研究。
- 商业舆情系统会卖带图表的历史热搜数据。需要好几年的回溯、又完全不想写代码时,花这笔钱是值得的。
更新于