让 IP 纯净度的每一分都能被读者自己验算:四条判据、四套权重,五个分数手算全对
同类工具给一个不解释的数字。这篇把评分过程整个摊开:一个 IP 的四条判据、总分怎么来的、四个使用场景怎么从同一批判据得出 58 到 91 的不同结论——全部用线上接口返回的真实数据手算复现,一分不差。
查 IP 纯净度的工具都会给你一个分数。79 分。 然后呢?
79 是因为它在机房网段,还是因为它上过黑名单?这两件事对「拿它跑采集」的影响完全不同,但分数一样。你没法从一个数字里把它们分开,也就没法判断该不该信这个结论。
这篇把评分过程摊开,并且——全部数字你可以自己拿计算器验算。
一、先把真实数据拿出来#
curl -s "https://ipure.art/api/lookup?ip=8.8.8.8"评分部分:
"risk": {
"purity": 78,
"riskPoints": 22,
"level": "neutral",
"label": "一般",
"verdict": "基本可用,严格风控的平台可能偶尔弹验证码",
"confidence": "medium",
"factors": [
{ "id": "usage", "category": "hosting", "points": 25,
"label": "IP 类型:机房 / 数据中心",
"detail": "机房 / 数据中心 IP 是自动化流量的主要来源" },
{ "id": "exposure:ports", "category": "exposure", "points": 6,
"label": "对外开放 2 个端口",
"detail": "开放端口:53、443" },
{ "id": "native", "category": "geo", "points": -5,
"label": "原生 IP",
"detail": "注册地与使用地一致,由当地运营商直接分配" },
{ "id": "sharing", "category": "sharing", "points": -4,
"label": "独享或低共享出口",
"detail": "同网段仅观测到 6 台设备" }
]
}现在验算:
25 + 6 + (-5) + (-4) = 22 ← riskPoints
100 - 22 = 78 ← purity对上了。四条判据,加起来就是分数,没有隐藏项。
报告页把同一件事画出来:

左侧扣分、右侧加分,共用同一比例尺
二、为什么是累加风险点,不是加权平均#
/**
* 内部模型是「从 0 分起累加风险点」而非加权平均:一个 IP 只要命中 Tor 出口,
* 无论其它指标多干净都必须是高风险;平均模型会把这种决定性证据稀释掉。
*
* 对外输出的是纯净度(100 - 风险点数,越高越干净)。两套尺度并存是有意的:
* 累加风险点让 floor(一票否决)有清晰语义,而纯净度才符合用户直觉。
*/这是个真实的建模选择。加权平均的问题:一个 Tor 出口如果地理清晰、无黑名单记录、注册信息完整,平均下来可能还是个中等分——而它是 Tor 出口这一件事,本身就该一票否决。
累加模型里「一票否决」有清晰语义(给一个足够大的点数,或者设 floor),平均模型里没有。
但对外必须换成纯净度,因为「78 分」符合直觉,「22 风险点」不符合。两套尺度并存不是冗余,是内部建模和对外表达各取所需。
三、结论只说后果,不说原因#
分档表里有条注释值得单独拎出来:
/**
* verdict 一律只描述**后果**,不说原因。同样是 47 分,可能是纯粹因为
* 落在机房网段,也可能是因为上了黑名单 —— 按档位写死的结论没法区分,
* 说"多个数据源判定异常"就会在前一种情况下变成臆断。
* 原因由 factors 逐条列出,那里才是准确的。
*/8.8.8.8 的 verdict 是「基本可用,严格风控的平台可能偶尔弹验证码」——它描述的是你会遇到什么,没有说「因为它是机房 IP」。
原因在 factors 里,逐条带着分值和来源。按档位生成的解释一定会在某些情况下说谎,因为同一个分数可以由完全不同的判据组合得出。
四、同一批判据,四个场景,58 到 91#
这是我认为最有用的一部分。
/**
* 关键在于**不同用途对 IP 的要求差别很大**,把总分复制几份贴上不同标签
* 是没有信息量的。举两个相反的例子:
*
* - ChatGPT 对机房和代理出口的容忍度极低,却完全不关心这个 IP
* 有没有上过垃圾邮件黑名单。
* - 发信恰恰相反:机房 IP 发信本来就正常,而 Spamhaus 收录是生死线。
*
* 所以每个场景对判据类别各有一套权重,同一份情报能得出真正不同的结论。
*/四套权重(1 = 与总分一致,0 = 完全不关心,未列出的按 1 处理):
| 场景 | 权重 |
|---|---|
| AI 服务 | proxy:2 hosting:1.8 vendor:1.5 sharing:1.2 abuse:0.8 geo:0.8 blocklist:0.2 |
| 流媒体 | geo:2 proxy:1.8 sharing:1.6 hosting:1.4 vendor:1.2 exposure:0.8 blocklist:0.2 |
| 跨境电商 | sharing:2 hosting:1.6 proxy:1.5 abuse:1.3 vendor:1.2 blocklist:0.5 |
| 邮件发送 | blocklist:2.5 abuse:1.8 rdns:1.5 vendor:0.8 exposure:0.8 hosting:0.4 geo:0.3 |
注意 blocklist 这一列:AI 场景 0.2,邮件场景 2.5,差 12.5 倍。这不是调参调出来的,是这两件事本来就不相干——ChatGPT 不查 Spamhaus。
验算#
拿上面那四条判据,套四套权重,和接口返回的分数对账:
FACTORS = [ # (id, category, points)
("usage", "hosting", 25),
("exposure:ports", "exposure", 6),
("native", "geo", -5),
("sharing", "sharing", -4),
]
for sid, w in WEIGHTS.items():
s = sum(p * w.get(cat, 1) for _, cat, p in FACTORS)
mine = round(100 - s)总分:风险点 25 + 6 + -5 + -4 = 22,纯净度 = 100 - 22 = 78
接口返回 purity=78 riskPoints=22 → 一致
场景 加权明细 手算 接口
----------------------------------------------------------------------------
ai 25×1.8 + 6×1 + -5×0.8 + -4×1.2 58 58 一致
streaming 25×1.4 + 6×0.8 + -5×2 + -4×1.6 77 77 一致
ecommerce 25×1.6 + 6×1 + -5×1 + -4×2 67 67 一致
email 25×0.4 + 6×0.8 + -5×0.3 + -4×1 91 91 一致五个数字,全部手算复现。
页面上就是这四张卡片:

右上角那句「各场景对 IP 的要求不同,用的是各自的判断权重,不是同一个总分」,就是这套设计的一句话说明
同一个 8.8.8.8:拿去发信 91 分(机房 IP 发信本来就正常,八个黑名单全干净),拿去上 ChatGPT 58 分(机房属性被放大 1.8 倍)。
一个「78 分」的总分完全掩盖了这 33 分的差距。
五、置信度不是装饰#
"confidence": "medium",
"sources": [
{ "name": "rdap", "kind": "free", "ok": true, "ms": 232 },
{ "name": "team-cymru", "kind": "free", "ok": true, "ms": 93 },
{ "name": "ipqualityscore", "kind": "commercial", "ok": false, "ms": 0,
"skipped": "未配置 IPQS_API_KEY" },
{ "name": "abuseipdb", "kind": "commercial", "ok": false, "ms": 0,
"skipped": "未配置 ABUSEIPDB_API_KEY" }
]medium 不是随便标的:两个商业源缺席,而它们正是代理识别的主力。少了它们,「这个 IP 是不是住宅代理」这个问题没有被真正回答过,只是没有证据说是。
报告如实标 medium,而不是用一个漂亮的 high 蒙混过去。
还有两个更细的一致性字段#
"countryAgreement": { "agreed": 3, "total": 3 },
"flagAgreement": { "isHosting": 1 }- 国家判定:三个源都说 US,三比零
- 机房判定:只有一个源说过
这两条摆在一起才有意义。8.8.8.8 是机房 IP 这件事人尽皆知,但从数据上看,这个判定背后只站着一家。而它恰恰是分值最高的那条判据(+25,占全部风险点的 100% 以上)。
六、一票否决要写死,不要靠分数#
有些结论不该由纯净度决定:
/**
* ChatGPT / Claude / Gemini 三家均不提供服务的地区(ISO 3166-1 alpha-2)。
*
* 命中这些地区的 IP,**无论纯净度多高都上不了境外 AI**:中国大陆更是 GFW
* 直连不通 + 地区不受支持双重封死。所以「AI 服务」结论要直接判为「地区受限」,
* 而不是按纯净度给「适合 / 不可用」—— 一个 100 分的北京住宅 IP 也用不了 ChatGPT。
*
* 用 denylist 而非 allowlist:不在此列表的地区照常走纯净度评分,避免把合法的
* 境外代理出口(美/日/新 等)误判为受限。
*/两个设计点:
① 有些约束是硬的,不该被分数表达。 一个北京的住宅 IP 可以拿满分——它确实是干净的原生住宅 IP。但它上不了 ChatGPT,这跟干不干净没关系。硬塞进评分只会让分数失去意义。
② 用 denylist 而不是 allowlist。 allowlist 会把所有没列进去的地区判为受限——包括那些完全正常的美/日/新出口。默认放行、明确列出例外,错误方向朝着「漏判」而不是「误判」,对这个场景是对的。
七、这套东西能抄走什么#
不限于 IP 评分。任何要给出「一个数字 + 一个结论」的系统都适用:
- 累加而不是平均,如果你的领域里存在决定性证据(一票否决)
- 内部尺度和对外尺度可以不同——建模方便和用户直觉往往不是一回事
- 聚合层只说后果,解释留给明细层,否则解释一定会在某些区间里说谎
- 每条判据带上分值和来源,让人能自己加一遍
- 同一批证据、不同权重,得出真正不同的场景结论,而不是把总分复制几份贴标签
- 置信度要有据可依,缺数据就降置信度,别装满
- 记录「几个源确认」,单源和多源的证据强度差得远
- 硬约束写死,别塞进评分
第 4 条是全部的基础。能被读者自己验算的分数,和一个不解释的数字,是两种不同的东西——哪怕它们碰巧相等。
附:自己验一遍#
curl -s "https://ipure.art/api/lookup?ip=8.8.8.8" | python3 -c "
import json, sys
d = json.load(sys.stdin)['risk']
print('factors:', [(f['id'], f['points']) for f in d['factors']])
print('sum =', sum(f['points'] for f in d['factors']))
print('report =', d['riskPoints'], '/ purity', d['purity'])
"数据源的返回随时在变,你跑出来的判据可能和本文不同。要验的不是这几个数字,是「加起来等不等于报告里的那个数」。
参考#
- ISO 3166-1 alpha-2 国家代码
- Spamhaus ZEN(邮件场景权重 2.5 的那个)
- OpenAI:支持的国家与地区
- Anthropic:支持的国家与地区
- Wikipedia:可解释性(机器学习)(第三节那条「聚合层不解释」的一般化说法)