简介:全国手机号码段归属地数据库,是面向开发者、数据分析师及运营人员的实用数据包,覆盖国内主要运营商号段,可用于手机号属地识别、地域统计与快速查询等场景。压缩包内共4个文件,包含SQL脚本、CSV数据表及TXT说明文档,整体大小7.48MB;SQL脚本提供建表语句和INSERT导入数据,可直接在MySQL等关系型数据库执行,CSV文件则可用Excel或WPS打开,实现排序、筛选、透视等操作,尤其适合不熟悉数据库查询的日常办公人员。数据总计360569条记录,每条包含手机号码段、运营商、省份、城市、区号、邮编等关键字段,覆盖移动、联通、电信全量号段信息,既可作为反欺诈风控中的号码风险识别依据,也能用于地域化营销、客户服务及通信消费习惯分析。目前已有5053人学习下载,说明该数据集在开发者和业务人员中具备较高的实用价值。读者可直接将SQL文件导入数据库以构建查询接口,或用CSV文件做离线统计,从而快速为业务系统增加手机号归属地能力。
1. 全国手机号码段归属地数据库:一张36万条记录的表解决归属地查询
做风控标注、客服工单分拣或者快递发货校验的时候,拿到一个陌生手机号先确认它属于哪个省哪个市,几乎是每天都要重复的操作。有人去搜网页接口,有人装个归属地查询APP,折腾半天还把手机号喂给了第三方。其实一张本地的号段表就够了:这份全国手机号码段归属地数据库收录了360569条记录,按手机号前7位匹配省、市、运营商和区号,以Excel格式交付。适合做离线批量查询、数据标注、测试号码生成的人,数据是静态快照,拿来当离线字典用非常顺手。
2. 号码段数据的底层结构与Excel实操:前7位规则、字段清洗与快速验证
2.1 前7位决定归属地:手机号的数据划分逻辑
国内手机号共11位,结构拆开看是三层:前3位是网络识别号,标识运营商和网络制式;第4到第7位是HLR识别码,标识归属地交换局;最后4位是用户号。所以运营商把一个号段投入市场时,实际上是以“前7位”为单位申请的,号段表的匹配键天然就是前7位。这份Excel数据里的每条记录,对应一个已分配的号段,后面挂着省份、城市、运营商和区号。
理解了这点,就知道为什么号段表不是按“每个手机号一行”来组织的——11位号码全量的组合是千亿级,而号段数量只有几十万级别。360569条这个量级,基本覆盖了已经分配的公众移动通信网号段。拿到一个完整手机号时,截取前7位去查表就行,不需要也不可能为每个号码建一条数据。
字段上这份表通常包含:号段(前7位)、省份、城市、运营商、区号。区号这个字段容易被忽略,但实际作用不小:做客服中心选址、判断外呼号码是否异地、拼接本地号码格式时都能用到。运营商字段一般标的是移动、联通、电信,遇到虚拟运营商和广电号段时会有差异,这一点在第4章展开。
2.2 Excel里的常用字段与清洗习惯
拿到Excel后第一件事不是急着查数据,而是先做三步清洗。
第一步,检查表头。常见表头可能是“号段/省份/城市/运营商/区号”,也可能叫“号码段/所属省/所属市/运营商/长途区号”。字段名不同没关系,关键是确认列顺序,后面导入数据库或写脚本时按列顺序映射。
第二步,把号段列格式设为文本。Excel对纯数字列有类型推断,虽然前7位号段没有前导零问题,但如果之后要在这个表旁边维护一张完整手机号表,11位手机号会被Excel自动转成科学计数法显示,到那时再匹配就会出各种幺蛾子。我一般会把号段列和手机号列全部设为文本,避免后续匹配时由于格式不一致导致#N/A。
第三步,去重和空值检查。号段表理论上不该有重复,但手工整合过的Excel不一定干净。用COUNTIF对号段列做重复检测,比如在辅助列写=COUNTIF(A:A,A2),筛选出大于1的行,逐条确认。空值检查用筛选,看看省份、城市、运营商有没有空行。空行会影响后续的VLOOKUP和LOAD DATA导入,提前清掉比在数据库里抓错省事得多。
2.3 用Excel快速验证一条数据
清洗完了先用VLOOKUP验证一下表能不能用。假设号段在A列从第2行开始,手机号在另一个工作表或本表F列,通过LEFT函数截取前7位再匹配,公式是这样:
=VLOOKUP(LEFT(F2,7),$A:$E,2,FALSE)注意这里匹配列是A列(号段),返回第2列是省份。如果要把城市、运营商也带出来,把列索引改成3、4继续写就行。LEFT(F2,7)先从完整手机号里切出前7位,再放进VLOOKUP里和号段列做精确匹配,FALSE保证匹配方式为精确查找,不要用近似匹配。
我在实际工作中经常用这个公式快速核对一批测试号码。把几百个手机号粘进去,拉一下公式,哪个号段查不到立刻就能筛出来。查不到的号段别急着认定数据有问题,先用第4章里的方法排查是不是新发放号段或者虚拟运营商。
3. 把Excel号段表导入数据库:从建表到查询的全流程配置
3.1 建表结构与字段类型选择
Excel适合手动验证,但要支撑线上查询或者批量JOIN,还是得落到数据库里。拿MySQL举例,表结构我会这样建:
CREATE TABLE phone_prefix ( prefix CHAR(7) NOT NULL COMMENT '手机号前7位,含前导零', province VARCHAR(32) NOT NULL COMMENT '省份', city VARCHAR(32) NOT NULL COMMENT '城市', carrier VARCHAR(16) NOT NULL COMMENT '运营商', area_code VARCHAR(8) DEFAULT NULL COMMENT '区号', PRIMARY KEY (prefix) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='手机号码段归属地表';字段类型这里有几个关键选择。prefix用CHAR(7)而不是INT或BIGINT,因为号段虽然在当前是7位数字,但本质是标识符不是数值,用字符串类型可以避免将来出现前导零号段时被隐式转换吃掉。province和city用VARCHAR(32)够用,运营商名称最长也就几个字,VARCHAR(16)留了余量。area_code允许为空,有些号段数据里没填区号,用DEFAULT NULL比存空字符串更干净。主键直接设在prefix上,这个字段天然唯一,同时作为查询索引用,不需要额外加索引。
字符集选utf8mb4而不是utf8,原因很实际:utf8在MySQL里是utf8mb3,存不了生僻字和部分特殊符号,utf8mb4是完整实现。省份城市字段虽然大概率不涉及生僻字,但导入工具或后续手工UPDATE时可能混入特殊字符,选utf8mb4省的将来报错。
3.2 Excel导入MySQL的两种方式
导入有两种常见路径。第一种是图形化工具,用Navicat的“导入向导”选Excel文件,像这样操作:导入类型选Excel文件,指定文件路径,目标表选phone_prefix,字段映射时把Excel列按顺序拖到表字段上,勾选“首行作为字段名”跳过表头,字符集选UTF-8,最后点开始。这种方式适合Excel文件里有合并单元格、宏一类复杂格式的情况,工具会自己处理一部分兼容问题。
第二种是命令行LOAD DATA,适合服务器环境或者要把导入步骤固化成脚本的场景:
LOAD DATA LOCAL INFILE '/data/phone_prefix.csv' INTO TABLE phone_prefix FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 LINES (prefix, province, city, carrier, area_code);用LOAD DATA之前有个前置动作:把Excel另存为CSV文件。注意另存时编码要选UTF-8,Excel默认存成GBK,直接LOAD进utf8mb4的表会导致中文乱码,这一步在第4章会详细说。FIELDS TERMINATED BY ','指定CSV的列分隔符是英文逗号,OPTIONALLY ENCLOSED BY '"'处理字段值里可能带双引号的情况,常见于城市名或运营商名被Excel自动加了引号。IGNORE 1 LINES跳过第一行表头,然后按顺序把CSV的五个列喂给表的五个字段。
3.3 日常查询SQL与性能验证
数据导进去之后,最常用的查询是给定一个手机号找归属地:
SELECT province, city, carrier FROM phone_prefix WHERE prefix = LEFT('13812345678', 7);LEFT函数从完整手机号里截取前7位,和prefix主键做等值匹配。因为prefix是主键,这步查询走的是索引查找,单条数据耗时在毫秒级。我习惯用EXPLAIN确认一下执行计划,看到type列为const或ref就说明命中了索引,如果看到ALL就需要回头检查主键是否设置成功。
批量查询的场景更常见,比如一次给几百个号码打归属地标签。这时候不要一条条查,用临时表JOIN效率高得多:
CREATE TEMPORARY TABLE tmp_phones (phone VARCHAR(11) NOT NULL PRIMARY KEY); INSERT INTO tmp_phones VALUES ('13812345678'), ('15912345678'), ('18612345678'); SELECT p.phone, pf.province, pf.city, pf.carrier FROM tmp_phones p LEFT JOIN phone_prefix pf ON pf.prefix = LEFT(p.phone, 7);这里LEFT JOIN比INNER JOIN更适合业务场景:查不到归属地的号码也要返回结果,只是后面三个字段为NULL,一眼就能看出哪些号码的号段不在表里。查出来之后,把NULL的行汇总一下,就是需要人工核实的号段清单。
4. 避坑与常见问题排查:号段数据的边界与五个常见误用
4.1 虚拟运营商号段查不到或运营商不准
现象:输入170、171、165、167、162开头的号码,返回的省份能匹配,但运营商一栏标的是“虚拟运营商”或者干脆查不到。
原因:虚拟运营商租用三大运营商的网络资源,号段码本身是单独分配的,传统号段表里要么没收录,要么归到某个基础运营商名下,导致显示与实际套餐的虚商品牌不一致。
解决:先确认表里是否包含这些号段记录,包含的话按现有标记用即可;不包含的话,自己维护一张虚商号段映射表,把170~1709等号段对应到实际虚商品牌,因为虚商在不同地区的放号情况不同,这块没有统一标准,只能根据业务实际覆盖范围补充。
4.2 携号转网让归属地“失真”
现象:一个号码原来是移动的,用户办理携号转网后变成了联通,但查询结果还是显示移动。
原因:号段表是以“号段发放时的归属运营商”为基准建立的,携号转网改变的是用户当前的签约运营商,不改变号码本身。号段归属是静态属性,用户归属是动态状态,一张静态表无法反映后者。
解决:不要用号段表判断用户当前的真实运营商,这类判断必须依赖运营商侧接口或用户自助上报。号段表适合的是“归属地省份/城市”这类几乎不随转网变化的属性。做业务规则时把“运营商”字段当成“号段原始运营商”理解,不要在风控策略里用它做唯一决策依据。
4.3 Excel里号码变成科学计数法导致VLOOKUP查不到
现象:在Excel里维护手机号表,输入13812345678后单元格显示成1.38123E+11,用VLOOKUP去号段表里匹配,明明号段存在却返回#N/A。
原因:Excel默认把超过一定长度的纯数字列转成科学计数法格式,单元格显示变了,实际存储值也被转成浮点数。号段表里的prefix如果没设成文本,两边类型不一致,VLOOKUP精确匹配直接失败。
解决:选中手机号列和号段列,单元格格式统一设成“文本”。已经变成科学计数法的,用“数据→分列→文本”方式恢复,分列向导里第1步选“分隔符号”,第2步不选任何分隔符,第3步列数据格式选“文本”,就能把科学计数法还原成完整号码。匹配公式里再包一层TEXT(A2,"0")强制转文本,双保险。
4.4 导入数据库后中文乱码
现象:用LOAD DATA导入CSV后,SELECT查询发现省份城市全是问号或乱码,但Excel里看是正常的。
原因:Excel另存为CSV时默认编码是GBK,而MySQL表用了utf8mb4,LOAD DATA按表的字符集解析GBK文件时直接解码失败,中文全变“???”。
解决:另存CSV时在“另存为”对话框里选择“CSV UTF-8”格式,而不是默认的“CSV”。如果文件已经存成CSV,用文本编辑器另存为UTF-8编码再导入。LOAD DATA语句里也可以追加CHARACTER SET utf8mb4声明,指定按该字符集解析文件,同一条语句示例:
LOAD DATA LOCAL INFILE '/data/phone_prefix.csv' INTO TABLE phone_prefix CHARACTER SET utf8mb4 FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 LINES (prefix, province, city, carrier, area_code);4.5 新号段不断发放导致数据过期
现象:用门店近期办理的新号码测试,发现号段表里查不到,或者省份显示完全不对。
原因:号段不是一次性分配完的,运营商和主管部门会不定期批复新号段。手头这份36万条的数据是某个时间点的快照,之后新增的号段自然不在覆盖范围里。
解决:把号段表当成“基线数据”而不是“实时权威数据”。每隔一段时间抽样测试,用最近三个月的新办号码跑一遍查询,查不到的存进补录表。如果业务对覆盖敏感,可以定期手动在Excel末尾追加新号段,再重新导入数据库。注意甄别网上流传的“最新号段”整理贴,最好以可验证的真实号码为准。
5. 进阶用法:用Python搭一个本地的号段归属地查询工具
数据库查询适合复杂关联,但有时候只是想在本地快速跑一批号码,开MySQL显得笨重。我一般会用Python写一个几十行的查询工具,把号段表载入内存字典,查询复杂度O(1),处理几万条号码也不慢。
import csv import sys PREFIX_FILE = "phone_prefix.csv" def load_db(path: str) -> dict: db = {} with open(path, encoding="utf-8") as f: reader = csv.DictReader(f) for row in reader: db[row["prefix"]] = row return db def locate(db: dict, phone: str): prefix = phone[:7] return db.get(prefix) if __name__ == "__main__": db = load_db(PREFIX_FILE) phones = sys.argv[1:] or ["13812345678"] for p in phones: info = locate(db, p) if info: province = info.get("province", "未知") city = info.get("city", "未知") carrier = info.get("carrier", "未知") print(f"{p}: {province} {city} {carrier}") else: print(f"{p}: 未匹配到号段")脚本逻辑分三块。load_db读CSV建字典,KEY是号段,VALUE是整行数据;locate截取目标手机号前7位做字典查询;主流程里遍历传入的号码批量输出。需要注意CSV第一行必须是表头,且字段名包含prefix、province、city、carrier,如果表头命名不同,把DictReader的字段名对应改成Excel里的实际列名即可。
运行时直接传手机号参数,比如python locate.py 13812345678 15912345678,脚本会逐条打印归属地。不想启动MySQL又需要跑批量的场景下,这个工具比打开Excel逐行VLOOKUP顺手得多。如果要更进一步,把数据导成SQLite文件,把上面的字典替换成sqlite3查询,就能在Python里同时享受索引和零部署。
做成HTTP接口也很容易,用Flask或FastAPI包一层GET方法,/locate?phone=13812345678就能接到现有系统里。我这套工具在本地给运营做了一版,现在每次接到新的号码样本,我都会强制走一遍“先查号段表、再抽非对称样本人工复核”的流程,把新增字段的匹配结果和线上老数据的统计口径做一致性对齐。这一套从Excel到字典的转换路径踩过不少坑,慢慢成型之后,后续再碰任何“静态字典型数据”都能直接复制这套思路。希望帮到你。
本文还有配套的精品资源,点击获取