广告位 · 页头(后台-模块管理-新建模块后替换此区域)

微信公众号网页授权拿到的 province 和 city 是什么行政级别?

在微信公众号开发里,网页授权是获取用户身份信息的常用方式。走完 code 换取 access_token、再调用用户信息接口的流程后,返回的 JSON 中会包含 province 和 city 两个字段。不少业务会直接拿这两个字段做地区展示或区域判断,但很多人对它们的行政级别存在误解:以为 city 一定对应地级市,或者以为能精确到区县。实际上这两个字段只是用户个人资料中填写的地区文本,不包含行政区划代码,而且直辖市、省直辖县等场景会让级别判断更复杂。理解字段的真实含义,才能避免在统计、分单、风控等环节出现错误。

微信公众号网页授权拿到的 province 和 city 是什么行政级别?

从数据源看,province 和 city 来自微信个人资料里的地区项。用户在微信里可以选择国家、省、市,但没有区县这一级,因此拿到的 city 通常是地级市或同级别的地区、自治州、盟,个别的省直辖县级市也可能出现。接下来分别从返回结构、行政级别判断、代码解析和业务适配几个角度展开。

一、字段来源与返回结构

微信公众号网页授权采用的是 OAuth2.0 流程。用户点击授权后,开发者先用 code 换取网页授权 access_token,再调用 sns/userinfo 接口拉取用户信息。接口返回的 JSON 里与地理位置相关的字段有三个:country、province 和 city。其中 country 通常是中国,province 返回用户资料里选择的省份,city 返回用户资料里选择的城市。

下面是一个标准的返回示例,可以看到这些字段的值都是简短的中文字符串,并没有附带行政区划代码。

{
  "openid": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M",
  "nickname": "示例用户",
  "sex": 0,
  "province": "广东",
  "city": "深圳",
  "country": "中国",
  "headimgurl": "https://thirdwx.qlogo.cn/example",
  "privilege": []
}

这里需要特别留意,province 和 city 都是用户自己填写或选择的结果,并不是微信通过定位生成的。如果用户从未完善个人资料,或者只填写了国家,那么这两个字段很可能返回空字符串。开发者不能假定获取到的值一定存在,更不能把这些值当作权威的行政区划依据。

二、province 与 city 的实际行政级别

从民政部的行政区划体系看,中国内地的行政区划分为省级、地级、县级、乡级。微信个人资料里的地区选择只有国家和省级、市级两级,并没有开放到区县。因此 province 字段对应的是省级行政区,包括省、自治区、直辖市和特别行政区;city 字段多数情况下对应的是地级行政区,例如地级市、地区、自治州、盟。

但这个对应关系并不绝对。首先,直辖市用户的个人资料里,province 可能是北京、上海、天津或重庆,而 city 可能也是相同的名称,或者为空。也就是说,直辖市的 city 并不代表一个独立的地级市,它只是资料里城市一栏的文本,甚至可能存在重复或缺失。其次,部分省直辖县级市在微信城市列表中也会被当作城市返回,例如河南的济源、海南的文昌。它们实际行政级别是县级,但字段名却叫 city。

下面用一张表展示几种典型情况下的判断结果。

province 值city 值实际行政级别判断
广东深圳地级市
北京北京直辖市(省级)
海南文昌省直辖县级市
内蒙古阿拉善盟(地级)

由此可见,仅靠字段名称本身,无法百分之百确定返回的城市到底属于哪一级。必须结合省份上下文和本地维护的行政区划字典来辅助判断。这也是很多做区域统计、分单或风控的系统容易踩坑的地方。

三、服务端解析与行政级别判断代码

下面给出一个 Python 示例,演示如何调用微信用户信息接口,并对返回的 province 和 city 做一个基础的行政级别判断。示例基于 requests 库,假设已经通过 code 换取了 access_token,并且拿到了 openid。

import requests
def get_wechat_user_info(access_token, openid):
    url = "https://api.weixin.qq.com/sns/userinfo"
    params = {
        "access_token": access_token,
        "openid": openid,
        "lang": "zh_CN"
    }
    resp = requests.get(url, params=params, timeout=5)
    resp.raise_for_status()
    return resp.json()
def parse_region(data):
    province = data.get("province", "")
    city = data.get("city", "")
    country = data.get("country", "")
    if country != "中国":
        return "境外或港澳台"
    municipalities = {"北京", "上海", "天津", "重庆"}
    county_level_cities = {"济源", "仙桃", "潜江", "天门", "文昌", "琼海", "万宁", "东方"}
    if province in municipalities:
        if city == "" or city == province:
            return "直辖市(省级)"
        else:
            return "直辖市下辖区域,但非标准区县"
    if city in county_level_cities:
        return "省直辖县级市"
    if city != "":
        return "地级市或同级行政区"
    return "省份未知城市"

这段代码先判断国家是否为“中国”,如果不是则直接归类为境外或港澳台。接着处理直辖市,因为直辖市在行政级别上属于省级,不能按普通地级市对待。随后用一个简易的省直辖县级市集合来判断部分城市,最后剩下的非空 city 才按地级市或同级行政区处理。需要说明的是,示例中的县级市列表并不完整,生产环境应当结合民政部公布的最新行政区划数据来维护。

此外,如果城市名称存在重名或同名不同省的情况,比如“鼓楼”这类区名不会出现在 city 字段,但少数县级市名称可能与地级市同名,仍建议把省份和城市放在一起做联合匹配,避免误判。

四、字段缺失与业务适配建议

实际接入时还会遇到 province 和 city 缺失的场景。用户可能从未在个人资料里填写地区,也可能由于微信隐私策略调整或接口返回变化导致字段为空。对于海外用户,country 可能不是中国,此时国内省市级判断就没有意义。因此业务逻辑必须先处理空值和境外情况。

如果产品目标是展示用户所在城市,这两个字段可以作为一个低成本的参考来源,但一定要加兜底文案,比如“用户未填写地区”。如果产品需要精确到区县,或者需要用地区做派单、库存、风控等强依赖逻辑,那么仅靠网页授权字段远远不够。更可靠的方式是引导用户进入地图选点或使用小程序里的定位能力,由用户主动授权定位并生成详细地址。

对于需要将城市名称标准化的系统,建议维护一张行政区划映射表,包含省份、城市、行政级别、行政区划代码等字段,并把微信返回的 province 和 city 作为输入进行模糊或精确匹配。匹配不上的数据进入人工审核或递归向上归类,这样既保留用户资料的原始值,又能在业务侧得到较为准确的行政级别。

总体来看,province 与 city 字段能提供粗略的省份和城市信息,但行政级别并不严格。开发时应将它们视为展示辅助数据,而不是精确地理位置数据,结合行政区划字典和产品场景做适配,才能避免后续出现统计偏差或逻辑错误。

广告位 · 文章底部(后台-模块管理替换此区域)