Skill: brucelau1987cn/china-weather-query (version 1.1.0)

--- name: china-weather description: 查询中国任意城市的实时天气、多日预报和生活指数。数据来源中国天气网（weather.com.cn）。当用户询问中国城市天气、气温、降雨、空气质量、穿衣建议、出行建议等天气相关问题时使用。支持全国 350 个城市。 --- # 中国天气查询 ## 使用方法 运行查询脚本： ```bash python3 <skill_dir>/scripts/weather_query.py <城市名> [--days N] [--detail] [--json] ``` ### 参数 - `城市名` — 城市名称，支持模糊匹配（如"杭"、"杭州"都能匹配） - `--days N` — 预报天数，默认 1 天，最大 5 天 - `--detail` — 显示生活指数（穿衣、紫外线、雨伞、洗车等） - `--json` — 输出原始 JSON 数据 ### 示例 ```bash # 上海实时天气 python3 scripts/weather_query.py 上海 # 北京 3 天预报 python3 scripts/weather_query.py 北京 --days 3 # 成都详细天气（含生活指数） python3 scripts/weather_query.py 成都 --days 2 --detail # 输出 JSON python3 scripts/weather_query.py 广州 --json ``` ## 数据内容 - **实况**：气温、天气、风力、湿度、AQI、PM2.5、能见度、气压 - **预报**：5 天内天气趋势（白天/夜间天气、最高/最低温、风向风力） - **生活指数**：穿衣、紫外线、雨伞、洗车、感冒、旅游、运动、空调、舒适度、晾晒 - **预警**：气象预警信号（蓝色/黄色/橙色/红色） ## 覆盖范围 全国 34 个省级区划，350 个城市。编码表见 `references/cities.json`。 ## 数据来源 中国天气网（weather.com.cn）→ 中央气象台，每日 06/08/12/16/20 时更新。 ## 注意 - 需要网络访问 `d1.weather.com.cn` - HTTP 接口需设置 `Referer: http://www.weather.com.cn/`

path: _meta.json
content: ownerId: kn71gt5phaerkthybwhz39kqv18540vy
slug: china-weather-query
version: 1.1.0
publishedAt: 1776570439378

path: references/cities.json
content: prov: 北京
city: 北京
code: 101010100

prov: 上海
city: 上海
code: 101020100

prov: 天津
city: 天津
code: 101030100

prov: 重庆
city: 重庆
code: 101040100

prov: 黑龙江
city: 哈尔滨
code: 101050101

prov: 黑龙江
city: 齐齐哈尔
code: 101050201

prov: 黑龙江
city: 牡丹江
code: 101050301

prov: 黑龙江
city: 佳木斯
code: 101050401

prov: 黑龙江
city: 绥化
code: 101050501

prov: 黑龙江
city: 黑河
code: 101050601

prov: 黑龙江
city: 大兴安岭
code: 101050701

prov: 黑龙江
city: 伊春
code: 101050801

prov: 黑龙江
city: 大庆
code: 101050901

prov: 黑龙江
city: 七台河
code: 101051001

prov: 黑龙江
city: 鸡西
code: 101051101

prov: 黑龙江
city: 鹤岗
code: 101051201

prov: 黑龙江
city: 双鸭山
code: 101051301

prov: 吉林
city: 长春
code: 101060101

prov: 吉林
city: 吉林
code: 101060201

prov: 吉林
city: 延边
code: 101060301

prov: 吉林
city: 四平
code: 101060401

prov: 吉林
city: 通化
code: 101060501

prov: 吉林
city: 白城
code: 101060601

prov: 吉林
city: 辽源
code: 101060701

prov: 吉林
city: 松原
code: 101060801

prov: 吉林
city: 白山
code: 101060901

prov: 辽宁
city: 沈阳
code: 101070101

prov: 辽宁
city: 大连
code: 101070201

prov: 辽宁
city: 鞍山
code: 101070301

prov: 辽宁
city: 抚顺
code: 101070401

prov: 辽宁
city: 本溪
code: 101070501

prov: 辽宁
city: 丹东
code: 101070601

prov: 辽宁
city: 锦州
code: 101070701

prov: 辽宁
city: 营口
code: 101070801

prov: 辽宁
city: 阜新
code: 101070901

prov: 辽宁
city: 辽阳
code: 101071001

prov: 辽宁
city: 铁岭
code: 101071101

prov: 辽宁
city: 朝阳
code: 101071201

prov: 辽宁
city: 盘锦
code: 101071301

prov: 辽宁
city: 葫芦岛
code: 101071401

prov: 内蒙古
city: 呼和浩特
code: 101080101

prov: 内蒙古
city: 包头
code: 101080201

prov: 内蒙古
city: 乌海
code: 101080301

prov: 内蒙古
city: 乌兰察布
code: 101080401

prov: 内蒙古
city: 通辽
code: 101080501

prov: 内蒙古
city: 赤峰
code: 101080601

prov: 内蒙古
city: 鄂尔多斯
code: 101080701

prov: 内蒙古
city: 巴彦淖尔
code: 101080801

prov: 内蒙古
city: 锡林郭勒
code: 101080901

prov: 内蒙古
city: 呼伦贝尔
code: 101081001

prov: 内蒙古
city: 兴安盟
code: 101081101

prov: 内蒙古
city: 阿拉善盟
code: 101081201

prov: 河北
city: 石家庄
code: 101090101

prov: 河北
city: 保定
code: 101090201

prov: 河北
city: 张家口
code: 101090301

prov: 河北
city: 承德
code: 101090401

prov: 河北
city: 唐山
code: 101090501

prov: 河北
city: 廊坊
code: 101090601

prov: 河北
city: 沧州
code: 101090701

prov: 河北
city: 衡水
code: 101090801

prov: 河北
city: 邢台
code: 101090901

prov: 河北
city: 邯郸
code: 101091001

prov: 河北
city: 秦皇岛
code: 101091101

prov: 河北
city: 雄安新区
code: 101091201

prov: 山西
city: 太原
code: 101100101

prov: 山西
city: 大同
code: 101100201

prov: 山西
city: 阳泉
code: 101100301

prov: 山西
city: 晋中
code: 101100401

prov: 山西
city: 长治
code: 101100501

prov: 山西
city: 晋城
code: 101100601

prov: 山西
city: 临汾
code: 101100701

prov: 山西
city: 运城
code: 101100801

prov: 山西
city: 朔州
code: 101100901

prov: 山西
city: 忻州
code: 101101001

prov: 山西
city: 吕梁
code: 101101101

prov: 陕西
city: 西安
code: 101110101

prov: 陕西
city: 咸阳
code: 101110201

prov: 陕西
city: 延安
code: 101110301

prov: 陕西
city: 榆林
code: 101110401

prov: 陕西
city: 渭南
code: 101110501

prov: 陕西
city: 商洛
code: 101110601

prov: 陕西
city: 安康
code: 101110701

prov: 陕西
city: 汉中
code: 101110801

prov: 陕西
city: 宝鸡
code: 101110901

prov: 陕西
city: 铜川
code: 101111001

prov: 陕西
city: 杨凌
code: 101111101

prov: 山东
city: 济南
code: 101120101

prov: 山东
city: 青岛
code: 101120201

prov: 山东
city: 淄博
code: 101120301

prov: 山东
city: 德州
code: 101120401

prov: 山东
city: 烟台
code: 101120501

prov: 山东
city: 潍坊
code: 101120601

prov: 山东
city: 济宁
code: 101120701

prov: 山东
city: 泰安
code: 101120801

prov: 山东
city: 临沂
code: 101120901

prov: 山东
city: 菏泽
code: 101121001

prov: 山东
city: 滨州
code: 101121101

prov: 山东
city: 东营
code: 101121201

prov: 山东
city: 威海
code: 101121301

prov: 山东
city: 枣庄
code: 101121401

prov: 山东
city: 日照
code: 101121501

prov: 山东
city: 莱芜
code: 101121601

prov: 山东
city: 聊城
code: 101121701

prov: 新疆
city: 乌鲁木齐
code: 101130101

prov: 新疆
city: 克拉玛依
code: 101130201

prov: 新疆
city: 石河子
code: 101130301

prov: 新疆
city: 昌吉
code: 101130401

prov: 新疆
city: 吐鲁番
code: 101130501

prov: 新疆
city: 巴州
code: 101130601

prov: 新疆
city: 阿拉尔
code: 101130701

prov: 新疆
city: 阿克苏
code: 101130801

prov: 新疆
city: 喀什
code: 101130901

prov: 新疆
city: 伊犁
code: 101131001

prov: 新疆
city: 塔城
code: 101131101

prov: 新疆
city: 哈密
code: 101131201

prov: 新疆
city: 和田
code: 101131301

prov: 新疆
city: 阿勒泰
code: 101131401

prov: 新疆
city: 克州
code: 101131501

prov: 新疆
city: 博州
code: 101131601

prov: 西藏
city: 拉萨
code: 101140101

prov: 西藏
city: 日喀则
code: 101140201

prov: 西藏
city: 山南
code: 101140301

prov: 西藏
city: 林芝
code: 101140401

prov: 西藏
city: 昌都
code: 101140501

prov: 西藏
city: 那曲
code: 101140601

prov: 西藏
city: 阿里
code: 101140701

prov: 青海
city: 西宁
code: 101150101

prov: 青海
city: 海东
code: 101150201

prov: 青海
city: 黄南
code: 101150301

prov: 青海
city: 海南
code: 101150401

prov: 青海
city: 果洛
code: 101150501

prov: 青海
city: 玉树
code: 101150601

prov: 青海
city: 海西
code: 101150701

prov: 青海
city: 海北
code: 101150801

prov: 青海
city: 格尔木
code: 101150901

prov: 甘肃
city: 兰州
code: 101160101

prov: 甘肃
city: 定西
code: 101160201

prov: 甘肃
city: 平凉
code: 101160301

prov: 甘肃
city: 庆阳
code: 101160401

prov: 甘肃
city: 武威
code: 101160501

prov: 甘肃
city: 金昌
code: 101160601

prov: 甘肃
city: 张掖
code: 101160701

prov: 甘肃
city: 酒泉
code: 101160801

prov: 甘肃
city: 天水
code: 101160901

prov: 甘肃
city: 陇南
code: 101161001

prov: 甘肃
city: 临夏
code: 101161101

prov: 甘肃
city: 甘南
code: 101161201

prov: 甘肃
city: 白银
code: 101161301

prov: 甘肃
city: 嘉峪关
code: 101161401

prov: 宁夏
city: 银川
code: 101170101

prov: 宁夏
city: 石嘴山
code: 101170201

prov: 宁夏
city: 吴忠
code: 101170301

prov: 宁夏
city: 固原
code: 101170401

prov: 宁夏
city: 中卫
code: 101170501

prov: 河南
city: 郑州
code: 101180101

prov: 河南
city: 安阳
code: 101180201

prov: 河南
city: 新乡
code: 101180301

prov: 河南
city: 许昌
code: 101180401

prov: 河南
city: 平顶山
code: 101180501

prov: 河南
city: 信阳
code: 101180601

prov: 河南
city: 南阳
code: 101180701

prov: 河南
city: 开封
code: 101180801

prov: 河南
city: 洛阳
code: 101180901

prov: 河南
city: 商丘
code: 101181001

prov: 河南
city: 焦作
code: 101181101

prov: 河南
city: 鹤壁
code: 101181201

prov: 河南
city: 濮阳
code: 101181301

prov: 河南
city: 周口
code: 101181401

prov: 河南
city: 漯河
code: 101181501

prov: 河南
city: 驻马店
code: 101181601

prov: 河南
city: 三门峡
code: 101181701

prov: 河南
city: 济源
code: 101181801

prov: 江苏
city: 南京
code: 101190101

prov: 江苏
city: 无锡
code: 101190201

prov: 江苏
city: 镇江
code: 101190301

prov: 江苏
city: 苏州
code: 101190401

prov: 江苏
city: 南通
code: 101190501

prov: 江苏
city: 扬州
code: 101190601

prov: 江苏
city: 盐城
code: 101190701

prov: 江苏
city: 徐州
code: 101190801

prov: 江苏
city: 淮安
code: 101190901

prov: 江苏
city: 连云港
code: 101191001

prov: 江苏
city: 常州
code: 101191101

prov: 江苏
city: 泰州
code: 101191201

prov: 江苏
city: 宿迁
code: 101191301

prov: 湖北
city: 武汉
code: 101200101

prov: 湖北
city: 襄阳
code: 101200201

prov: 湖北
city: 鄂州
code: 101200301

prov: 湖北
city: 孝感
code: 101200401

prov: 湖北
city: 黄冈
code: 101200501

prov: 湖北
city: 黄石
code: 101200601

prov: 湖北
city: 咸宁
code: 101200701

prov: 湖北
city: 荆州
code: 101200801

prov: 湖北
city: 宜昌
code: 101200901

prov: 湖北
city: 恩施
code: 101201001

prov: 湖北
city: 十堰
code: 101201101

prov: 湖北
city: 神农架
code: 101201201

prov: 湖北
city: 随州
code: 101201301

prov: 湖北
city: 荆门
code: 101201401

prov: 湖北
city: 天门
code: 101201501

prov: 湖北
city: 仙桃
code: 101201601

prov: 湖北
city: 潜江
code: 101201701

prov: 浙江
city: 杭州
code: 101210101

prov: 浙江
city: 湖州
code: 101210201

prov: 浙江
city: 嘉兴
code: 101210301

prov: 浙江
city: 宁波
code: 101210401

prov: 浙江
city: 绍兴
code: 101210501

prov: 浙江
city: 台州
code: 101210601

prov: 浙江
city: 温州
code: 101210701

prov: 浙江
city: 丽水
code: 101210801

prov: 浙江
city: 金华
code: 101210901

prov: 浙江
city: 衢州
code: 101211001

prov: 浙江
city: 舟山
code: 101211101

prov: 安徽
city: 合肥
code: 101220101

prov: 安徽
city: 蚌埠
code: 101220201

prov: 安徽
city: 芜湖
code: 101220301

prov: 安徽
city: 淮南
code: 101220401

prov: 安徽
city: 马鞍山
code: 101220501

prov: 安徽
city: 安庆
code: 101220601

prov: 安徽
city: 宿州
code: 101220701

prov: 安徽
city: 阜阳
code: 101220801

prov: 安徽
city: 亳州
code: 101220901

prov: 安徽
city: 黄山
code: 101221001

prov: 安徽
city: 滁州
code: 101221101

prov: 安徽
city: 淮北
code: 101221201

prov: 安徽
city: 铜陵
code: 101221301

prov: 安徽
city: 宣城
code: 101221401

prov: 安徽
city: 六安
code: 101221501

prov: 安徽
city: 池州
code: 101221701

prov: 福建
city: 福州
code: 101230101

prov: 福建
city: 厦门
code: 101230201

prov: 福建
city: 宁德
code: 101230301

prov: 福建
city: 莆田
code: 101230401

prov: 福建
city: 泉州
code: 101230501

prov: 福建
city: 漳州
code: 101230601

prov: 福建
city: 龙岩
code: 101230701

prov: 福建
city: 三明
code: 101230801

prov: 福建
city: 南平
code: 101230901

prov: 江西
city: 南昌
code: 101240101

prov: 江西
city: 九江
code: 101240201

prov: 江西
city: 上饶
code: 101240301

prov: 江西
city: 抚州
code: 101240401

prov: 江西
city: 宜春
code: 101240501

prov: 江西
city: 吉安
code: 101240601

prov: 江西
city: 赣州
code: 101240701

prov: 江西
city: 景德镇
code: 101240801

prov: 江西
city: 萍乡
code: 101240901

prov: 江西
city: 新余
code: 101241001

prov: 江西
city: 鹰潭
code: 101241101

prov: 湖南
city: 长沙
code: 101250101

prov: 湖南
city: 湘潭
code: 101250201

prov: 湖南
city: 株洲
code: 101250301

prov: 湖南
city: 衡阳
code: 101250401

prov: 湖南
city: 郴州
code: 101250501

prov: 湖南
city: 常德
code: 101250601

prov: 湖南
city: 益阳
code: 101250701

prov: 湖南
city: 娄底
code: 101250801

prov: 湖南
city: 邵阳
code: 101250901

prov: 湖南
city: 岳阳
code: 101251001

prov: 湖南
city: 张家界
code: 101251101

prov: 湖南
city: 怀化
code: 101251201

prov: 湖南
city: 永州
code: 101251401

prov: 湖南
city: 湘西
code: 101251501

prov: 贵州
city: 贵阳
code: 101260101

prov: 贵州
city: 遵义
code: 101260201

prov: 贵州
city: 安顺
code: 101260301

prov: 贵州
city: 黔南
code: 101260401

prov: 贵州
city: 黔东南
code: 101260501

prov: 贵州
city: 铜仁
code: 101260601

prov: 贵州
city: 毕节
code: 101260701

prov: 贵州
city: 六盘水
code: 101260801

prov: 贵州
city: 黔西南
code: 101260901

prov: 四川
city: 成都
code: 101270101

prov: 四川
city: 攀枝花
code: 101270201

prov: 四川
city: 自贡
code: 101270301

prov: 四川
city: 绵阳
code: 101270401

prov: 四川
city: 南充
code: 101270501

prov: 四川
city: 达州
code: 101270601

prov: 四川
city: 遂宁
code: 101270701

prov: 四川
city: 广安
code: 101270801

prov: 四川
city: 巴中
code: 101270901

prov: 四川
city: 泸州
code: 101271001

prov: 四川
city: 宜宾
code: 101271101

prov: 四川
city: 内江
code: 101271201

prov: 四川
city: 资阳
code: 101271301

prov: 四川
city: 乐山
code: 101271401

prov: 四川
city: 眉山
code: 101271501

prov: 四川
city: 凉山
code: 101271601

prov: 四川
city: 雅安
code: 101271701

prov: 四川
city: 甘孜
code: 101271801

prov: 四川
city: 阿坝
code: 101271901

prov: 四川
city: 德阳
code: 101272001

prov: 四川
city: 广元
code: 101272101

prov: 广东
city: 广州
code: 101280101

prov: 广东
city: 韶关
code: 101280201

prov: 广东
city: 惠州
code: 101280301

prov: 广东
city: 梅州
code: 101280401

prov: 广东
city: 汕头
code: 101280501

prov: 广东
city: 深圳
code: 101280601

prov: 广东
city: 珠海
code: 101280701

prov: 广东
city: 佛山
code: 101280801

prov: 广东
city: 肇庆
code: 101280901

prov: 广东
city: 湛江
code: 101281001

prov: 广东
city: 江门
code: 101281101

prov: 广东
city: 河源
code: 101281201

prov: 广东
city: 清远
code: 101281301

prov: 广东
city: 云浮
code: 101281401

prov: 广东
city: 潮州
code: 101281501

prov: 广东
city: 东莞
code: 101281601

prov: 广东
city: 中山
code: 101281701

prov: 广东
city: 阳江
code: 101281801

prov: 广东
city: 揭阳
code: 101281901

prov: 广东
city: 茂名
code: 101282001

prov: 广东
city: 汕尾
code: 101282101

prov: 云南
city: 昆明
code: 101290101

prov: 云南
city: 大理
code: 101290201

prov: 云南
city: 红河
code: 101290301

prov: 云南
city: 曲靖
code: 101290401

prov: 云南
city: 保山
code: 101290501

prov: 云南
city: 文山
code: 101290601

prov: 云南
city: 玉溪
code: 101290701

prov: 云南
city: 楚雄
code: 101290801

prov: 云南
city: 普洱
code: 101290901

prov: 云南
city: 昭通
code: 101291001

prov: 云南
city: 临沧
code: 101291101

prov: 云南
city: 怒江
code: 101291201

prov: 云南
city: 迪庆
code: 101291301

prov: 云南
city: 丽江
code: 101291401

prov: 云南
city: 德宏
code: 101291501

prov: 云南
city: 西双版纳
code: 101291601

prov: 广西
city: 南宁
code: 101300101

prov: 广西
city: 崇左
code: 101300201

prov: 广西
city: 柳州
code: 101300301

prov: 广西
city: 来宾
code: 101300401

prov: 广西
city: 桂林
code: 101300501

prov: 广西
city: 梧州
code: 101300601

prov: 广西
city: 贺州
code: 101300701

prov: 广西
city: 贵港
code: 101300801

prov: 广西
city: 玉林
code: 101300901

prov: 广西
city: 百色
code: 101301001

prov: 广西
city: 钦州
code: 101301101

prov: 广西
city: 河池
code: 101301201

prov: 广西
city: 北海
code: 101301301

prov: 广西
city: 防城港
code: 101301401

prov: 海南
city: 海南
code: 101310101

prov: 香港
city: 香港
code: 101320101

prov: 澳门
city: 澳门
code: 101330101

prov: 台湾
city: 台北
code: 101340101

prov: 台湾
city: 高雄
code: 101340201

prov: 台湾
city: 台中
code: 101340401

path: scripts/weather_query.py
content: #!/usr/bin/env python3
"""中国天气查询脚本 - 基于中国天气网API
用法: python3 weather_query.py <城市名> [--days N] [--detail]
"""

import json
import os
import sys
import re
import urllib.request
import urllib.error

CITIES_FILE = None  # 由调用方注入，或自动查找

def load_cities():
    """加载城市编码表"""
    global CITIES_FILE
    # 基于脚本所在目录推导 skill 根目录
    script_dir = os.path.dirname(os.path.abspath(__file__))
    skill_dir = os.path.dirname(script_dir)  # scripts/ -> skill root
    search_paths = [
        os.path.join(skill_dir, 'references', 'cities.json'),
        "/root/.openclaw/workspace/skills/china-weather-query/references/cities.json",
        "/root/.openclaw/workspace/skills/china-weather/references/cities.json",
        "/root/.openclaw/workspace/china_weather_cities.json",
    ]
    if CITIES_FILE:
        search_paths.insert(0, CITIES_FILE)
    for path in search_paths:
        try:
            with open(path, 'r') as f:
                return json.load(f)
        except:
            pass
    return None

def search_city(name):
    """搜索城市，返回编码"""
    cities = load_cities()
    if not cities:
        return None, None
    # 精确匹配
    for c in cities:
        if c['city'] == name:
            return c['code'], c['prov']
    # 模糊匹配
    for c in cities:
        if name in c['city'] or name in c['prov']:
            return c['code'], c['prov']
    return None, None

def fetch_weather(code):
    """从中国天气网获取天气数据"""
    url = f"http://d1.weather.com.cn/weather_index/{code}.html"
    req = urllib.request.Request(url)
    req.add_header('Referer', 'http://www.weather.com.cn/')
    try:
        with urllib.request.urlopen(req, timeout=8) as resp:
            data = resp.read().decode('utf-8')
        return parse_weather(data)
    except Exception as e:
        return {"error": str(e)}

def parse_weather(data):
    """解析天气数据"""
    result = {}

    # 实况 dataSK
    m = re.search(r'var dataSK\s*=\s*(\{[^;]+)', data)
    if m:
        try:
            sk = json.loads(m.group(1))
            result['current'] = {
                'city': sk.get('cityname', ''),
                'temp': sk.get('temp', ''),
                'weather': sk.get('weather', ''),
                'weather_en': sk.get('weathere', ''),
                'wind_dir': sk.get('WD', ''),
                'wind_speed': sk.get('WS', ''),
                'humidity': sk.get('sd', ''),
                'aqi': sk.get('aqi', ''),
                'pm25': sk.get('aqi_pm25', ''),
                'visibility': sk.get('njd', ''),
                'pressure': sk.get('qy', ''),
                'rain': sk.get('rain', ''),
                'rain_24h': sk.get('rain24h', ''),
                'time': sk.get('time', ''),
                'date': sk.get('date', ''),
            }
        except:
            pass

    # 今日预报 cityDZ
    m = re.search(r'var cityDZ\s*=\s*(\{[^;]+)', data)
    if m:
        try:
            dz = json.loads(m.group(1))['weatherinfo']
            result['today'] = {
                'city': dz.get('cityname', ''),
                'temp_high': dz.get('temp', ''),
                'temp_low': dz.get('tempn', ''),
                'weather': dz.get('weather', ''),
                'wind': dz.get('wd', ''),
                'wind_level': dz.get('ws', ''),
            }
        except:
            pass

    # 5天预报 fc
    m = re.search(r'var fc\s*=\s*(\{[^;]+)', data)
    if m:
        try:
            fc = json.loads(m.group(1))
            forecasts = []
            for day in fc.get('f', []):
                forecasts.append({
                    'date': day.get('fi', ''),
                    'day_label': day.get('fj', ''),
                    'weather_day': weather_code_to_text(day.get('fa', '')),
                    'weather_night': weather_code_to_text(day.get('fb', '')),
                    'temp_high': day.get('fc', ''),
                    'temp_low': day.get('fd', ''),
                    'wind_day': day.get('fe', ''),
                    'wind_night': day.get('ff', ''),
                    'wind_level_day': day.get('fg', ''),
                    'wind_level_night': day.get('fh', ''),
                })
            result['forecast'] = forecasts
        except:
            pass

    # 生活指数 dataZS — 数据是扁平结构，key 格式为 {prefix}_name/{prefix}_hint/{prefix}_des_s
    m = re.search(r'var dataZS\s*=\s*(\{[^;]+)', data)
    if m:
        try:
            zs_outer = json.loads(m.group(1))
            zs_data = zs_outer.get('zs', zs_outer)  # 数据在 .zs 子对象里
            indices = {}
            index_names = {
                'ct': '穿衣', 'uv': '紫外线', 'ys': '雨伞', 'xc': '洗车',
                'gm': '感冒', 'tr': '旅游', 'yd': '运动', 'ac': '空调',
                'co': '舒适度', 'ls': '晾晒', 'xq': '心情', 'pj': '啤酒',
                'jt': '交通', 'yh': '约会', 'gl': '太阳镜', 'ag': '过敏',
                'lk': '路况', 'cl': '晨练', 'nl': '夜生活',
                'gj': '逛街', 'pl': '空气污染扩散', 'fs': '防晒',
                'pp': '化妆', 'gz': '干燥',
            }
            for k, name in index_names.items():
                if f'{k}_name' in zs_data:
                    indices[name] = {
                        'hint': zs_data.get(f'{k}_hint', ''),
                        'desc': zs_data.get(f'{k}_des_s', ''),
                    }
            result['indices'] = indices
        except:
            pass

    # 预警 alarmDZ
    m = re.search(r'var alarmDZ\s*=\s*(\{[^;]+)', data)
    if m:
        try:
            alarm = json.loads(m.group(1))
            if alarm.get('w'):
                result['alarms'] = alarm['w']
        except:
            pass

    return result

def weather_code_to_text(code):
    """天气代码转文字"""
    codes = {
        '00': '晴', '01': '多云', '02': '阴', '03': '阵雨',
        '04': '雷阵雨', '05': '雷阵雨并伴有冰雹', '06': '雨夹雪',
        '07': '小雨', '08': '中雨', '09': '大雨', '10': '暴雨',
        '11': '大暴雨', '12': '特大暴雨', '13': '阵雪', '14': '小雪',
        '15': '中雪', '16': '大雪', '17': '暴雪', '18': '雾',
        '19': '冻雨', '20': '沙尘暴', '21': '小到中雨', '22': '中到大雨',
        '23': '大到暴雨', '24': '暴雨到大暴雨', '25': '大暴雨到特大暴雨',
        '26': '小到中雪', '27': '中到大雪', '28': '大到暴雪',
        '29': '浮尘', '30': '扬沙', '31': '强沙尘暴', '53': '霾',
        '99': '无',
    }
    return codes.get(code, '未知')

def format_output(data, days=1, detail=False):
    """格式化输出"""
    lines = []

    if 'error' in data:
        lines.append(f"❌ 查询失败: {data['error']}")
        return '\n'.join(lines)

    # 实况
    if 'current' in data:
        cur = data['current']
        lines.append(f"📍 {cur['city']} · {cur['date']} {cur['time']}")
        lines.append(f"🌡️ 实时气温: {cur['temp']}°C | {cur['weather']}")
        lines.append(f"💨 {cur['wind_dir']} {cur['wind_speed']} | 湿度 {cur['humidity']}")
        if cur.get('aqi') and cur['aqi'] != '':
            aqi = int(cur['aqi'])
            if aqi <= 50: aqi_label = '优 ✅'
            elif aqi <= 100: aqi_label = '良 🟡'
            elif aqi <= 150: aqi_label = '轻度污染 🟠'
            elif aqi <= 200: aqi_label = '中度污染 🔴'
            else: aqi_label = '重度污染 🟤'
            lines.append(f"📊 AQI: {aqi} ({aqi_label}) | PM2.5: {cur['pm25']}")
        if cur.get('visibility'):
            vis = cur.get('visibility', '').replace('km','')
            lines.append(f"👁️ 能见度: {vis}km | 气压: {cur['pressure']}hPa")

    # 预报
    if 'forecast' in data:
        fc = data['forecast'][:days]
        lines.append(f"\n📅 未来{len(fc)}天预报:")
        for day in fc:
            lines.append(f"\n  📆 {day['day_label']} ({day['date']})")
            lines.append(f"  🌤️ {day['weather_day']} 转 {day['weather_night']}")
            lines.append(f"  🌡️ {day['temp_high']}°C / {day['temp_low']}°C")
            lines.append(f"  💨 {day['wind_day']} {day['wind_level_day']}")

    # 生活指数
    if detail and 'indices' in data:
        lines.append(f"\n📋 生活指数:")
        for name, info in data['indices'].items():
            lines.append(f"  {name}: {info['hint']} — {info['desc']}")

    # 预警
    if 'alarms' in data and data['alarms']:
        lines.append(f"\n⚠️ 天气预警:")
        for alarm in data['alarms']:
            w_type = alarm.get('w5', '')
            w_level = alarm.get('w7', '')
            w_time = alarm.get('w8', '')
            w_title = alarm.get('w13', '')
            lines.append(f"  [{w_type}{w_level}预警] {w_title} ({w_time})")

    return '\n'.join(lines)

def main():
    if len(sys.argv) < 2:
        print("用法: python3 weather_query.py <城市名> [--days N] [--detail] [--json]")
        print("示例: python3 weather_query.py 上海")
        print("      python3 weather_query.py 北京 --days 3 --detail")
        print("      python3 weather_query.py 杭州 --json")
        sys.exit(1)

    city_name = sys.argv[1]
    days = 1
    detail = False
    json_output = False

    i = 2
    while i < len(sys.argv):
        if sys.argv[i] == '--days' and i + 1 < len(sys.argv):
            days = int(sys.argv[i + 1])
            i += 2
        elif sys.argv[i] == '--detail':
            detail = True
            i += 1
        elif sys.argv[i] == '--json':
            json_output = True
            i += 1
        else:
            i += 1

    # 搜索城市
    code, prov = search_city(city_name)
    if not code:
        # 尝试直接用输入作为编码
        if re.match(r'^\d{9}$', city_name):
            code = city_name
        else:
            print(f"❌ 未找到城市: {city_name}")
            sys.exit(1)

    if prov:
        print(f"🔍 匹配到: {prov} - {city_name if city_name != prov else ''}", file=sys.stderr)

    # 查询天气
    data = fetch_weather(code)

    if json_output:
        print(json.dumps(data, ensure_ascii=False, indent=2))
    else:
        print(format_output(data, days=days, detail=detail))

if __name__ == '__main__':
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)