手机归属地查询:电商风控,用归属地识别异常下单与区域分布
# 手机归属地查询:电商风控,用归属地识别异常下单与区域分布
> **接口**:手机归属地查询 `6-1` | **是否免费**:是(注册默认可免费调用,有使用档次限制)| **请求方式**:POST / GET | **返回格式**:JSON | **适用人群**:电商风控 / 运营 / 数据分析同学 | **阅读时间**:约 8 分钟
## TL;DR
- 把收货号码的归属地 `prov` / `provCode` 与用户填写的收货省份做一致性比对,不一致可作为异常下单的**辅助预警信号**。
- 按 `prov` 聚合订单,可以快速得到区域销量分布,辅助选品、仓配与投放决策。
- 这是**辅助风控信号,不是唯一判据**;接口免费、单参数、失败不扣点数,适合在用户侧脚本里循环调用(受免费档位限制)。
## Why:为什么电商要用归属地
电商场景下,手机号是贯穿注册、下单、收货的核心标识。一个常见风险是:**用户填写的收货地址省份,和它绑定手机号的归属省份对不上**。比如收货地址写"广东",但号码号段归属在"云南",这种错位本身不代表欺诈,但值得进入人工复核或二次验证的队列——它可能是误填、代付、地址被篡改,也可能是更值得警惕的信号。
另一个高频需求是**区域销量分布**。运营和供应链团队往往想知道"这个月来自哪些省的订单最多"。与其维护一套地址解析规则,不如直接用号码归属地的 `prov` 字段做聚合:号码归属是标准化的省名,聚合逻辑简单、稳定。
需要强调的是,归属地只是**众多风控维度里的一个**,它不能证明"这是坏人",也不能替代设备指纹、支付验证、历史行为等成熟手段。本文把它定位为"低成本、可叠加的辅助信号"。
## What:接入前准备与接口速览
前置条件:
1. 在 [易源官网](https://www.showapi.com) 注册账号(本接口为免费服务,注册默认可调用)。
2. 进入 [AppKey 管理页](https://www.showapi.com/console#/myApp) 复制 AppKey,后面代码替换 `YOUR_APPKEY`。
3. 准备好要查询的手机号(订单里的收货号码)。
| 项目 | 内容 |
|------|------|
| 接口名称 | 手机归属地查询 |
| 接入点 | `6-1`(本接口仅 1 个接入点,同步请求-响应) |
| 接口地址 | `https://route.showapi.com/6-1?appKey={your_appKey}` |
| 请求方式 | POST 或 GET |
| 必填参数 | `num`(手机号,字符串) |
| 返回格式 | JSON,业务数据在 `showapi_res_body` |
| 计费 | 免费服务(注册默认可免费调用,设使用档次限制);**失败不扣点数** |
| 更新频率 | 半年更新一次新号段归属地 |
| 注意 | **不支持携号转网查询** |
## How:从号码到风控信号
### 步骤 1 · 写一个归属地查询函数
下面代码封装了一个查询函数,带 10s 超时和 `ret_code` 判断。失败时返回 `None`,不扣点数,可放心在批量比对前调用。
```python
import requests
API_URL = "https://route.showapi.com/6-1"
APP_KEY = "YOUR_APPKEY" # 替换为你的真实 AppKey
def query_attribution(phone: str):
"""查询手机号归属地,失败返回 None(不扣点数)。"""
try:
resp = requests.get(
API_URL,
params={"appKey": APP_KEY, "num": phone},
timeout=10,
)
data = resp.json()
body = data.get("showapi_res_body", {})
if body.get("ret_code") == 0:
return body
# ret_code != 0 表示失败(如 -2 非11位、-4 格式错),不扣点数
return None
except Exception:
return None
```
### 步骤 2 · 归属地与收货地址一致性比对
用返回的 `prov`(省名)或 `provCode`(省别编码)和订单里用户填写的收货省份做比对。不一致则打标为"待复核"。
```python
def check_address_consistency(phone: str, ship_prov: str) -> dict:
"""比对号码归属省份与收货填写省份。"""
body = query_attribution(phone)
if body is None:
return {"match": None, "reason": "归属地查询失败/号码格式错"}
phone_prov = body.get("prov") # 如 "云南"
phone_prov_code = body.get("provCode") # 如 "530000"
# 简单比对省名;生产环境建议同时用 provCode 做规范化比对
matched = (phone_prov == ship_prov)
return {
"phone_prov": phone_prov,
"phone_prov_code": phone_prov_code,
"ship_prov": ship_prov,
"match": matched,
"flag": "OK" if matched else "REVIEW", # 不一致进入复核队列
}
# 示例:收货地址填广东,但号码归属云南
print(check_address_consistency("18908711111", "广东"))
# -> {'phone_prov': '云南', 'ship_prov': '广东', 'match': False, 'flag': 'REVIEW'}
```
### 步骤 3 · 按省份聚合区域销量分布
拿到一批订单的收货号码后,按 `prov` 聚合即可统计区域分布。
```python
from collections import Counter
def region_distribution(orders: list) -> dict:
"""orders: [{ 'order_id':..., 'phone':... }];返回各省订单数。"""
counter = Counter()
for o in orders:
body = query_attribution(o["phone"])
if body is None:
counter["UNKNOWN"] += 1
continue
counter[body.get("prov", "UNKNOWN")] += 1
return dict(counter.most_common())
orders = [
{"order_id": "A1", "phone": "18908711111"}, # 云南
{"order_id": "A2", "phone": "18908711111"}, # 云南
]
print(region_distribution(orders))
# -> {'云南': 2}
```
> 注意:上面的聚合是"用户侧脚本循环调用"的方式。免费档位有使用上限,订单量大时请先做客户端格式校验(11 位、纯数字),再考虑本地缓存手机号→归属地(详见系列缓存篇),避免浪费免费额度。
## 返回示例与解析
```json
{
"showapi_res_code": 0,
"showapi_res_error": "",
"showapi_res_id": "ce135f6739294c63be0c021b76b6fbff",
"showapi_res_body": {
"num": 1890871,
"prov": "云南",
"ret_code": 0,
"areaCode": "0871",
"name": "电信",
"cityCode": "530100",
"postCode": "650000",
"provCode": "530000",
"type": 2,
"city": "昆明"
}
}
```
| 字段 | 含义 | 风控用途 |
|------|------|----------|
| `prov` | 省 | 与收货省份比对、区域聚合 |
| `provCode` | 省别编码(本省身份证前几位) | 规范化省比对,减少"云南/云南省"写法差异 |
| `city` | 市 | 更细粒度区域分布 |
| `type` | 运营商枚举(1移动/2电信/3联通/4广电/-1未知) | 识别异常运营商组合(详见 type 篇) |
| `num` | 号段(前 7 位) | 非完整号码,仅作号段参考 |
| `ret_code` | 0 成功,其他失败 | 失败不扣点数 |
> 字段完整含义见 [《手机归属地返回字段全解》](https://www.showapi.com/guides/phone-attribution-response-fields-6)。
## 进阶 / 边界
- **辅助信号,不是判据**:归属地不一致只应进入"复核/二次验证"流程,不应直接拦截订单或封号。请配合支付验证、设备指纹等共同决策。
- **不支持携号转网**:号码携转后归属仍按原号段,可能和用户当前常住地不同,比对时要允许一定误报率。
- **失败不扣点数**:`ret_code != 0` 时不消耗额度,可放心在循环里对每个号码做格式前置校验。
- **免费档位限制**:循环调用受免费档位上限约束,量大时务必做客户端校验 + 本地缓存。
- **无经纬度**:接口不返回坐标,区域热力图需要外部地理编码。
## FAQ
**Q1:归属地不一致就一定是欺诈吗?**
不是。它只是异常信号之一,可能是误填、代付或携号转网导致。应作为"建议复核"而非"直接拦截"的依据。
**Q2:用 `prov` 还是 `provCode` 比对更好?**
建议优先用 `provCode`(数字编码)做规范化比对,避免"云南"与"云南省"等文字写法差异导致误判;`prov` 适合给人看。
**Q3:上千条订单循环调用会超免费额度吗?**
会。免费服务有使用档次限制。请先做 11 位/纯数字客户端校验,再对通过校验的号码查询,并缓存"号码→归属地"结果复用。
**Q4:`type` 字段在风控里怎么用?**
`type` 标识运营商(1移动/2电信/3联通/4广电/-1未知)。它本身不是风险指标,但异常组合(如大量 `-1` 未知)可进入观察队列。详见 [《type 字段全解》](https://www.showapi.com/guides/phone-attribution-type-field-6)。
**Q5:能直接查一个号段的全部归属地做批量吗?**
ShowAPI 没有官方批量查询能力。批量只能由你在用户侧脚本里循环单条调用,且受免费档位限制。
**Q6:返回的 `num` 是完整手机号吗?能用来匹配订单吗?**
不是。`num` 是号段(前 7 位),不是完整号码,不能用它做订单与用户的精确匹配,请用你系统里的完整号码。
## 相关能力 / 下一步阅读
- [《手机归属地返回字段全解:prov/city/type/postCode 一文读懂》](https://www.showapi.com/guides/phone-attribution-response-fields-6) —— 每个字段类型、取值与坑位
- [《type 字段全解:1移动/2电信/3联通/4广电/-1未知怎么用》](https://www.showapi.com/guides/phone-attribution-type-field-6) —— 运营商枚举的实战用法
- [《物流/外卖场景:收货号码归属地校验与派送分区》](https://www.showapi.com/guides/phone-attribution-logistics-6) —— 把归属地用于派送分区与时效
- **本系列共 12 篇**:查看[手机归属地查询 · 官方指南总目录](https://www.showapi.com/guides/phone-attribution-guides-6)