全国邮编区号大全:3423条离线行政区划字典的导入与避坑指南
简介全国邮编区号数据库收录了3423条省、市、县、区信息完整涵盖区号与邮编字段适合开发者在物流系统、地址库、数据清洗等场景中直接调用。资源共包含4个文件分别提供SQL、JSON、CSV、XLSX格式可满足数据库导入、程序解析、Excel查看等不同需求SQL脚本适合MySQL等关系库JSON便于Web服务调用CSV可高效处理XLSX便于人工浏览整体压缩包仅167KB轻量便携。该数据包由作者p445098355整理上传已有950人学习/下载内容结构清晰字段命名规范可直接用于项目实践。获取后可省去手工搜集与整理基础行政区划数据的繁琐步骤支持快速构建邮编查询、区号匹配或地址规范化功能同时3423条记录也能满足数据校验、离线分析等场景需要对提升开发与运营效率很有帮助。作为一套纯净的离线数据集它避免了在线接口的调用限制可自由集成到各类项目中。1. 全国邮编区号大全一条 3423 行的离线字典能解决什么做订单系统或者物流运费计算的时候最头疼的不是逻辑而是缺一份能离线用的行政区划数据。调线上接口要处理超时和限流自己维护又怕更新不及时。这份全国邮编区号大全把市、县、区名、区号和邮编整理成了 3423 条记录提供 sql、json、csv、xlsx 四种格式解压后随手可用。适合三类人做电商后台要自动补全地址的开发做物流系统要按邮编分区的实施做报表要关联行政区划的数据分析师。先说明一点它粒度是“区县级”不是每条街道都有想精确到支局邮编的别抱期待但对绝大多数表单校验和运费判断场景足够。2. 拆开 zip 先看数据四种格式、字段结构与 3423 条的覆盖边界拿到 zip 后建议先看 json 而不是直接导 sql。原因很简单json 的结构就是字典表最终想落成的结构字段名一眼能对上比打开 sql 文件翻建表语句省事得多。2.1 先看 JSON字段名、类型与一条完整记录第一步先把 zip 解开确认文件列表和大小然后只读 json 的前 800 字节。unzip 全国邮编区号大全.zip -d ./youbian cd ./youbian ls -lh head -c 800 youbian.json第一条命令解压到./youbian目录第二条进入目录第三条看文件大小和实际列表第四步只看前 800 字节防止文件太大把终端刷爆。正常输出里能看到这个 zip 里就是youbian.json、youbian.csv、youbian.sql、youbian.xlsx四个文件摘要里说的 sql/json/csv/xls 四种格式都在。head -c 800看不到完整数组但字段名已经暴露了。这份数据的字段设计是区县级粒度每条记录包含省份、城市、区县名、区号和邮编。JSON 是数组套对象典型的一条记录长这样不同数据源字段名可能有差异这份拆的时候看到的是 province/city/county/areacode/postcode 这类命名{ province: 广东省, city: 广州市, county: 天河区, areacode: 020, postcode: 510630 }这条记录的含义天河区属于广州市、广东省区号 020邮编 510630。这里有个细节需要注意区号和邮编都应该是字符串不是数字“020”如果存成数字会变成 20这节在第五章会展开讲。看 JSON 结构时顺便统计一下记录数jq length youbian.json # 输出 3423 jq .[0] | keys youbian.json jq .[0] youbian.jsonjq length返回数组元素个数用来验证数据总量和摘要里的 3423 是否一致jq .[0] | keys先列出第一条记录的所有字段名确认表结构再写业务代码避免代码里访问不存在的键jq .[0]打印第一条完整记录看实际内容。如果你机器上没装 jq用 Python 也一样import json with open(youbian.json, r, encodingutf-8) as f: data json.load(f) print(len(data)) # 3423 print(data[0])这里encodingutf-8必须指定否则在 Windows 下默认用 GBK 读 UTF-8 文件打开就是乱码。还可以顺手查一下有无空值字段比如某个区县缺邮编jq [.[] | select(.postcode null)] | length youbian.json输出 0 说明这条路上没有缺失如果有非零值说明源数据里有脏记录后面导入数据库时要留意。2.2 四种格式的差异与选型理由这个 zip 里同时给了四种格式不是冗余而是分别对应不同的使用路径。我自己的选型经验是后端要入关系型数据库就用 sql做一次性数据迁移、清洗用 csv写接口或前端离线字典用 json要发给业务同事核对用 xlsx。四者没有哪个“最好”只有哪个“最省事”。格式适用场景导入代价最容易踩的坑sql直接进 MySQL/PostgreSQL低但要先看建表语句编码不对导致中文乱码DROP TABLE 误删csv数据处理、跨语言迁移低任何工具都能读前导零被当数值编码识别错json接口字典、微服务内存加载最低读进来就是结构化数据整体加载超大文件会吃亏xlsx业务核对、人工查阅用办公软件直接开邮编被显示成科学计数法表格里最后一行值得多说一句xlsx 对人友好对程序不友好。如果你准备把 Excel 导入数据库我一般会先让它另存为 csv 再走导入流程而不是让 ETL 工具直接读 xlsx因为 Excel 的单元格类型在保存时已经定型邮编这种“看起来像数字但不是数字”的列最容易坏。2.3 覆盖边界3423 条到底覆盖到什么程度看到 3423 条有人会问全国县区不止这些吧对。按行政区划统计全国有 300 多个地级市、2800 多个县级行政区但这个数字不是简单的一县一条。3423 这个量级意味着它把四个直辖市拆到了区街道把大部分地级市拆到了市辖区和县级市还把一些经济开发区、高新区这种实际存在邮政业务的区域也收了进去。所以在使用时要明确这份数据能解决的问题是“用户填了市/区县补全区号和邮编”以及“用户填了邮编反查大致省市”。它不能解决的是精确到街道或者某个支局段的问题比如一个大学园区内多个邮编的具体归属这种粒度需要邮政系统的专业邮编库。把边界想清楚后面用起来才不会在查询落空时怀疑数据坏了。3. 把 youbian.sql 导入 MySQL建库、验证与常用查询SQL 文件是整个资源里最“重”的一份它通常已经帮你写好了建表语句和 3423 条 INSERT。但直接source进生产库前我一般会先建一个独立的库避免它自带的 DROP TABLE 把同名的旧表清掉这个坑在 5.3 细说。3.1 导入前先建库并确认字符集导入前花一分钟看一眼 SQL 文件头这一步能省掉后面半小时的排错。head -20 youbian.sql主要看三件事建表语句用的引擎和字符集、有没有 DROP TABLE、INSERT 语句的列顺序。看完再建库导入CREATE DATABASE IF NOT EXISTS address DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE address; SOURCE /path/to/youbian.sql;CREATE DATABASE指定 utf8mb4是因为中文生僻字在 utf8 下可能存不进去utf8mb4 是 MySQL 里最稳妥的选择COLLATE utf8mb4_general_ci影响排序和比较大小写邮编区号场景用默认的 general_ci 就行。SOURCE是 mysql 客户端内置命令后面跟绝对路径路径写错会直接报错。如果你习惯用命令行等效做法是mysql -uroot -p address --default-character-setutf8mb4 youbian.sql这里--default-character-setutf8mb4和建库时的字符集保持一致防止文件是 UTF-8 但客户端连接用了 latin1导入后中文全变乱码。这一步是最容易被忽略的乱码的锅通常不在数据本身在连接参数。3.2 验证导入结果记录数、表结构、索引导入完成不意味着结束必须验证。我见过几次“SQL 执行没有报错但实际只导进去一部分”的情况原因基本都是源文件里有被截断的超长 INSERT或者重复执行导致主键冲突跳过。验证分三步一次做完SHOW TABLES; SELECT COUNT(*) FROM youbian; DESC youbian;SHOW TABLES确认表建出来了COUNT(*)必须等于 3423如果小于这个数说明有语句没执行成功如果大于说明有重复导入DESC看字段类型重点确认邮编、区号字段是不是字符串。如果表结构和预期不一样需要改表结构常见做法是把邮编列改成定长字符串ALTER TABLE youbian MODIFY postcode CHAR(6); ALTER TABLE youbian ADD INDEX idx_postcode (postcode);postcode用CHAR(6)而不是INT是因为邮编不是数值是编码改结构这个动作对应到业务里就是大家常说的“mysql 数据库修改结构”的活别因为表里全是数字就顺手存成 int。ADD INDEX给邮编列加索引按邮编反查的频率通常比按城市名高索引加上能省不少查询时间。提示COUNT(*)结果不是 3423 时先别急着调业务回看一下导入日志看是否有报错行如果是重复导入导致的超过 3423先清理再继续。3.3 常用查询按城市查区号、按邮编反查地区数据进库之后核心操作就是两类查询。一类是用户在前端选了“浙江省-杭州市”后台查出杭州的区号 0571另一类是物流系统拿到一个邮编 310000需要反查它属于杭州。SELECT province, city, county, areacode FROM youbian WHERE city 杭州市;如果同省有多个同名区县这个查询会返回多行所以更稳妥的写法是带上省份条件SELECT county, areacode, postcode FROM youbian WHERE province 浙江省 AND city 杭州市;反查邮编的写法正好反过来SELECT province, city, county FROM youbian WHERE postcode 310000;这里有个使用细节查询条件里的邮编建议传字符串。如果你代码里传入的是整型 310000MySQL 会自动把字符串列转成数字比较结果没区别但万一邮编列里存了一个“00310000”这种脏数据类型转换会影响索引命中所以应用层传参时保持字符串类型更稳。3.4 换到 PostgreSQL 或 SQLite 时的改动点这份 SQL 如果按 MySQL 语法生成直接导 PostgreSQL 会报错。常见原因是 MySQL 的反引号、ENGINEInnoDB这类建表选项、以及int(11)显示宽度PostgreSQL 都不认。处理办法很朴素拿到 SQL 后用文本编辑器把头部的建表语句改成 PG 风格INSERT 语句本身基本通用。或者绕开 SQL 文件直接用 CSV 导入createdb address psql -d address -c CREATE TABLE youbian ( province text, city text, county text, areacode text, postcode text ); psql -d address -c \COPY youbian FROM youbian.csv WITH (FORMAT csv, HEADER true, ENCODING UTF8)\COPY是 psql 的导入命令HEADER true表示第一行是列名要跳过ENCODING UTF8指定源文件编码。PostgreSQL 和 MySQL 对字符集的要求不同PG 默认 UTF8所以这里反而不用额外设置。SQLite 的导入更简单很多项目做原型验证时直接拿它当本地字典库。用 Python 内置的 sqlite3 就能完成import sqlite3, csv conn sqlite3.connect(youbian.db) conn.execute(CREATE TABLE youbian (province TEXT, city TEXT, county TEXT, areacode TEXT, postcode TEXT)) with open(youbian.csv, r, encodingutf-8-sig) as f: reader csv.DictReader(f) for row in reader: conn.execute( INSERT INTO youbian VALUES (?,?,?,?,?), (row[province], row[city], row[county], row[areacode], row[postcode]) ) conn.commit() conn.close()这段代码把 CSV 逐行写入 SQLite参数用?占位符防止注入utf-8-sig是为了兼容带 BOM 的 CSV 文件。五列的顺序对应 CSV 的表头如果你的 CSV 列顺序不同把row里的键名改成实际列名。4. 按场景选格式JSON 给服务、CSV 给迁移、XLSX 给业务核对四种格式各有主场。这一章按真实使用场景拆开讲服务启动时怎么加载、数据迁移时怎么防坑、发给业务核对时怎么处理 Excel 的类型问题。4.1 JSON 加载到内存做成接口字典服务端如果不想为一份不到 100KB 的字典专门建表直接让 JSON 常驻内存是最省事的方式。3423 条记录加载进来也就是几 MB 堆内存完全不用上缓存。关键是只在启动时加载一次不要在每次请求里读文件。import json with open(youbian.json, r, encodingutf-8) as f: address_list json.load(f) postcode_index {item[postcode]: item for item in address_list} def get_by_postcode(code: str): return postcode_index.get(str(code))json.load一次性把整个数组读成 Python 列表然后我把它转成一个以邮编为 key 的字典查询从 O(n) 变成 O(1)。get_by_postcode入参强制转成字符串避免调用方传310000和字典里的310000匹配不上。同样的思路也可以用在 FastAPI、Flask 或者 Node 的进程里只要保证服务启动时加载一次即可。要查区号就再建一个areacode_index按区号反查一批城市时在内存里做一次过滤def get_cities_by_areacode(areacode: str): return [row for row in address_list if row[areacode] str(areacode)]这里返回的是列表因为同一个区号可能对应多个区县调用方要处理数组而不是单对象。4.2 CSV 做迁移dtype 和前导零问题一次性处理CSV 是人见人爱的交换格式也最容易在类型上翻车。用 pandas 读的时候如果不指定 dtype邮编“010000”会被读成整型 10000区号“010”会变成 10。所以读取必须显式声明列类型import pandas as pd df pd.read_csv( youbian.csv, dtype{postcode: str, areacode: str}, encodingutf-8 ) print(df.shape) # (3423, 5) print(df[postcode].str.len().max()) # 6dtype参数把两列强制成字符串encodingutf-8避免 Windows 中文环境误判。df.shape[0]是 3423可以用来和 JSON 里的数量做交叉验证再用str.len().max()检查邮编长度全是 6 位说明没有数值化截断。如果你拿到的 CSV 是 GBK 编码encoding改成gbk两种编码都试一次谁不乱码用谁。如果你已经在前面一步读坏了比如区号列变成了 10、20 这种补救办法是用zfill补回前导零df[areacode] df[areacode].astype(str).str.zfill(3)zfill(3)把10补成010。但注意这个方法只适合长度不超过 3 位的区号如果数据本身是四位区号zfill(3)不会截断也不会改变四位数放心用。邮编同理str.zfill(6)可以把误转的310000保持原样把10000补回010000。4.3 XLSX 给业务核对先解决科学计数法把 xlsx 发给运营或者财务核对第一件事是告诉他们别双击打开就完事。邮编列遇到以 0 开头的值Excel 会直接丢掉前导零遇到超过 11 位的数字会显示成科学计数法。这不是数据错了是 Excel 的类型推断在作怪。给业务方核对时双保险做法是发 xlsx 的同时附一个纯文本的 csv 版本让他们遇到显示异常时以 csv 为准。如果你自己要用 Python 把 xlsx 里的邮编读出来用 openpyxl 处理单元格的值已经是数字的话需要补零from openpyxl import load_workbook wb load_workbook(youbian.xlsx) ws wb.active for row in ws.iter_rows(min_row2, values_onlyFalse): cell row[4] # 假设邮编是第 E 列 if isinstance(cell.value, (int, float)): cell.value str(int(cell.value)).zfill(6) wb.save(youbian_fixed.xlsx)load_workbook默认保留单元格格式iter_rows遍历从第二行开始的每一行第一行是表头isinstance(cell.value, (int, float))判断 Excel 是否已经把它当数字存了是的话转成字符串后用zfill(6)补回前导零再另存为新文件。注意zfill(6)只适合邮编长度不超过 6 位的场景如果源数据本身有 5 位邮编补成 6 位反而错了所以补零前最好先看一眼原始数据的最大长度。如果你准备把 Excel 里的数据导入数据库最省事的路是先用办公软件另存为 csv再走 4.2 的 pandas 流程而不是直接写代码读 xlsx 再拼 SQL。绕开 Excel 的类型系统就绕开了一半的坑。5. 避坑指南邮编数据导入与使用中的五个踩坑记录下面把我拆这类数据时遇到的高频问题按“现象、原因、解决”格式列出来照着排查。5.1 前导零被数值化区号 010 变成 10现象导入 MySQL 或 Excel 后区号“010”变成了“10”邮编“010000”变成了“10000”查询时结果对不上。原因建表时把区号列设成了 INT或者 CSV 导入工具自动推断成数值类型。区号和邮编是编码不是数字前导零有意义。解决建表用CHAR(6)或VARCHAR(10)不碰 INT读 CSV 时用dtype{postcode: str, areacode: str}。如果已经导坏了用 SQL 补零UPDATE youbian SET areacode LPAD(areacode, 3, 0) WHERE LENGTH(areacode) 3;改完先查几条确认比如之前是 10 的现在应该变回 010。5.2 中文乱码字段全是问号现象SQL 文件导入后省份城市显示成????????。原因文件是 UTF-8 编码但 MySQL 连接用了默认的 latin1或者文件本身是 GBK被按 UTF-8 读。乱码的锅通常不在数据本身在连接参数。解决命令行导入加--default-character-setutf8mb4如果源文件是 GBK 的 csv把encoding换成gbk后再读。已经导坏的表先删掉重建不要尝试原地 UPDATE 修编码血泪经验是越修越乱。5.3 SQL 文件自带 DROP TABLE现象执行 source 后原本存在的同名表不见了连同旧数据一起被清掉。原因很多数据集为了方便重复导入会在建表语句前写DROP TABLE IF EXISTS youbian;这是常态但很危险。解决导入前用head -20 youbian.sql看一眼文件头确认有没有 DROP生产环境永远先导入到临时库验证再迁移到正式业务表。哪怕确认没有 DROP我也建议用独立库导入因为这类字典表很可能与业务表重名。5.4 区号不是一一对应同一个区号查出多条记录现象按区号“0755”反查城市一次返回了深圳的多个区县查询结果远多于预期。原因一个城市下辖的所有区县共用同一个区号区号是地级粒度不是区县级粒度。解决反查时带上城市条件比如查“广东省-深圳市-0755”如果只要城市名用去重查询SELECT DISTINCT province, city, areacode FROM youbian WHERE areacode 0755;单独用区号过滤会返回多条这是数据本身的粒度决定的不是数据错了。5.5 行政区划调整后的陈旧数据现象某县改成区了数据里还是旧县名或者邮编还是旧的。原因数据集发布有滞后撤县设区、乡镇合并后旧名称不会立刻更新。解决把这份数据定位成“大致归属”字典不用在需要绝对权威的场景。重要业务流程上线前抽样对比一下邮政系统公布的邮编发现问题再人工修正。同时留意这份 zip 的发布时间它决定数据实际代表哪个行政版本的区划。6. 用 CSV 反查 SQL把这份邮编库变成可信赖的字典表数据导完、查过、能跑通这是第一步。我习惯在交付前再做一次交叉验证把 json 和 csv 当作两份独立的数据源互相查一遍记录数和内容确认它们没差。import csv, json with open(youbian.json, r, encodingutf-8) as f: json_data json.load(f) with open(youbian.csv, r, encodingutf-8-sig) as f: csv_rows list(csv.DictReader(f)) print(json:, len(json_data), csv:, len(csv_rows)) assert len(json_data) len(csv_rows), 记录数不一致 json_index {row[postcode]: row for row in json_data} diff [] for row in csv_rows: jr json_index.get(row[postcode]) if jr is None or jr[county] ! row[county]: diff.append(row[postcode]) print(不一致数量:, len(diff))这段逻辑不复杂但很实用先断言两个文件的记录数都是 3423再按邮编聚类逐条比对区县名。任何一个邮编在 json 和 csv 里的归属地不一致都会被diff接住。我过去用这个方法抓到过数据集里两个文件版本不一致的翻车现场从那以后每次拿到此类离线字典数据我都强制走一遍数量对比加内容抽查确认无差异后才允许进业务表。验证通过后还有一件容易被忽略的事检查表里有没有完全重复的行。同一邮编可能属于多个区但同一区县不应出现两条相同记录用 SQL 去重检测一次SELECT postcode, COUNT(*) FROM youbian GROUP BY postcode HAVING COUNT(*) 1;返回空就说明邮编没有重复可以做业务表的外键参考有返回就要逐个排查是数据错了还是真的一个邮编多区县。字典表工具虽小但它是订单、物流、报表的公共底座数据不干净下游全是连锁反应。希望帮到你。本文还有配套的精品资源点击获取