做一个真能查出谁取关了你的推特粉丝追踪器
做一个真能查出谁取关了你的推特粉丝追踪器
你的粉丝数从 4,102 变成了 4,098。
四个人走了。X 不会告诉你是谁,也不会告诉你什么时候。到明天,三个新粉丝进来,数字又变回 4,101 —— 像什么都没发生过。
这个缺口就是粉丝追踪工具存在的理由。而大多数工具处理得并不好。
这篇讲怎么做一个真正管用的,以及那个决定它到底能不能查出东西的设计选择。
为什么光看数字没用
任何 X 主页都有 followers_count,一个实时更新的数字。它把你真正想知道的东西全藏起来了。
设想某一天你涨了 12 个粉、掉了 12 个粉。数字纹丝不动。在任何基于计数的追踪器看来,这一天什么也没发生 —— 而实际上有 24 个关系变了,走掉的那 12 个可能恰恰是最重要的。
数字给你的是净值,追踪需要的是集合。
把这个集合从 X 里完整拿出来才是费劲的部分 —— 我们把它做成了一次调用,你可以直接从差集逻辑开始写。
这一个区别,就是"能用的追踪器"和"在你的受众底下暗流涌动时报一条平线"的分水岭。
真正重要的那个设计选择
想知道谁取关了,你得比对两份粉丝列表快照 —— 昨天的和今天的。剩下的交给集合运算:
新增粉丝 = 今天 − 昨天
取关的人 = 昨天 − 今天
很简单。人们做错的地方在于:每份快照里到底存什么。
直觉是把整个资料都存下来 —— 昵称、简介、头像、粉丝数,全套。看起来周全,但这正是大多数自建追踪器一个月内就变慢变贵的原因。
检测变化不需要资料,只需要身份。 存 ID;只对真正发生变化的那几个账号去查资料。
对一个 5 万粉的账号,这是"每天存 5 万份完整资料"和"每天存 5 万个整数"的区别。而在抓取那一侧,轻量接口的单次成本也低于完整版 —— 这个差价每天都在累加。
可运行的代码
两步:拉当前粉丝集合,和上次的结果做差集。
第一步 —— 拉粉丝列表
import requests
BASE = "https://api.socialapi.tech"
KEY = "your_api_key"
def get_followers(username):
"""返回完整的粉丝 ID 集合, 一直翻页到底。"""
ids, cursor = set(), None
while True:
params = {"username": username, "limit": 200}
if cursor:
params["cursor"] = cursor
r = requests.get(
f"{BASE}/v1/user/followers_ids",
params=params,
headers={"X-API-Key": KEY},
timeout=60,
)
r.raise_for_status()
body = r.json()
for row in body["data"]:
ids.add(row["id"])
cursor = body.get("meta", {}).get("next_cursor")
if not cursor:
break
return ids
注意用的是 followers_ids 而不是 followers —— 它只返回 {id, username},而差集需要的就只有这些。
翻页上最坑人的一个细节: 结束条件是 next_cursor 为空,不是某一页返回的条数少于你要的数量。列表中间出现短页是正常的,不代表到底了。如果把短页当成终点,你会悄无声息地截断列表 —— 而这在下一次运行时会表现为一大批根本不存在的"取关"。
第二步 —— 和昨天做差集
import json, pathlib
def track(username):
snapshot = pathlib.Path(f"{username}_followers.json")
current = get_followers(username)
if not snapshot.exists():
# 第一次跑, 还没有可比对的基准
snapshot.write_text(json.dumps(sorted(current)))
print(f"已保存基准: {len(current)} 个粉丝")
return
previous = set(json.loads(snapshot.read_text()))
gained = current - previous
lost = previous - current
print(f"新增 {len(gained)} 个, 取关 {len(lost)} 个")
for uid in lost:
print(f" 取关: {uid}")
snapshot.write_text(json.dumps(sorted(current)))
整个追踪器就这些。每天跑一次,你就有了一份完整的"谁来了、谁走了"的历史。
第三步 —— 把 ID 还原成人
差集给你的是 ID。要显示昵称,只查发生变化的那几个:
def describe(user_ids):
"""把少量 ID 还原成资料。只对差集调用这个。"""
r = requests.get(
f"{BASE}/v1/batch/user_info",
params={"ids": ",".join(str(i) for i in user_ids)},
headers={"X-API-Key": KEY},
timeout=60,
)
return r.json()["data"]
如果 4 个人取关,这就是一次请求查 4 份资料 —— 而不是 5 万份。
这种批量查正是我们批量接口的用途 —— 一次调用,多个 ID。
一个好的追踪器该记什么
差集跑通之后,有意思的部分在于你保留什么:
时间。 取关是会扎堆的。如果某条推文发出后一小时内走了十一个人,那说明的是这条推文的问题,而不是你的受众整体有问题。
是谁,而不只是多少个。 掉一个 200 粉的账号和掉一个 20 万粉的账号是两回事。在做差集的那一刻把粉丝数一并记下来,你就能给它们加权。
方向变化。 关注→取关→又关注,这种行为和一次性离开完全不同。只有基于集合的追踪器才看得见这件事。
互关状态。 把粉丝集合和关注集合交叉一下,互关关系就白送给你了 —— 同样两份列表,多做一次集合运算而已。
多久跑一次?
对大多数账号,每天一次是正确的默认值,原因不太直观:X 的粉丝列表在时间上并不是完全稳定和有序的。 每五分钟拉一次,你会看到一些账号忽然出现又忽然消失,而它们其实既没关注也没取关你。那些是假象,只会把你的日志塞满误报。
每日快照的间隔足够长,列表已经稳定下来了。如果你确实需要更细的粒度,可以每小时跑,但要加一条规则:一个账号必须在连续两次快照里都缺席,才判定为取关。这一条规则能消掉绝大部分噪声。
对超大账号还要留意翻页成本:5 万粉丝按每页 200 条算,一次快照 250 个请求。每天一次没问题;每五分钟一次就是每天 7.2 万个请求,而换来的信息根本没变。
追踪别人的账号
上面所有做法对任何公开账号都成立,不限于你自己的。反正读的都是公开的粉丝列表 —— 代码一个字都不用改,只换 username。
这其实是更常见的商业用法:看谁开始关注了竞品、发现某个行业人物关注了一家新公司、捕捉一批账号同时聚拢到某个人身上的那一刻。
⚠️ 有一条边界要说清楚:这只对公开账号有效。受保护的账号不对外暴露粉丝列表 —— 对你、对任何人都一样,任何 API 都改变不了这一点。如果某个工具声称能做到,那它要么在骗你,要么在做你不会想参与的事。
大家会问的问题
能看到具体是谁取关了我吗? X 自己不给 —— 它有意不暴露这个。办法是比对你自己的粉丝快照,也就是上面代码做的事。
我查别人的粉丝,对方会收到通知吗? 不会。读取公开粉丝列表不是一个可见动作,不产生任何通知。
怎么自动追踪粉丝和取关? 把差集脚本挂到定时任务上 —— cron、GitHub Action、定时触发的 Lambda 都行,脚本本身不用改。
我之前没记录,现在能查出谁取关了吗? 不能。取关只能靠比对发现,所以必须有基准。今天的快照就是明天的基准 —— 越早开始越早有用。
怎么查某个人关注了谁? 把粉丝接口换成关注接口,同样的做法。差集逻辑一模一样。
所谓的"粉丝检查器"是什么? 基本就是上面这套的托管版 —— 帮你存快照、用界面展示差集。自己搭一个下午就够,而且历史数据在你自己手里。
能追踪粉丝数的变化曲线吗?
能,而且是顺带的:每次运行的 len(current) 就是时间序列。真正事后补不回来的是集合,数字随时能从集合算出来。
最多能追踪多少粉丝? 受翻页量限制,没有硬性上限。每页 200 条,10 万粉的账号一次快照 500 个请求。
能用来找精准粉丝吗? 可以 —— 拉一个受众与你目标重合的账号的粉丝列表,你就得到了一批已经对这个话题感兴趣的人。之后按粉丝数或简介关键词筛选即可。
为什么我的追踪器报了根本没发生的取关?
几乎都是翻页被截断。某次运行提前结束的话,它没拉到的每个粉丝都会看起来像取关。检查一下你是不是在跟 next_cursor 到空,而不是遇到短页就停。
能看到谁访问了我的主页吗? 不能。X 不对任何人暴露主页访问数据,也没有对应的接口。任何声称能给你这个的工具都是编的。
取关之后又关注回来,能看出来吗? 能,前提是你存的是集合而不是数字 —— 你会看到他离开、然后重新出现。基于计数的追踪器则完全看不到。
怎么查推特上谁的粉丝最多?
那是另一个问题,需要的是排行而不是差集。做法是拉一批候选账号的资料,按 followers_count 排序。
有免费的做法吗? 代码是免费的,数据不是。你总得有个粉丝列表的来源,而 X 官方接口按读取次数收费。无论走哪条路,都要为翻页量做预算。
查 X 的取关和查推特的一样吗? 完全一样 —— X 和推特是同一个平台、同一批数据,改名没有改变任何做法。
能同时追踪多个账号吗? 可以。脚本接收一个 username,循环一遍就行。每个账号存一份独立快照,差集互不干扰。
如果对方是被封号而不是取关呢?
他会从列表里消失,所以单纯的差集会报成取关。如果这个区别对你重要,把 lost 集合里的 ID 拿去查一下 —— 被封的账号查不出有效资料。
怎么查推特粉丝数? 它就在资料页上。 ★一个调用就能拿到任何公开账号的粉丝数★,你自己的或别人的。
推特有实时粉丝数吗? 按间隔轮询资料页。 ★"实时"就是你的轮询频率★ —— 没有任何端点会把粉丝数变化推给你。
怎么看粉丝数历史? ★只有你自己记录过才行。★ 平台不为你保存历史 —— 一个计数是一次读数,不是一个序列。
能拿到历史粉丝数吗? 不能追溯。 ★这就是贯穿本页的那个不对称:往前捕获极其简单,往回重建根本不可能。★
怎么画粉丝增长曲线? 每天存一次读数,再把序列画出来。 ★单个数字没有任何可比对象。★
粉丝计数器是什么? 就是循环读取资料页。 ★每一个都是这么工作的★,不管界面看起来像什么。
怎么长期追踪粉丝统计? 按固定间隔采样并存下每一次。 ★间隔必须恒定★,否则这个序列衡量的是你自己的调度节奏。
能看到谁取关了我吗? 只能靠比对快照 —— ★没有取关通知,也没有取关者名单★。
能搜索我的粉丝吗? 把列表取回来在本地过滤。 ★不存在跨粉丝的服务端搜索。★
怎么分析我的粉丝? 把带完整资料的列表拉下来,再按粉丝数、简介或活跃度分层 —— 识别不活跃账号。
粉丝数包含不活跃账号吗? 包含。 ★计数就只是计数★ —— 它不说明有多少是真实或活跃的。
我的粉丝数为什么会波动? 正常的自然流失,加上平台定期清理。 ★每天小幅变动是正常的,不值得做出反应。★
推特粉丝追踪是什么? 按计划记录粉丝数和粉丝列表。 ★平台不存历史,所以"追踪"是你做的事,不是你取回来的东西。★
有粉丝统计工具吗? 任何存每日快照的工具都是。 ★没有一个能给你"你开始之前"的历史。★
能拿到粉丝数历史吗? ★只有你记录过的。★ 不存在可供请求的历史序列 —— 这就是贯穿本页的那个不对称。
能看别人账号的历史粉丝数吗? 同一个答案 —— ★你追踪过就有;追溯地要就没有★。
怎么画粉丝增长曲线? 每天存一次读数,再把序列画出来。 ★单个数字没有任何可比对象。★
粉丝计数器是什么? 就是循环读取资料页。 ★每一个都是这么工作的★,不管界面看起来像什么。
粉丝数每天都会波动吗? ★会,而且小幅变动是正常的★ —— 自然流失加上平台定期清理。
怎么衡量粉丝增长率? 两次快照的差值,除以天数。 ★至少需要两次读数。★
能拿到粉丝的人群画像吗? 只能拿到每个粉丝资料页上公开的东西 —— ★简介、地点文本、粉丝数★。没有任何推断出来的。
怎么按粉丝数给我的粉丝排序? 把带完整资料的列表取回来,在本地排序。 ★不存在服务端排序。★
该多久拍一次快照? ★每天足够,而且间隔必须保持恒定★ —— 改动它会污染你自己的趋势线。
如果你不想自己搭这些管道
上面的追踪逻辑大概四十行。真正花时间的是它底下的东西 —— 不会截断的翻页、能区分限流和瞬时失败的重试,以及让这一切无人值守地长期跑下去。
这部分正是我们的 API 在做的事。一个接口,固定的按次价格,没有属于你的限流要管。轻量的 followers_ids 接口单次成本低于完整资料版 —— 当你每天要翻六位数的粉丝列表时,这个差价很实在。
有两种情况我们不收钱:请求还没发出去就被我们拒绝的(比如参数写错),以及我方原因导致的错误。你可以自己验证 —— 故意发一个错误请求,看余额动不动。
延伸阅读:X 接口报限流错误时该怎么办 · 怎么移除粉丝而不拉黑对方。