技术博客
手机归属地查询:Excel 批量查归属地,用脚本替代手工逐条查

手机归属地查询:Excel 批量查归属地,用脚本替代手工逐条查

作者: 万维易源
2026-08-27
手机归属地查询Excel批量脚本循环号段查询
# 手机归属地查询:Excel 批量查归属地,用脚本替代手工逐条查 > **接口**:手机归属地查询 `6-1` | **是否免费**:是(注册默认可免费调用,有使用档次限制)| **请求方式**:POST / GET | **返回格式**:JSON | **适用人群**:运营/数据/客服人员、需要批量处理通讯录的开发者 | **阅读时间**:约 10 分钟 ## TL;DR - ShowAPI **没有官方批量接口**,本文用脚本循环读取 Excel 里的手机号、逐条调用 `6-1`、再把结果写回——本质是"用户侧脚本循环调用"。 - 循环调用**受免费档位限制**:号码量大时要先评估档位,或先做本地去重/缓存,避免一遍跑光额度。 - 对 `type=-1` 或 `ret_code` 负数的记录**跳过并记入失败清单**,不要硬写空值覆盖原表。 ## Why:手工逐条查太慢了 市场部有一张几万行的客户手机号表,想加一列"归属地""运营商"做区域分析;客服有一份通讯录要补"省市"标签。如果打开网页一个个粘贴查询,几百条就够喝一壶,上万条根本不现实。 但必须说清楚一件事:**ShowAPI 的手机归属地查询没有官方批量/异步能力,也无订阅推送、无回调**。所谓"批量",只能是你在自己机器上写个脚本,**循环读取 Excel 每行手机号,逐条调一次接口,再把结果写回**。这是用户侧行为,不是平台能力。 好消息是接口**只要求一个必填参数 `num`**、且**失败时(ret_code!=0)不扣点数**,所以写脚本成本很低;坏消息是循环调用会累积成功请求数,受免费档位上限约束,大批量前务必规划速率与缓存。 ## What:前置条件与接口速览 | 项目 | 内容 | |------|------| | 接口名称 | 手机归属地查询 | | 接入点 | `6-1`(本接口仅 1 个接入点,同步请求-响应) | | 接口地址 | `https://route.showapi.com/6-1?appKey={your_appKey}` | | 请求方式 | POST 或 GET | | 鉴权方式 | Query 参数 `appKey` | | 必填参数 | `num`(手机号,字符串,示例 `18908711111`) | | 返回格式 | JSON,业务数据在 `showapi_res_body` 内 | | 写回字段 | `prov`/`city`/`name`/`type`/`areaCode` | | 运营商枚举 `type` | 1=移动、2=电信、3=联通、4=广电、-1=未知 | | 计费 | 免费服务(有使用档次限制);**失败不扣点数** | | 批量方式 | **无官方批量**,仅用户侧脚本循环调用 | | 注意 | **不支持携号转网查询** | **关键认知:** 本文所有"批量"都是你的脚本在循环发单条请求,平台侧仍是逐条同步响应。设计脚本时一定要加速率控制、失败重试与缓存,否则既打满档位又容易超时。 ## How:读取 Excel → 循环调用 → 写回 ### 步骤 1 · 准备环境与输入表 假设 `phones.xlsx` 里有一列名为 `phone` 的手机号(A 列也可以,按列名读取更稳)。需要安装:`pip install openpyxl pandas requests`。 ### 步骤 2 · 循环调用并写回(Python) 下面脚本读取 `phone` 列,逐条查询,把 `prov`/`city`/`name`/`type` 写回新列,并对失败/未知记录单独记录: ```python import time import requests import pandas as pd API_URL = "https://route.showapi.com/6-1" APP_KEY = "YOUR_APPKEY" # 替换为你的真实 AppKey TYPE_NAME = {1: "移动", 2: "电信", 3: "联通", 4: "广电", -1: "未知"} def lookup(phone: str): """返回 (ok, row)。ok=False 时 row 为失败原因。""" if not str(phone).strip().isdigit() or len(str(phone).strip()) != 11: return False, "格式非11位纯数字" try: resp = requests.get( API_URL, params={"appKey": APP_KEY, "num": phone}, timeout=10, ) body = resp.json().get("showapi_res_body", {}) except requests.RequestException: return False, "网络/超时" if body.get("ret_code") != 0: # 失败不扣点数,但本行不写归属地 return False, f"ret_code={body.get('ret_code')}" if body.get("type") == -1: # 查到但运营商未知,记一笔,不写错值 return False, "type=-1 未知运营商" return True, { "prov": body.get("prov"), "city": body.get("city"), "name": body.get("name"), "type": body.get("type"), "typeName": TYPE_NAME.get(body.get("type"), "未知"), "areaCode": body.get("areaCode"), } def main(): df = pd.read_excel("phones.xlsx") results, failures = [], [] for idx, row in df.iterrows(): phone = str(row.get("phone", "")).strip() ok, res = lookup(phone) if ok: results.append(res) else: failures.append({"phone": phone, "reason": res}) results.append({}) # 占位,保持与 df 行数对齐 # 速率控制:每条约 0.2s,避免过快;按你的档位调整 time.sleep(0.2) # 简单进度 if (idx + 1) % 100 == 0: print(f"已处理 {idx + 1} 行") res_df = pd.DataFrame(results) out = pd.concat([df, res_df], axis=1) out.to_excel("phones_with_attr.xlsx", index=False) if failures: pd.DataFrame(failures).to_excel("phones_failed.xlsx", index=False) print(f"完成,{len(failures)} 行失败,已写入 phones_failed.xlsx") if __name__ == "__main__": main() ``` ### 步骤 3 · 加失败重试(指数退避) 网络抖动时不应直接判失败。把 `lookup` 包一层重试: ```python def lookup_with_retry(phone: str, max_retry: int = 3): delay = 1.0 for attempt in range(max_retry): ok, res = lookup(phone) if ok or "网络/超时" not in res: # 业务失败(非网络)不重试 return ok, res time.sleep(delay) # 指数退避 delay *= 2 return ok, res ``` 把步骤 2 主循环里的 `lookup(phone)` 换成 `lookup_with_retry(phone)` 即可。 ## 返回示例与解析 ```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` | 省 | 区域分布分析 | | `city` | 市 | 区域分布分析 | | `name` | 运营商名称 | 运营商占比统计 | | `type` | 运营商枚举(1移动/2电信/3联通/4广电/-1未知) | 运营商分流依据 | | `areaCode` | 城市区号 | 辅助核对地域 | | `ret_code` | 业务状态码,0 成功,其他失败 | 失败则跳过不写 | > 字段的完整含义、类型与取值见 [《手机归属地返回字段全解》](https://www.showapi.com/guides/phone-attribution-response-fields-6)。 ## 进阶 / 边界 - **无官方批量**:本文所有"批量"都是脚本循环单条请求,平台侧仍是逐条同步响应,不存在批量入口或异步队列。 - **免费档位限制**:循环调用会累积成功请求数,受档位上限约束。**大批量前先评估档位**;可对号码去重、对近期查过的号码做本地缓存,减少重复调用(详见系列缓存篇)。 - **失败不扣点数**:`ret_code != 0` 不消耗额度,但成功调用会计入档位,别把"重试"当成零成本无限打。 - **type=-1 / ret_code 负数 → 跳过并记录**:这类行不写归属地、不写错值,单独落到失败表,方便后续人工核对或二次处理。 - **不支持携号转网**:返回的归属地/运营商按原号段,无法反映携转后实际状态。 - **无经纬度**:只返回省/市文字,无法直接在地图打点。 ## FAQ **Q1:ShowAPI 有官方的批量查询接口吗?** 没有。本接口仅 1 个同步接入点 `6-1`,无订阅推送、无回调、无官方批量能力。所谓批量只能是你用脚本循环调用单条接口实现,属于用户侧行为。 **Q2:几万条号码一次性跑,会不会把免费档位用光?** 会。循环调用每成功一次都计入档位。建议先对号码去重、做本地缓存,并评估你的档位上限;必要时分批分天跑。 **Q3:某些号码查询失败(ret_code 负数)会扣费吗?** 不会。文档明确"失败时(ret_code!=0)不扣点数"。但成功调用会计入档位,重试也要算成功次数。 **Q4:type=-1(未知运营商)的记录怎么处理?** 建议跳过写归属地、单独记到失败/待核对表,不要硬写空值或错误运营商,避免污染分析结果。 **Q5:接口能批量返回经纬度做地图分布吗?** 不能。本接口不返回经纬度,只给省/市文字归属地;地图可视化需另行配合外部地理编码服务。 **Q6:返回里的 num 是完整手机号吗?可以拿它回填原表吗?** 不可以。`num` 返回的是**号段(前 7 位)**,如 `1890871`,不是完整号码,写回时务必使用你 Excel 里自己的原号码列。 ## 相关能力 / 下一步阅读 - [《5 分钟接入手机归属地查询:从注册到第一条返回》](https://www.showapi.com/guides/phone-attribution-quickstart-6) —— 还不会发请求?从注册到第一条返回 5 分钟跑通 - [《手机归属地返回字段全解:prov/city/type/postCode 一文读懂》](https://www.showapi.com/guides/phone-attribution-response-fields-6) —— 每个字段的类型、取值与坑位 - [《免费档位下如何做缓存:本地缓存手机号→归属地,减少调用》](https://www.showapi.com/guides/phone-attribution-cache-6) —— 大批量前必看,用缓存扛住档位限制 - [《错误码排查:-2 非11位 / -3 非数字 / -4 格式 / -5-6 找不到》](https://www.showapi.com/guides/phone-attribution-error-codes-6) —— 4 类错误对照与排查路径 > **本系列共 12 篇**:查看[手机归属地查询 · 官方指南总目录](https://www.showapi.com/guides/phone-attribution-guides-6)