仁者见仁,智者见智。——《易经・系辞上》

一个用户名,四百多个站点:Sherlock 做的是最窄的一件事

先说一个形状

「一个用户名」是什么形状?

它不是一串随机字符。人在不同平台注册时,往往会保留同样的手感:同样的词、同样的下划线、同样的数字后缀、同样的缩写。哪怕换了三个平台,那个 ID 还是那个 ID。

所以当你想知道一个人在网上留下过多少痕迹时,最省力的入口不是搜索,是把这个名字拿到每个站点去试一遍。

Sherlock 就做这一件事。它的描述只有一行:按用户名在社交网络中找出对应的账号。

项目地址:https://github.com/sherlock-project/sherlock

文档的副标题写得更具体:在 400 多个社交网络里追查社交账号。

四百多个。这个数字意味着四百多套规矩:每个站点有自己的命名方式、自己的返回行为,而且这些规矩会变。

所以它本质上是一份需要持续维护的站点清单,外面套了一个命令行。

用法:一行命令,一堆 txt

最简单的用法就一行:

1
sherlock user123

想一次查多个,就把名字并排写:

1
sherlock user1 user2 user3

结果不会打印成一个漂亮的仪表盘,而是写进一个个纯文本文件——每个用户名一个,文件名就是用户名,比如 user123.txt。

这个选择很有意思。它没有做数据库,没有做 Web UI,没有做账号体系。它输出的是最容易后续处理的格式:一个文件,一行一个地址。

在侦察类工具里,这是最合适的形态。因为你接下来的动作通常是 grep、sort、丢进表格、或者和别人手上的清单做差集。txt 在这件事上比任何花哨的界面都好用。

那些参数,其实是一份「我知道我会错」的清单

--help 里有二十来个选项,逐条读下来会发现,它们几乎全在解决同一个问题:结果不可靠。

这是我认为这个工具最诚实的地方。

--dump-response 把 HTTP 响应打到标准输出,用于「定向调试」。

翻译过来:当某个站点明明有这个人,工具却说没有,你可以把原始响应挖出来看看——是页面改版了,是被反爬拦了,还是状态码变了。

一个只给你结论的工具,你没法反驳它。一个能把证据摊开的工具,你可以。

--ignore-exclusions 忽略上游的排除规则,而说明里直接写着:可能返回更多误报。

这句话值得单独拎出来。多数工具会把它写成「强制模式」或「深度模式」,听起来很厉害。而这里说的是:这么干会更不准。它没有把「更用力」包装成「更好」。

--timeout 默认 60 秒。对四百多个站点的批量请求来说,这个默认值偏保守,保守的意思是——宁可慢,也别把「超时」误判成「不存在」。

--nsfw 把默认列表里的成人站点也纳入检查,默认是不查的。

--local 强制使用本地的 data.json,而不是上游那份。

--json 更有意思:可以从一个 JSON 文件加载数据,也可以从一个在线的、合法的 JSON 加载;还接受上游的 PR 编号。

最后这半句透露了它的维护方式。站点清单的更新是通过 PR 走的,而这个参数让你能直接拿一个还没合并的 PR 的数据来跑——等于把「贡献流程」变成了一次可以本地验证的实验。

那个 {?},是全篇最巧的一处

帮助里有一句很容易读漏的话:可以用 {?} 检查相似的用户名,它会被替换成 _、-、.。

也就是说,你写 user{?}123,工具会去试 user_123、user-123、user.123。

这个小设计的价值,在于它承认了一个人类习惯:同一个人在不同站点,常常只能改一个分隔符。

因为平台对用户名的字符限制不一样。有的不许下划线,有的不许点,有的干脆只留字母数字。于是「同一个名字」在实际注册时会分叉成几种写法,而这几种写法之间的差异,往往就是那一个符号。

一个字符的占位符,把「找一个人的多个马甲」这件事,从手工枚举变成了三次自动请求。

这种功能不会出现在宣传语里,但它是那种用过一次就再也离不开的东西。

那份名单本身,才是这个项目的主体

四百多个站点听起来像个数字,实际上是一份资产。

它依赖一份随仓库一起走的数据文件,而 --local 可以强制只用本地那一份——不许从上游拉。

这件事的意义在于:代码几乎不变,名单一直在变。

一个站点改了路径、换了状态码、加了人机验证,那条记录就得跟着改。所以这个项目真正的日常维护,不是写功能,是跟着四百多个网站一起漂移。

而 --ignore-exclusions 的存在,说明这份名单里还带着另一层信息:哪些站点被标记为「不建议查」。

这个标记通常来自两类原因——误报率太高,或者站点本身有争议。工具默认尊重它,而把「我不听」做成一个需要你主动输入的参数,并在说明里写清后果。

默认保守,破例要付费——这个模式在工具设计里很值得学。它把风险放在了需要显式选择的那一侧。

只查几个站点,或者走一条代理

真实使用里,你很少需要四百个。

更多时候是三种场景:验证一个已经确定的方向、绕过网络限制、把结果交给下游程序。

--site 让你把范围收窄到指定的站点,可以重复给多个。查询量从四百降到三,速度快得不像同一个工具。

--proxy 让所有请求走一条代理,比如一条本地 socks5。这不是「高级功能」,而是某些网络环境里的必需品。

--no-color 关掉终端颜色。听起来是个审美选项,其实是给管道用的——带 ANSI 转义符的输出,塞进日志或者被另一个程序解析时会变成垃圾。

--browse 则是反方向:把所有结果在默认浏览器里一次性打开。

一个参数表能同时容下「给机器读」和「给人看」两种诉求,说明它真的被用过很多次。

安装:三条路,和一条警告

文档给的安装方式很干脆,一张三行的表:

方式 备注
pipx install sherlock-project 可以用 pip 或 uv 代替 pipx
docker run -it --rm sherlock/sherlock
dnf install sherlock-project

三条路,覆盖三种人:想要隔离环境的、不想装 Python 的、用 Fedora 系发行版的。

然后是一条加粗的警告:ParrotOS 和 Ubuntu 24.04 的包由第三方维护,看起来是坏的,这两个系统的用户应该改用 uv、pipx、pip 或者 Docker。

「看起来是坏的」这个措辞很微妙。它没说「已经坏了很多天」,也没说「我们正在修」——它说的是我们在观察,观察到的是坏的,所以你别走这条路。

这种表述比「暂不支持」有用得多,因为它给了你替代方案。

再往下还有一句:Debian 13 以上、Ubuntu 22.10 以上、Homebrew、Kali、BlackArch 有社区维护的包,但这些包不由项目直接支持或维护。

一句话划清了责任边界。开源项目最容易被这种事拖垮:用户装了第三方包,坏了,来提 issue,维护者既没有权限也没有义务去修。

提前说清楚,是省下未来一百条无效工单的唯一办法。

输出:四种格式,四种下游

它能写 txt、CSV、xlsx,也能按 JSON 加载数据。

这四个选择对应四种人。txt 给脚本;CSV 给表格;xlsx 给要发邮件的场景;JSON 给要喂给另一个程序的人。

而 --print-all 和 --print-found 又是一组对照:一个输出没找到的站点,一个只输出找到的。

为什么要看「没找到」的?因为缺失也是信息。如果某个人在四百个站点里只出现了三个,那三个就是重点;而如果你在排查的是一个站点为什么误报,你需要看的是全部。

工具把这两个视角都留了出来,而且默认不啰嗦。

它证明什么,不证明什么

这一点值得说清楚,因为这类工具最常被误用。

Sherlock 检查的是:某个用户名在某个站点上,是否对应一个存在的页面。

它不检查:那个页面背后是不是你想找的那个人。

同名的人满地都是。一个叫 alex 的用户名,会在几十个站点上「命中」,而那可能是几十个不同的人。工具给你的是候选集,不是身份确认。

这个区别在文档里没有明说,但从它的参数设计能读出来:它给了超时、给了原始响应、给了排除规则、给了「可能更多误报」的警告——所有的谨慎都花在「这次请求可不可信」上,而没有花在「这个人是不是他」上。

因为后者不是它能回答的问题。

一个侦察工具的成熟,往往体现在它不假装自己能下结论。

一个小工具的长跑

帮助信息里写着版本号:0.16.0。

对一个主打「一次性查询」的工具来说,这个版本号说明它已经改了很多轮。而它的许可是 MIT,创建者写在最下面,一个名字,一个链接,没有任何头衔。

这类工具的生命周期通常很朴素:先有人自己要用,写了个脚本;后来站点越来越多,脚本变成了项目;再后来有人提 PR 补站点,项目变成了社区的事。

四百多个站点,靠一个人是维护不过来的。所以它把名单开放成数据,把更新开放成 PR,把验证开放成参数——--json 能直接吃一个上游 PR 号,等于让每个用户都能在合并之前先试一遍。

一个开源项目的可持续性,往往就藏在这种「让别人能替你验证」的设计里。

最后

四百多个站点,一个用户名,一堆 txt。

这个工具做的事情窄到可以一句话说完,但它在窄的地方做了很多正确的小决定:把原始响应给你看、把误报的可能写在参数说明里、把第三方包的责任划清、把「同一个人换了个分隔符」这种人类习惯做成一个占位符。

它的价值不在于「能查到」,而在于查完之后你知道该怀疑什么。

在这个行当里,后者比前者稀缺得多。