今天也遇到并解决了这个问题,事后才发现已有很多讨论,但背后的原理还有待细说。遇到这个问题的时候最先想到的就是抓包分析一下,因为知乎既然支持实时的自动补全,一定有网络请求。
我们在知乎编辑器页面打开浏览器的开发者工具-网络菜单,清空一下当前流量,然后在编辑器里输入 @清川,就可以发现网页发送了以下请求数据:
我们可以看到知乎使用的自动补全接口如下,其中%E6%B8%85%E5%B7%9D是「清川 」的URL编码。
https://www.zhihu.com/people/autocomplete?token=%E6%B8%85%E5%B7%9D&max_matches=10&use_similar=0
可以看到后面的max_matches字段赫然写着10,这意味着每次查询只返回10条结果。而这个请求是写死在知乎编辑器的JS逻辑里的,也就是说,就算前端UI不控制只显示6条,你也是看不到其他人的。这里知乎其实是为了做通信量的优化,但少了一个功能性逻辑,就是在@后面自动补全的列表滚到底端时再发送一条新的请求,请求10条以后的结果。但是这样呢,后台就要对每次请求维护一个分页表,又增加了性能成本,于是索性不管了。
由于上面的数据包是一个GET请求,我们把链接直接放到浏览器地址栏,并把最大匹配数量改到100000试试。一般来说不会有这么多重名,就可以看到所有结果了。然而这时看到的却是:
[["entry"]]
说明后台设置了上限值,不能这么大。我们二分查找一点点尝试,最后试出来最大数量是500。也就是说这里也有潜在的BUG,当重名数量达到500时,还是会有用户被检索不到。
我们把结果丢进JSON在线格式化网站中整理一下:
可以清楚地发现每个用户的信息列表里第2个位置处都有一个唯一的字符串,这个其实是用户的标识,暂且称其为用户字符串,可以在用户主页的网址中找到。
并不是用户的唯一标识就一定能用于搜索,很多答主的逻辑不对,爬过知乎的会很清楚知乎用户是有id的,但不是这个字符串,比如我的「fe459e428af0b5f8abb53f2f4f3c732c」。
这里取决于后台实现逻辑,可以从返回值字段入手进行猜测。后来实验结果验证了其实@后面可以搜索的内容很多:
可搜索字段名 | 字段值举例 |
---|---|
用户名 | 清川 |
用户字符串 | liu-ji-27-94 |
一句话介绍 | 科研小学生,关注模型压缩与联邦学习 |
个人简介 | 一部关于人生的游记:人间纪行(https://liushangyu.xyz) |
你甚至在搜索的时候同时输入多个字段值,将不同的字段值用空格隔开。这就让人可以猜想出后台的搜索逻辑:将可搜索字段信息连接成字符串,对每个查询字段值取最大匹配的交集。而搜索结果的顺序嘛,就是知乎首页搜索栏搜索出来的顺序,按照最近的创作活跃度排序,这里也很有可能是复用了搜索接口。
结论:当用户不在提示列表里,或者重名且头像相同(默认头像)时,我们就可以使用用户字符串去搜索ta。用户字符串可以在个人主页地址获取。