A 关注了 B 吗?查两个都不属于你的账号之间的关系
A 关注了 B 吗?查两个都不属于你的账号之间的关系
最直觉的做法,恰好是最贵的做法。
要知道一个账号有没有关注另一个,大多数代码会去翻粉丝列表找匹配。★对一个百万粉的账号,这是几千次请求,只为回答一个是/否★ —— 而且账号越大越慢。
有一个直接的做法,而大多数人漏掉的是:★这个答案有两个互相独立的半边★。
关系是有方向的
"这两个账号互相关注吗"其实是两个问题:
A → B ? A 有没有关注 B
B → A ? B 有没有关注 A
四种可能状态,而其中只有一种是"互关":
| A 关注 B | B 关注 A | 含义 |
|---|---|---|
| ✅ | ✅ | ★互关★ |
| ✅ | ❌ | A 是单方面关注者 |
| ❌ | ✅ | B 是单方面关注者 |
| ❌ | ❌ | 没有关系 |
★把这个塌缩成一个布尔值,是这里最常见的建模错误★ —— 也是"他们有联系吗"这类功能给出令人困惑答案的根源。
两个方向来自同一个调用,这正是这个检查便宜的原因。
直接查
import requests
BASE = "https://api.socialapi.tech"
KEY = "your_api_key"
HDRS = {"X-API-Key": KEY}
def relationship(source, target):
"""两个都不属于你的账号之间,两个方向的关系。"""
r = requests.get(f"{BASE}/v1/user/relationship",
params={"source": source, "target": target},
headers=HDRS, timeout=60)
r.raise_for_status()
d = r.json()["data"]
return {
"follows": d["following"], # source → target
"followed_by": d["followed_by"], # target → source
"mutual": d["following"] and d["followed_by"],
}
r = relationship("nasa", "esa")
print(f"nasa → esa: {r['follows']} esa → nasa: {r['followed_by']} 互关: {r['mutual']}")
★两个账号都不需要是你的。★ 这是一次第三方对第三方的检查 —— 你读的是两个与你毫无关系的账号之间的公开关系。
这一点值得验证而不是相信: 我们用已知不对称的账号对测过,★因为一个很合理的故障模式是:接口悄悄忽略 source 参数,返回的其实是"我们自己的账号 vs target"★。对不对称的账号对跑两个方向,结果是严格镜像,这才证明 source 确实被尊重了。
★这个验证值得对任何关系类接口都做一遍,包括我们的★ —— 一个"错得很合理"的布尔值,是那种能存活好几个月的 bug。
什么时候用它、什么时候翻列表
★规则:两个账号都已知就直接查。需要发现他们是谁,才去翻列表。★
| 问题 | 做法 |
|---|---|
| A 关注 B 吗? | ★一次关系调用★ |
| 这 50 个账号关注 B 吗? | 50 次关系调用 |
| ★谁关注了 B?★ | ★翻粉丝列表★ |
| B 的粉丝里谁也关注了 C? | 翻一次列表,再逐个查 |
交叉点比人们预期的低。 ★直接查 50 对已知账号,比翻一份大粉丝列表更划算★ —— 因为任何有真实规模的列表都是很多页,而每次检查只是一个小请求。
⚠️ 例外: 一旦你要的是身份而不是是/否,★再多次关系检查也替代不了列表★ —— 你没法靠猜来枚举。
两种形态各是一个调用 —— 选哪个取决于你在问哪个问题。
它不能告诉你什么
响应里带的字段不止关注状态,而其中一个需要一句警告。
★★拉黑和静音状态不是第三方可见的。★★ B 有没有拉黑 A,★是任何接口都无法就"两个不属于你的账号"告诉你的★ —— 那条边界是真实且普适的,和让"谁看过我的资料"不可能的是同一条。
如果你要知道的是有没有人拉黑了你,那是完全另一套方法 —— 退出登录做对比。
★把这个端点当作只回答两个问题:A 有没有关注 B,B 有没有关注 A。★ 再多的,就是没人能跨过的边界了。
大家会问的问题
怎么查一个人有没有关注另一个人? 一次关系调用,带上两个用户名。 ★两个都不必是你的账号。★
能查两个都不属于我的账号之间的关系吗? ★能。★ 公开账号之间的关注关系本来就是公开的。
需要翻粉丝列表吗? 只要是/否就不需要。 ★只有当你要发现身份时才翻列表。★
怎么找互关? 两个方向都为真。 要算一整批的话,对两份列表求交集,而不是逐对查。
关系是对称的吗? 不对称 —— 这正是全部要点。 ★A 关注 B,完全不说明 B 关注 A。★
方向为什么重要? 因为★"他们有联系吗"把四种不同状态塌缩成了一种★,而其中三种都不是提问者的本意。
怎么查很多对? 每对一个调用。 ★对一批固定的账号对,这比翻任何大列表都便宜。★
什么时候粉丝列表更好? 当你要的是"谁"而不是"是否"。 ★靠检查是枚举不出身份的。★
能查有没有人拉黑我吗? 用这个不行。 ★拉黑不是第三方可见的★ —— 真正的方法。
能查两个账号有没有互相拉黑吗? 不能。 对不属于你的账号,这不向任何人开放。
能查谁静音了谁吗? 不能。 ★静音在设计上就是隐形的★,任何方向都是。
对受保护账号有效吗? 只对公开关系有效。 ★受保护账号不暴露自己的关系。★
答案有多新? 调用那一刻的实时状态。 ★一分钟前发生的关注也会反映出来。★
能追踪关注关系什么时候变的吗? 只能反复查并存下结果。 ★不存在关注变化通知★ —— 追踪怎么做。
能知道这个关注是什么时候发生的吗? 不能。 ★关注关系不带时间戳★ —— 只有你自己拍的快照才能给它定日期。
如果用户名改了会怎样? 检查会失败,或者解析到错误的账号。 ★任何要存下来的东西都用 ID★ —— 为什么。
如果账号被封了呢? 关系无法解析。 ★要处理这个错误,而不是当成"没有关注"。★
"following"和"好友"是一回事吗? 不是。 ★X 没有需要双方同意的加好友★ —— 关注是单向的。
这算一次请求吗? 算,而且是很小的一次 —— 这正是它在定向检查上胜过翻页的原因。
能查某人有没有关注我吗? 能,把你的用户名作为 target。 ★不过对你自己的账号,粉丝列表能一次给你全部。★
怎么验证一个接口真的尊重了 source 参数? ★拿一对不对称的账号跑两个方向。★ 如果两次调用不成镜像,说明 source 被忽略了 —— 这是个值得尽早抓出来的 bug。
接口为什么会忽略 source? 因为底层平台调用往往是相对会话的。 ★不处理这一点的服务商,返回的是他们自己的关系,不是你要查的那个。★
能用它建一张关注图谱吗? 对一批已知账号可以 —— 把每一对都查一遍。★要做发现仍然需要列表。★
查多少对算太多? 当你要查的已经是一个大集合里的大部分配对时,翻列表再在本地求交会更便宜。
转发意味着关注吗? 不意味着。 ★任何人都能转发任何人★ —— 这是两个无关的信号。
能看历史上的关注关系吗? 只能看你自己记录过的。 ★没有历史会提供给你。★
这和粉丝数是一回事吗? 不是 —— 粉丝数是一个聚合值,这个是某一对具体账号。
能检测到软拉黑吗? 只能通过它的效果: ★一个悄无声息消失的关注★ —— 什么是软拉黑。
关注会影响我看到什么吗? 会,不过时间线里还混着推荐 —— 分发怎么运作。
能在一个调用里批量查关系吗? 不能在一个调用里 —— 但★每次都足够小,几百对用循环完全可行★。
简短版
★查 A 有没有关注 B,是一个很小的请求,而且对两个与你毫无关系的账号同样有效。★ 为了同一个问题去翻粉丝列表,是大多数代码默认走上的那条贵路。
把它建模成两个布尔值,不是一个。 ★存在四种状态,而"互关"只是其中之一。★
我们的 API 在一个调用里返回两个方向,拍平成朴素的布尔值。只读、固定按次价格 —— 而且★我们用不对称账号对做镜像测试,验证过这个方向确实是第三方对第三方★,这个检查值得对任何服务商都跑一遍。
它不会告诉你什么: 不属于你的账号之间的拉黑与静音。★那不是缺口 —— 那是让这个平台能正常用下去的边界。★
延伸阅读:读取一份完整的关注列表 · 长期追踪粉丝变化 · 查某人是否拉黑了你 · 为什么数字 ID 才是稳定的键。