Python 拉取 C&SD 官方 API:香港 2022 年已跨过“超老龄线“,而抚养比正在爬回 1961

文章目录

    • [1. 结论先行](#1. 结论先行)
    • [2. 环境信息](#2. 环境信息)
    • [3. 数据与口径:一个隐藏很深的 API](#3. 数据与口径:一个隐藏很深的 API)
    • [4. 核心代码](#4. 核心代码)
    • [5. 运行结果:三个数字讲完 65 年](#5. 运行结果:三个数字讲完 65 年)
    • [6. 可视化:交叉的结构与 V 形的抚养比](#6. 可视化:交叉的结构与 V 形的抚养比)
    • [7. 抚养比 33→50 意味着什么](#7. 抚养比 33→50 意味着什么)
    • [8. 踩坑与避坑](#8. 踩坑与避坑)
    • [9. 总结](#9. 总结)
    • 参考链接

1. 结论先行

"香港老龄化"是个天天被说的词,但多数人说不清它走到哪一步了。用政府统计处(C&SD)官方 API 拉全序列(1961-2026,65 年),三个实测结论:65 岁以上人口占比 1961 年只有 3.2%,2026 年已达 24.4%,其中 2022 年首次跨过 20% 的"超老龄社会"线 ;0-14 岁儿童占比从 40.5% 一路跌到 9.2%;而最反直觉的是总抚养比走了一条 V 形曲线------1961 年 78,2011 年跌到历史低点 33,2026 年回升到 50,正在爬回起点

"老龄化"三个字背后其实是两个方向相反的过程:老人占比在涨,少儿占比在崩,两者相加的抚养比反而先降后升。单看任何一个数字都会误判,这也是值得用代码把全序列拉出来算一遍的原因。

2. 环境信息

  • Python 3.13,只用标准库(urllib / json)+ matplotlib,零第三方依赖
  • 数据源:C&SD 官方 JSON API censtatd.gov.hk/api/get.php?id=110-01001&lang=en&full_series=1(Table 110-01001:按性别和年龄组的人口,半年频,1961 年 6 月至 2026 年 6 月,单次返回 3.5MB / 11,154 条)
  • 口径:取每年 6 月(年中)观测、性别合计;抚养比 =(0-14 + 65+)÷ 15-64 × 100

3. 数据与口径:一个隐藏很深的 API

统计处网页表格(web_table.html)看着像个下载页,download_csv=1 参数返回的其实是动态 HTML 壳------真正数据在 api/get.php 这个 GET 端点上,返回结构化 JSON:dataSet 数组每条记录带 SEX(空=合计)、AGE(18 个五岁组)、period(如 202606)、figure(千人)。这个端点不需要任何鉴权,比第三方转载干净一个量级,建议收藏备用

两个口径决定结论对错:一是取年中而非年末 (人口估计的官方口径);二是统计单位是年龄组不是年份------18 个组先映射到"少儿(0-14)/ 劳动年龄(15-64)/ 老年(65+)"三个桶,再算占比。

4. 核心代码

python 复制代码
import json, urllib.request

URL = "https://www.censtatd.gov.hk/api/get.php?id=110-01001&lang=en&full_series=1"

def age_group(age: str) -> str:
    if age in ("", "Total"):
        return "skip"
    if age == "85_and_over" or int(age.split("-")[0]) >= 65:
        return "elderly"
    if int(age.split("-")[0]) <= 14:
        return "young"
    return "working"

ds = json.loads(urllib.request.urlopen(
    urllib.request.Request(URL, headers={"User-Agent": "Mozilla/5.0"}),
    timeout=60).read().decode("utf-8"))["dataSet"]

years = {}
for e in ds:
    if e["SEX"] != "" or not e["period"].endswith("06"):   # 合计 + 年中
        continue
    year = int(e["period"][:4])
    band = age_group(e["AGE"])
    if band == "skip":
        continue
    bucket = years.setdefault(year, {})      # 注意:不能写 years[year] 于同句右侧
    bucket[band] = bucket.get(band, 0) + e["figure"]

rows = []
for y in sorted(years):
    b = years[y]
    total = b["young"] + b["working"] + b["elderly"]
    rows.append({"year": y, "total": total,
                 "elderly_share": b["elderly"] / total * 100,
                 "dependency": (b["young"] + b["elderly"]) / b["working"] * 100})

d = {r["year"]: r for r in rows}
print(f"65+ share: {d[1961]['elderly_share']:.1f}% -> {d[2026]['elderly_share']:.1f}%")
print(f"dependency: {d[1961]['dependency']:.0f} -> {d[2026]['dependency']:.0f}")

注释里那行必须强调:d[k1][k2] = d[k1]... 这种"同一句里先创建再读取"的写法,Python 会先算右侧再建键,首次运行直接 KeyError------这是本次实测踩到的真坑,代码审查都未必看得出来。这段分组聚合套路可直接复用到任何官方统计 API,建议收藏备用。

5. 运行结果:三个数字讲完 65 年

复制代码
years: 1961 -> 2026 (66 years)
total: 3,267,900 -> 7,618,000
65+ share: 3.2% -> 24.4%
0-14 share: 40.5% -> 9.2%
dependency ratio: 78 -> 50
first year 65+ >= 15%: 2015 (15.3%)
first year 65+ >= 20%: 2022 (20.8%)
2011->2021: 65+ 13.3% -> 19.6% (+6.3pp), dependency 33 -> 44
2021->2026: 65+ 19.6% -> 24.4% (+4.8pp), dependency 44 -> 50

三个看点:一,2022 年是分水岭 ------65+ 占比首次过 20%,按国际通行标准香港进入"超老龄社会",且 2015 到 2022 只用了 7 年从 15% 走到 20%(多数经济体要走几十年)。二,儿童占比 40.5% → 9.2% ,1961 年四个香港人里一个多小孩,现在十个人里不到一个。三,增速还在加速:最近五年 65+ 又涨了 4.8 个百分点,没有放缓迹象。

还有一个容易被忽略的结构信号:65+ 的人口基数不光是占比在涨,绝对人数也在涨------占比 24.4% × 总人口 761.8 万,约 186 万人的 65+ 群体,是 1961 年(约 10.5 万人)的近 18 倍。讨论"老龄化"时,占比决定趋势,绝对人数决定资源体量,两个口径需要分开看。

6. 可视化:交叉的结构与 V 形的抚养比

第一张图双轴看结构交叉:总量柱缓慢爬升,红线(65+)与绿线(0-14)在 2010 年代中期交叉后越拉越开。第二张是全文最有信息量的图:抚养比 1961 年 78 → 2011 年 33 → 2026 年 50 ------1961 年的高抚养比是"孩子多"撑起来的,2026 年的高抚养比是"老人多"撑起来的,同一个数字背后是完全不同的社会。这段双轴+双序列的画图逻辑值得收藏,分析结构变化先把"看起来相似"的两个数字拆开

7. 抚养比 33→50 意味着什么

2011 年前后是抚养比的历史低点:每 100 个劳动年龄人口只需养 33 个被抚养人。此后 15 年涨到 50,按 2021→2026 的 +6/5年 外推,2030 年代中期就会突破 60、奔向 1961 年的水平------但结构已经完全不同:1961 年的 78 里大头是孩子(教育支出),未来的 60+ 里大头是老人(医疗与福利支出),后者的单位成本远高于前者。比较抚养比时必须同时看构成,这是"数字相同、含义相反"的典型案例。

对个人而言,这条曲线的读法是倒过来的:劳动年龄人口占比的高点已经过去,未来若干年"每 100 人养 50-60 人"会成为常态------这不是危言,而是一个可以提前做算术的确定趋势:退休储蓄的年数、医疗支出的预期、甚至"和上一代比该几岁买房",都应该按这条曲线而不是按直觉来摆位置。

8. 踩坑与避坑

踩坑 后果 避开姿势
d[k1][k2] = d[k1]... 同句先建键再读取 首次运行 KeyError(右侧先求值) bucket = years.setdefault(year, {}) 再累加
用 web_table.html 的 download_csv 参数 返回动态 HTML 壳,不是 CSV api/get.php?id=表号 的 JSON 端点
把半年频数据全画进图 一年两个点,曲线毛刺 过滤 period.endswith("06") 只取年中
85_and_over 当普通区间解析 int("85_and_over") 崩溃 特判再统一映射
只看 65+ 不看构成 抚养比结论完全相反 少儿/劳动/老年三桶分开算

这张表建议收藏,尤其第一行------右侧先求值是那种"写的时候感觉良好、跑的时候莫名其妙"的坑。

9. 总结

65 年官方数据把"香港老龄化"从一句口号变成三条可检验的曲线:65+ 占比 3.2%→24.4%(2022 年过超老龄线)、儿童占比 40.5%→9.2%、抚养比 V 形反转正在爬回 1961。社会数据分析的价值不在预测某个精确数字,而在把"感觉"和"结构"分开------这三条曲线里,最值得盯的不是老人占比,而是抚养比拐点之后的斜率。

代码、API 端点、口径定义全部可复现,换一张统计处表号就能分析另一个主题。这篇也收录进「CodingPlan·八月创作之星博客挑战赛」九月第一篇。

本文为原创技术实践,数据来自香港政府统计处官方 API(Table 110-01001,2026-09 检索,年中口径);"超老龄社会"按国际通行 65+ 占比 20% 标准;不构成任何社会政策判断。

参考链接

  1. C&SD Web Table 110-01001(数据集页):https://www.censtatd.gov.hk/en/web_table.html?id=110-01001
  2. data.gov.hk 数据集入口:https://data.gov.hk/en-data/dataset/hk-censtatd-tablechart-110-01001
  3. C&SD 统计表 API 说明(web_table 页内 API Example 区块)
相关推荐
2601_9623824313 分钟前
Python零基础入门,看完直接上手写代码
python·机器学习·编程语言·数据科学·入门教程
信誓旦旦的程序猿14 分钟前
【零依赖量化数据实战 #25】北交所技术指标与基本面
java·人工智能·python·股票数据api·股票数据·股票数据api接口·股票api数据接口
2601_9669496516 分钟前
Python 量化开发为什么适合使用金融数据 SDK?从数据获取到策略研究的工程化实践
开发语言·python·数据分析·量化交易·股票数据·quantdash
科技小E17 分钟前
训完怎么带走?AI模型私有化部署平台DLTM模型导出ONNX/PyTorch与离线部署跑遍产线边缘
人工智能·pytorch·python
晓窗科技17 分钟前
专业的AI基座公司
人工智能·python
花间相见18 分钟前
【LangChain组件02】—— create_agent函数详解
python
程序员良辰29 分钟前
服务器 JDK 环境变量配置:一键安装和手动配置有什么区别?
开发语言·python
nanawinona32 分钟前
先跑通小流程,再让 AI 和 Python 承接复杂量化
人工智能·python