
简介这份资源是淘宝四级地址库面向电商开发、物流配送、地理信息系统及数据分析人员用于解决省、市、区县、乡镇街道四级行政区划的查询、匹配与地址校验问题。压缩包内共1个文件为sql格式的数据库脚本整体约351KB可直接导入数据库使用字段涵盖行政区划代码、级别、名称、上级代码等便于通过SQL进行检索与关联分析。数据采用国家标准行政区划代码前两位标识省份依次对应城市、区县与乡镇街道四级结构完整并附带街道信息能支撑精确到门牌号的地址填充与验证。目前已有1071人学习下载适合需要处理地理位置数据的开发者参考。借助该库可快速搭建地址选择组件、优化订单收货地址校验、按地域划分市场也能为本地化推荐提供基础数据支撑实用价值较高。1. 淘宝4级地址库从省到街道一份能直接塞进项目的行政区划数据做电商订单、物流分单或者用户画像的兄弟大概率都遇到过这种场景前端省市区三级联动跑得好好的到了填写详细地址那一步用户随手填个“XX街道”“XX镇”后端一校验发现数据库里根本没有街道级数据只能把这一截当纯文本存下来。等到要做同城配送范围计算、街道级热力图或者按行政区划做数据统计时才发现缺了这一层回头补数据的成本高得离谱。这份淘宝4级地址库就是冲着这个缺口来的——省、市、区县、街道四级完整覆盖并且和国家标准行政区划代码对齐不是那种网上随便爬的残缺版本。它适合做电商后台、物流系统、CRM 地址模块的开发者也适合需要按行政区划做数据分析的团队。拿到手之后你不需要再自己拼凑省市县街道的层级关系直接导入就能用。2. 淘宝地址库的数据结构四级层级怎么组织代码怎么对齐2.1 四级地址的存储模型与字段含义这份地址库的核心价值在于它把“省—市—区县—街道”四个层级用一套统一的编码体系串起来了。常见做法是采用一张自关联表或者四张独立表我一般倾向于单表加层级字段的方式查询和缓存都更顺手。典型字段包括行政区划代码adcode、父级代码parent_code、层级level、名称name、完整路径full_name、拼音首字母pinyin等。其中 adcode 是灵魂字段国家标准行政区划代码是 6 位数字街道级会扩展到 9 位或 12 位这份数据在街道层级上做了补齐所以你能看到类似110101001这样的街道代码。字段名类型说明adcodevarchar(12)行政区划代码省市区6位街道9位起parent_codevarchar(12)父级行政区划代码顶级为0或空leveltinyint1省 2市 3区县 4街道namevarchar(64)行政区划名称full_namevarchar(255)完整路径如“浙江省杭州市西湖区某街道”pinyinvarchar(64)拼音用于搜索联想注意不同来源的街道代码位数可能不一致国家标准是 9 位但部分历史数据会保留 12 位。导入前先确认你业务里用的校验规则否则会出现“代码对不上、关联查不出”的玄学问题。2.2 用 Python 快速验证数据完整性与层级关系拿到数据后别急着往生产库灌先跑一遍完整性检查。下面这段脚本做三件事统计各级数量、检查是否存在孤儿节点父级代码找不到对应记录、验证街道级数据是否覆盖了你关注的区域。import pandas as pd # 读取地址库假设是 CSV 格式字段按实际调整 df pd.read_csv(taobao_address.csv, dtype{adcode: str, parent_code: str}) # 1. 各级数量统计 level_counts df[level].value_counts().sort_index() print(各级行政区划数量) print(level_counts) # 2. 孤儿节点检查父级代码不在 adcode 集合中 adcode_set set(df[adcode]) orphans df[~df[parent_code].isin(adcode_set) (df[parent_code] ! 0)] print(f\n孤儿节点数量{len(orphans)}) if len(orphans) 0: print(orphans[[adcode, name, parent_code]].head(10)) # 3. 检查街道级覆盖以某个区为例 district_code 330106 # 某区代码按实际替换 streets df[(df[parent_code] district_code) (df[level] 4)] print(f\n该区街道数量{len(streets)}) print(streets[[adcode, name]].to_string(indexFalse))这段代码的逻辑很直白先看总量对不对再用集合运算快速定位断链的节点。parent_code ! 0这个条件是因为顶级省份的父级通常写 0 或空不参与孤儿判断。如果你发现孤儿节点集中在某个省大概率是数据合并时漏了中间层这时候别硬导先找数据提供方确认。参数上唯一要改的就是文件路径和字段名其他不用动。2.3 导入 MySQL 并建立索引的实操步骤验证通过后下一步是入库。我一般用LOAD DATA LOCAL INFILE或者 Python 的to_sql数据量大的话前者快很多。建表时注意 adcode 和 parent_code 都建索引因为后续查询几乎都是按这两个字段过滤。CREATE TABLE address_division ( id int NOT NULL AUTO_INCREMENT, adcode varchar(12) NOT NULL COMMENT 行政区划代码, parent_code varchar(12) NOT NULL DEFAULT 0 COMMENT 父级代码, level tinyint NOT NULL COMMENT 层级 1省 2市 3区 4街道, name varchar(64) NOT NULL COMMENT 名称, full_name varchar(255) DEFAULT NULL COMMENT 完整路径, pinyin varchar(64) DEFAULT NULL COMMENT 拼音, PRIMARY KEY (id), KEY idx_adcode (adcode), KEY idx_parent (parent_code), KEY idx_level (level) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT四级行政区划地址库;导入命令用LOAD DATA时记得加LOCAL和正确的分隔符CSV 里有逗号的话字段要用引号包起来否则会串列。导入完成后跑一句SELECT level, COUNT(*) FROM address_division GROUP BY level;对一下数量和前面 Python 统计的结果一致才算稳。如果数量对不上先看是不是有行被截断了常见原因是full_name字段超长或者编码不是 utf8mb4。3. 把地址库接进业务联动查询、缓存与接口设计3.1 省市区街道四级联动的 SQL 与缓存策略前端联动查询的本质是“给一个父级代码返回所有子级”。有了 parent_code 索引这条 SQL 快得飞起-- 查询某个市下面的所有区县 SELECT adcode, name, level FROM address_division WHERE parent_code 330100 AND level 3 ORDER BY adcode;但真实业务里不会每次都查库尤其是省市区这种几乎不变的数据。我一般会做两级缓存本地缓存Caffeine 或 Guava存全量数据Redis 存按 parent_code 聚合的 JSON 结构。全量数据也就几千条街道加上几千条省市区内存完全放得下。缓存 key 用addr:children:{parent_code}value 是子级列表的 JSON 字符串。更新频率极低设置个 24 小时过期都行有变更手动刷新。注意街道级数据在部分城市会有调整比如撤镇设街道如果你的业务对地址准确性要求极高建议每季度核对一次数据源别一套数据用三年。3.2 用 Redis 缓存全量地址树的代码示例下面这段 Python 代码演示怎么把地址库组装成树形结构塞进 Redis前端拿一次就能渲染整个联动组件减少请求次数。import json import redis import pandas as pd r redis.Redis(hostlocalhost, port6379, db0) df pd.read_csv(taobao_address.csv, dtype{adcode: str, parent_code: str}) # 按 parent_code 分组构建 children 映射 children_map {} for _, row in df.iterrows(): parent row[parent_code] if parent not in children_map: children_map[parent] [] children_map[parent].append({ adcode: row[adcode], name: row[name], level: int(row[level]) }) # 批量写入 Rediskey 为 addr:children:{parent_code} pipe r.pipeline() for parent, children in children_map.items(): key faddr:children:{parent} pipe.set(key, json.dumps(children, ensure_asciiFalse)) pipe.execute() print(f已写入 {len(children_map)} 个父级节点)逻辑说明先遍历 DataFrame 按 parent_code 聚合然后 pipeline 批量写 Redis避免逐条 set 的网络开销。ensure_asciiFalse保证中文不被转义成 Unicode 码点前端拿到的就是可读的 JSON。参数上唯一要注意的是 Redis 连接信息生产环境记得加密码和连接池。如果数据量特别大可以只缓存到区县级街道级按需查库但这份数据街道量不算夸张全缓存问题不大。3.3 地址搜索与拼音联想的实现思路用户输入“hz”想匹配“杭州”或者输入“西湖”想同时匹配区和街道这就需要拼音字段和模糊查询配合。简单做法是用LIKE加拼音前缀匹配但数据量大时性能会掉。我一般会引入一个轻量搜索引擎或者用 MySQL 全文索引但最省事的方案是把 name 和 pinyin 拼成一个 search_key 字段建普通索引查询时用search_key LIKE hz%。-- 添加搜索字段并建索引 ALTER TABLE address_division ADD COLUMN search_key varchar(128) DEFAULT NULL; UPDATE address_division SET search_key CONCAT(name, , IFNULL(pinyin, )); CREATE INDEX idx_search ON address_division(search_key); -- 前缀搜索 SELECT adcode, name, level, full_name FROM address_division WHERE search_key LIKE hz% LIMIT 20;这种前缀匹配对拼音首字母特别有效用户输入“hz”能命中“杭州”输入“xihu”能命中“西湖”。缺点是只能前缀匹配中间匹配要用%hz%但那样索引就失效了。折中方案是前端做防抖后端限制返回条数用户体验不会差。如果你追求更丝滑的联想可以上 Elasticsearch 的 completion suggester但那是另一个量级的工程了中小项目没必要。4. 避坑与排查地址库落地时最容易翻车的五个点4.1 行政区划代码对不上导致关联查询为空现象明明数据里有“某街道”但用 parent_code 关联查不出来或者前端联动到区县就断了。原因不同来源的 adcode 位数不一致国家标准是 6 位但有些数据把街道代码写成了 12 位或者父级代码补了前导零而子级没补。解决统一用字符串类型存储导入前跑一遍df[adcode].str.len().value_counts()看位数分布把不一致的挑出来手动对齐。父级代码和子级代码的位数规则要一致要么都补零要么都不补。4.2 街道级数据缺失或名称重复现象某些城市的街道数量明显偏少或者同一个区下面出现两个同名街道。原因数据合并时漏了部分区域或者不同年份的区划调整导致重名比如两个“城关镇”在不同县。解决用 adcode 做唯一键别用名称做关联。发现缺失时拿国家标准行政区划代码表比对缺的补上。重名问题在展示时加上父级名称前缀比如“某县-城关镇”避免用户混淆。4.3 导入时字符集不对导致中文乱码现象导入 MySQL 后SELECT出来是问号或者乱码。原因CSV 文件编码是 GBK而数据库是 utf8mb4或者LOAD DATA时没指定字符集。解决导入前用file -i或文本编辑器确认编码统一转成 UTF-8。LOAD DATA时加CHARACTER SET utf8mb4建表也用 utf8mb4。如果已经乱码了只能重新导入别想着改库编码能救回来。4.4 缓存与数据库不一致现象更新了地址库数据但前端还是旧的或者 Redis 里查不到新加的街道。原因缓存没有失效机制或者更新数据库后忘了刷缓存。解决地址数据变更频率低但一旦变更就要同时更新数据库和 Redis。我一般写个管理接口更新完数据库后直接删掉对应的addr:children:{parent_code}key下次查询自动回源。别用定时全量刷新没必要还容易出时间差。4.5 层级判断逻辑写死导致扩展困难现象代码里到处是if level 3这种硬编码后来想加个“社区”层级就崩了。原因层级数字写死在业务逻辑里没有抽象成配置。解决把层级定义成常量或枚举查询时用level 4这种范围条件别写等号。前端联动组件也做成可配置层级数的传个 maxLevel 参数就行。这份数据目前是四级但保不齐以后要扩展到五级留个口子不费事。5. 进阶技巧用地址库做配送范围判断与数据校验地址库最实用的进阶场景是配送范围判断。比如你有一批订单需要判断收货地址是否在某个网点的配送范围内而配送范围是按街道划定的。这时候你只需要把订单地址解析到街道级 adcode然后和网点配置的街道列表做交集。def is_in_delivery_range(order_address, delivery_streets): order_address: 已解析的地址字典包含 adcode delivery_streets: 网点覆盖的街道 adcode 集合 order_street_code order_address.get(street_adcode) if not order_street_code: return False # 解析不到街道默认不配送 return order_street_code in delivery_streets # 示例网点覆盖了三个街道 delivery_set {330106001, 330106002, 330106003} order {street_adcode: 330106002, full_name: 浙江省杭州市西湖区某街道} print(is_in_delivery_range(order, delivery_set)) # 输出 True这个逻辑简单但有效关键是订单地址要能准确解析到街道级 adcode。如果你的用户地址是自由文本那就需要先做地址解析把“某街道”映射到 adcode。常见做法是用地址库的 full_name 做模糊匹配或者用分词加拼音匹配。我一般会写一个解析函数先精确匹配 full_name匹配不到再用 name 加父级名称组合匹配最后兜底用拼音。另一个技巧是用地址库做数据校验。比如用户注册时填的地址你可以校验省市区街道是否在库中存在避免脏数据入库。校验逻辑就是拿 adcode 去查库查不到就提示用户重新选择。这个校验放在前端做体验最好后端再做一次兜底。从那以后我每次拿到新的地址数据都强制走一遍“位数检查—孤儿节点检查—导入对账—缓存刷新”这四步少一步都可能埋雷。希望帮到你。本文还有配套的精品资源点击获取