新闻详情

新闻详情

首页 / 资讯中心 / 详情

Python采集中国天气网天气数据:JSON接口与城市ID实战

发布时间:2026/9/25 7:35:39来源:尧图网络
Python采集中国天气网天气数据:JSON接口与城市ID实战
简介资源包内含一套基于Visual Studio 2008的C# Windows Forms完整工程面向需要接入中国天气网公开API的初级开发者解决从构造HTTP请求、解析JSON/XML到界面展示的关键流程。压缩包共34个文件主要包括9个.cs源码文件、3个.dll库文件、3个.exe可执行程序以及配置文件、资源文件和项目工程文件整体仅259KB结构精简便于运行调试。已有2308人学习下载。工程演示了通过HttpWebRequest发送请求并接收响应使用JavaScriptSerializer解析JSON或用XDocument处理XML同时说明API密钥注册与管理要点。代码封装了天气数据获取方法能够提取温度、湿度、风向等信息并配有Windows窗体界面示例可快速迁移到其他项目。对刚接触网络编程或想基于VS2008实现天气功能的读者这是一份可运行、可参考的实战模板。1. 中国天气网获取天气数据不靠解析 HTML靠的是它藏在 JS 里的 JSON 接口“中国天气网获取天气数据”这件事最容易被新手带偏的地方是一上来就对着www.weather.com.cn的 HTML 页面写正则。实际做过一轮就会明白页面结构半年能改三次而藏在页面背后的 JSON 接口和内嵌 JS 变量反而稳定得多。这篇文章要讲的是一条我自己跑通过的路线——先用城市 ID 锁定目标再分别拿实时数据、今日概况和七天预报最后把采集做成带重试、校验和存储的小任务。适合谁要做天气展示页面、气象数据分析、本地小仪表盘又不想接付费天气 API 的开发者。后面所有代码基于 Python 3.8依赖只有requests和beautifulsoup4。2. 先搞懂中国天气网的数据源城市 ID 和三个关键接口2.1 城市 ID 从哪来页面、JS 文件与 city3jdata 三级接口中国天气网所有天气数据的入口都不是城市名而是一个 9 位数字城市 ID比如北京是101010100上海是101020100。网页端搜索框输入“海淀”跳转到的 URL 里就是101010200之类的 ID。9 位 ID 的常见规则是前三位101固定接着两位省代码、两位市代码、两位区县代码。但这条规则只适合做校验不适合直接拼——直辖市、省直辖县级市、新设立的区经常不按连号走手拼出来的 ID 有一半是查不到数据的。安全的做法是用网站自身的分级接口去查。城市数据分三级省份、地级市、区县对应三个接口import requests headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36 } # 第一级省份列表返回 {01: 北京, 02: 上海, ...} province_resp requests.get( http://www.weather.com.cn/data/city3jdata/province.html, headersheaders, timeout10 ) provinces province_resp.json() print(provinces[01]) # 北京 # 第二级某省份下的城市返回 {0101: 北京, ...} city_resp requests.get( fhttp://www.weather.com.cn/data/city3jdata/stat/{01}.html, headersheaders, timeout10 ) cities city_resp.json() print(cities[0101]) # 北京 # 第三级某城市下的区县返回 {0101: 海淀, ...} station_resp requests.get( fhttp://www.weather.com.cn/data/city3jdata/station/{0101}.html, headersheaders, timeout10 ) stations station_resp.json() print(stations[0101]) # 海淀这段代码的逻辑是逐级往下查每一级返回的都是{代码: 名称}的 JSON 对象。省代码是两位市代码是省代码加城市两位区县代码是市代码加区县两位。最终拼成城市 ID 时规则是101 省代码 市代码 区县代码。用上面的例子101010101对应海淀101010101但海淀在网页端的实际 ID 是101010200说明第三级 station 返回的区县代码和实际数据服务 ID 并不总是一一对应。所以这个接口真正可靠的用法是拿前两级确认省和市区县一级只作参考最终 ID 以页面 URL 里的为准。2.2 三个核心接口实时天气、今日概况与七天预报拿到城市 ID 之后数据接口分三类覆盖不同需求接口 path返回内容适合用途注意点/data/sk/{city_id}.html实时温度、风向、风力、湿度、气压仪表盘实时显示老接口字段名是 WD/WS 这种缩写/data/cityinfo/{city_id}.html今日最高/最低温、天气现象、发布时间今日概览卡片temp1 是最低温temp2 是最高温容易写反/weather/{city_id}.shtml七天预报完整页面七天趋势数据在页面内嵌 JS 变量里需要正则提取实时接口和概况接口返回的是标准 JSON七天预报的.shtml是一个完整 HTML 页面数据不在 HTML 标签里而是嵌在script标签的 JS 变量中。这个差异决定了后面解析方式完全不同前两个用resp.json()一步到位七天预报需要用正则先取出 JS 变量再json.loads。这三个接口都不需要登录、不需要 token、不需要签名参数唯一的硬性要求是请求头里带一个正常的User-Agent。不带 UA 的请求偶尔会返回 200 但内容为空这是网站反爬最轻的一层骗过它不需要什么技巧。2.3 为什么选 JSON 接口而不是直接解析 HTML很多人第一次做这个需求下意识就去抓http://www.weather.com.cn/weather/101010100.shtml然后在 BeautifulSoup 里找.tem、.win这些 class。这个方案的致命问题不是写不出来而是维护不住页面改版一次class 名换一批你的解析代码就要重写。JSON 接口的字段虽然也变但变动频率低得多而且老接口即便字段冗余也不会随意删。另一个维度是请求体积。一个.shtml页面加图片、广告占位、统计脚本动辄几十 KB而/data/sk/接口返回的 JSON 只有几百字节。如果你要监控几十个城市JSON 接口在带宽和解析耗时上的优势是量级的。后面所有的实现都基于这个选型能拿 JSON 就不碰 HTML页面只用来提取七天预报里那段 JS 变量。3. 用 Python 跑通最小采集脚本从城市 ID 到天气 JSON 落盘3.1 先写一个查城市 ID 的工具函数实际项目中我不会每次运行都去查三级接口城市的 ID 变化不频繁查一次存成本地配置文件更合理。但第一次写脚本时这个工具函数能帮忙确认手头 ID 是否有效。把上一步的三级查询包成一个函数import requests HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36 } def query_city_id(province_code: str, city_code: str) - str: 根据省市代码拼出9位城市ID并验证该ID是否有实时数据。 base f101{province_code}{city_code}00 # 用实时接口做验证404或报错说明ID不可用 check_url fhttp://www.weather.com.cn/data/sk/{base}.html resp requests.get(check_url, headersHEADERS, timeout10) if resp.status_code ! 200: return None data resp.json() if data.get(weatherinfo, {}).get(cityid) ! base: return None return base print(query_city_id(01, 01)) # 北京 101010100函数参数是两位省代码和两位市代码内部直接拼101 省 市 00然后用实时接口验证。这里有一个容易被忽略的细节北京这种直辖市的区一级代码是00数据挂在它下面普通地级市的市区也是00结尾。校验时拿返回的cityid字段和拼接结果比对能过滤掉一部分“接口返回了 JSON 但实际是错误页”的情况。这个函数返回值如果是None就说明省市代码配错了需要回到 city3jdata 接口去查。3.2 实时天气与今日概况两个最稳的 JSON 接口城市 ID 确定之后采集主体代码只有十几行。下面这版把实时数据和今日概况一起抓下来并打印关键字段import requests import json from datetime import datetime CITY_ID 101010100 # 北京 HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36 } def fetch_weather(city_id: str) - dict: sk_url fhttp://www.weather.com.cn/data/sk/{city_id}.html info_url fhttp://www.weather.com.cn/data/cityinfo/{city_id}.html sk_resp requests.get(sk_url, headersHEADERS, timeout10) sk_resp.encoding utf-8 sk_data sk_resp.json()[weatherinfo] info_resp requests.get(info_url, headersHEADERS, timeout10) info_resp.encoding utf-8 info_data info_resp.json()[weatherinfo] record { city: sk_data[city], cityid: city_id, temp_now: float(sk_data[temp]), # 当前温度 wind_dir: sk_data[WD], # 风向 wind_level: sk_data[WS], # 风力 humidity: sk_data[SD], # 相对湿度 temp_low: info_data[temp1], # 今日最低温 temp_high: info_data[temp2], # 今日最高温 weather: info_data[weather], # 天气现象 fetch_time: datetime.now().isoformat() # 本地采集时间 } return record if __name__ __main__: print(json.dumps(fetch_weather(CITY_ID), ensure_asciiFalse, indent2))这段代码的逻辑分三步请求实时接口、请求概况接口、合并字段。sk_data[temp]返回的是字符串比如23或-8所以转成float方便后续计算。temp1和temp2同样是字符串里面带了单位℃我没有在这里转换因为后面清洗章节会统一处理。这里要解释两个参数timeout10是必须的中国天气网的接口在极端天气时偶尔会慢不设超时会让采集任务挂死resp.encoding utf-8是主动声明编码避免 requests 对部分接口返回的 GBK 内容误判成 Latin-1 导致乱码。如果你在 Windows 终端打印中文乱码通常是终端编码问题不是接口问题把输出重定向到文件检查即可。3.3 七天预报从页面内嵌 JS 变量里提取七天预报没有公开的纯 JSON 接口常规做法是抓.shtml页面然后从页面里的hour3dataJS 变量中提取。这个变量存放未来 48 小时和 7 天趋势的数据结构是城市 ID 作为键里面带weatherList之类的数组。用正则把它抠出来再解析import requests import re import json def fetch_forecast_7d(city_id: str) - list: url fhttp://www.weather.com.cn/weather/{city_id}.shtml resp requests.get(url, headersHEADERS, timeout15) resp.encoding utf-8 html resp.text # 页面内嵌的JS变量包含7天数据 match re.search(rvar hour3data(\{.*?\});, html, re.S) if not match: return [] raw json.loads(match.group(1)) city_key str(city_id) if city_key not in raw: return [] # 从数据里取出7天趋势列表 weather_list raw[city_key].get(weatherList, []) result [] for item in weather_list: result.append({ date: item.get(date, ), temp_low: item.get(low, ), temp_high: item.get(high, ), weather: item.get(wth, ), wind: item.get(wd, ), }) return result if __name__ __main__: for day in fetch_forecast_7d(101010100): print(day)re.search(rvar hour3data(\{.*?\});, html, re.S)这段正则是整个函数最核心的部分\{.*?\}是非贪婪匹配re.S让.能匹配换行符保证 JS 变量跨行也能取全。取出来的字符串是一个 JSON 对象键是城市 ID值里嵌套了weatherList数组。这个提取结果有个坑weatherList里包含的不只是 7 天有些城市会带 6 小时间隔的多组数据打印出来可能超过 7 条。实际使用时要按date字段去重只保留每天最后一条或第一条。另外low和high是纯数字字符串weather是中文描述wd是风向字段名和实时接口完全不一样别套用同一套解析逻辑。4. 把采集脚本升级成数据任务轮询频率、存储与失败重试4.1 轮询频率怎么定气象数据的时效边界能抓到数据之后第一个要决定的是多久跑一次。气象数据有天然的时效边界实时温度每 510 分钟更新一次今日概况每天只在几个固定时间点刷新七天预报每小时全量重算。没必要用同一套频率去刷三个接口我一般按下面的表格配置数据推荐轮询间隔依据实时温度/风力5 分钟数据源本身约 5 分钟刷新一次今日最高/最低温30 分钟一天内只变几次30 分钟足够覆盖七天预报1 小时上游预报每天更新数次小时级足够具体调度我用一个简单的whilesleep循环加时间戳判断不用引入 Celery 这类重框架。如果你要跑在服务器上更稳妥的办法是系统 crontab 或systemd timer这样采集进程崩溃了会被系统拉起而不是靠脚本内部自愈。4.2 存储方案SQLite 一张表 vs CSV 按天分文件数据量小的时候CSV 按天分文件是最直观的方案文件名带日期每天一个文件排查问题方便。但 CSV 有两个问题并发写入会丢数据按日期查询要跨文件。采集任务通常单线程跑并发问题不大可如果你后面要分析历史趋势SQLite 一张表省事得多。我推荐最小化方案SQLite 单表时间戳做主键索引。import sqlite3 from datetime import datetime DB_PATH weather.db def init_db(): conn sqlite3.connect(DB_PATH) conn.execute( CREATE TABLE IF NOT EXISTS weather_hourly ( fetch_time TEXT NOT NULL, city_id TEXT NOT NULL, temp_now REAL, temp_low REAL, temp_high REAL, humidity REAL, wind_dir TEXT, weather TEXT, PRIMARY KEY (fetch_time, city_id) ) ) conn.commit() conn.close() def save_record(record: dict): conn sqlite3.connect(DB_PATH) conn.execute( INSERT OR REPLACE INTO weather_hourly (fetch_time, city_id, temp_now, temp_low, temp_high, humidity, wind_dir, weather) VALUES (?, ?, ?, ?, ?, ?, ?, ?) , ( record[fetch_time], record[cityid], record[temp_now], record[temp_low], record[temp_high], record[humidity], record[wind_dir], record[weather], )) conn.commit() conn.close()INSERT OR REPLACE的语义是如果fetch_time和city_id组合已存在就覆盖整行。这让重跑采集脚本变得安全——重复执行不会产生脏数据只会更新同一条记录。init_db()要在采集启动前调用一次建表语句里的PRIMARY KEY (fetch_time, city_id)同时充当去重约束和查询索引后面查某个城市某天的数据会很快。存储字段的类型值得注意temp_now是 REAL而temp_low、temp_high在 JSON 里带℃后缀存入前必须先清洗成float否则 SQLite 虽然能存但后续AVG、MAX这类聚合函数算出来全是 0。4.3 失败重试与数据校验不能把脏数据写进库网络请求一定会失败这是采集任务里唯一可以提前确定的事。超时、连接重置、对方返回 500都需要重试。但重试不能无脑循环否则对方服务器把你 IP 封了都不知道。我一般用指数退避最多重试三次import time import requests def fetch_with_retry(url: str, headers: dict, max_retries: int 3) - requests.Response: for attempt in range(max_retries): try: resp requests.get(url, headersheaders, timeout10) if resp.status_code 200: return resp except requests.RequestException: pass time.sleep(2 ** attempt) # 第1次等2秒第2次等4秒第3次等8秒 raise RuntimeError(f请求失败: {url})重试逻辑的关键参数是time.sleep(2 ** attempt)第一次失败等 2 秒第二次等 4 秒第三次等 8 秒。这个退避速度对天气数据足够了不会因为间隔太短触发反爬也不会因为间隔太长拖慢任务。如果三次都失败直接抛异常让上层任务记录日志而不是吞掉错误继续往下走。数据校验在入库前做比入库后清洗省事得多。至少校验两点温度值是否在合理区间夏天不可能出现-30冬天不可能出现45以及返回的cityid是否和请求的 ID 一致。校验不通过就丢弃这轮数据等下一个周期自然更新而不是把异常值写进库里以后再花半天排查是从哪来的。5. 天气数据采集避坑5 个让脚本翻车的真实细节5.1 城市 ID 变了接口返回 404还以为是网站改版现象某一天开始/data/sk/{city_id}.html返回 404但城市在网页端能找到天气信息。原因不是网站改版而是部分城市 ID 在实时数据服务里根本不存在。特别是行政区划调整后新设立的区县 ID 在网页端能用但老数据接口还没来得及同步。另一个高频场景是自己按101 省 市 区拼接的 ID拼出来看着合理实际服务端没有对应数据。解决城市 ID 以city3jdata接口逐级查出的为准区县一级不要自己拼用页面 URL 里的实际 ID。启动采集任务前跑一遍探测脚本把失效的 ID 从配置里移除并告警。5.2 返回 200 但内容乱码或空 body现象resp.text打印出来全是或者resp.json()抛出JSONDecodeError但浏览器里打开同 URL 完全正常。原因部分接口在特定网络环境下返回 GBK 编码requests 默认会从响应头猜编码猜错的概率不低。另外一个场景是响应被 gzip 压缩虽然 requests 会自动解压但如果通过代理访问压缩头可能被剥离导致 body 成了二进制乱码。解决显式设置编码优先按响应头里的charset判断没有就退回gbk重试一把。压缩问题先打印resp.headers.get(Content-Encoding)确认再排查代理配置。不要依赖resp.apparent_encoding它对短文本的猜测经常不准。5.3 温度是负值int() 硬转没问题但缺字段会 KeyError现象冬天跑得好好的脚本某天变成KeyError: temp日志里一堆 Traceback。原因天气接口是“有则返回、无则不返回”的规则字段不保证齐全。尤其雷雨、大风等极端天气场景实时接口偶尔会缺失temp或WD字段。用resp.json()[weatherinfo][temp]这种链式访问字段一缺就崩。解决所有字段取值改用.get()并给默认值入库前再做一次字段存在性校验。宁可记录NULL也不要让采集任务死在半路更不要把异常值硬塞进数据库。5.4 d1 接口直接抓返回 403Referer 和时间戳的坑现象用d1.weather.com.cn开头的 JSONP 接口抓数据requests.get()直接 403加 UA、换 IP 都没用。原因d1域名下的接口校验Referer必须来自www.weather.com.cn同时习惯要求带_回调参数。这是专门给网页端 JS 用的接口直接裸请求会被当成非法调用拒绝。解决请求头加Referer: http://www.weather.com.cn/URL 末尾补?_加毫秒时间戳。如果只是做常规天气展示直接用/data/sk/老接口更省事没必要跟d1接口较劲。5.5 凌晨抓到的“今日”还是昨天的数据现象每天 0 点到 6 点之间采集的temp_low、temp_high、weather都是前一天的对比网页端看到的数据也对不上。原因今日概况接口的刷新时间不是自然日零点而是按气象业务节奏走一般在清晨完成当日数据上线。ptime字段数据发布时间能明确看出数据的实际时间戳。解决入库前检查返回的ptime在“今日”字段刷新前不要把采集结果当当日数据写入。两个选择要么采集任务避开 06 点要么把ptime也存进库里让下游自行判断数据新鲜度。我选了后者保留原始时间戳的价值远比省一条记录大。6. 从 JSON 到干净气象表字段清洗与数据校验的最后一公里前面所有代码拿到的都还是“能用但不够规范”的数据温度带单位、字段名不统一、部分值可能是空字符串。如果只做展示直接打印没问题如果要入库做分析必须先清洗成统一格式。我常用的做法是把三个接口的数据合并成一个扁平字典再统一做类型转换。清洗规则有几条temp1和temp2去掉℃后转float同时记住temp1是最低、temp2是最高temp_now直接转float但要做空值判断因为实时接口在极端天气下可能缺这个字段湿度SD去掉%后转float风向WD、风力WS、天气现象weather保持字符串原样但要去掉首尾空格。验证采集数据对不对我有一个土办法把清洗后的温度和time字段打印出来和手机自带天气 App 同城市对比。温差在 12℃ 内是正常的因为气象站位置和温度计不同如果相差 5℃ 以上优先怀疑城市 ID 串了——别笑多城市采集时配置写错城市 ID 是最高频的事故。另一个验证点是交叉验证用/data/sk/的temp和.shtml页面里hour3data的当前温度做比对两个独立数据源一致基本可以确认链路没有解析 bug。我自己踩过的最大一个坑是把temp1和temp2写反导致一整周的“最高温”都是负数。后来在脚本里加了一条断言temp_high temp_low不满足直接告警。这种自校验逻辑比写十行注释都管用。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

WinCC嵌入Excel报表开发指南:从OLE配置到自动导出 2026/9/25 8:02:50

WinCC嵌入Excel报表开发指南:从OLE配置到自动导出

1. 为什么WinCC报表需要Excel这把“瑞士军刀”1.1 传统报表方案的痛点做自动化项目的人,迟早都会撞上报表这个需求。现场调试的时候,业主方提得最多的几个要求里,“每天给我出一份当班产量报表”“把这几天的温度曲线导出来给我看看”几乎是必…

阅读更多 →
开源商业化怎么做?COSCon‘25全球商业化论坛亮点解析 2026/9/25 8:02:44

开源商业化怎么做?COSCon‘25全球商业化论坛亮点解析

COSCon‘25 的议程发布消息一出来,我第一时间把它从头到尾捋了一遍。作为常年蹲在开源商业化和社区运营交叉口的人,我对“开源全球商业化论坛”这个名字其实期待了很久。过去几年,国内几乎所有开源大会都在解决“怎么把项目做出来”“怎么把人…

阅读更多 →
使用 AWS SDK for Java 2.x 操作 AWS HealthImaging:数据存储、DICOM 导入与影像集管理实战指南 2026/9/25 8:02:37

使用 AWS SDK for Java 2.x 操作 AWS HealthImaging:数据存储、DICOM 导入与影像集管理实战指南

示例工程教程后端 【免费下载链接】aws-doc-sdk-examples Welcome to the AWS Code Examples Repository. This repo contains code examples used in the AWS documentation, AWS SDK Developer Guides, and more. For more information, see the Readme.md file below. 项目地…

阅读更多 →
Atlas 300V Pro 24G部署YOLO全流程:从推理加速卡选型到昇腾NPU实战 2026/9/25 8:02:37

Atlas 300V Pro 24G部署YOLO全流程:从推理加速卡选型到昇腾NPU实战

最近几天,后台和微信私信里问得最多的就是两个问题:Atlas 300V Pro 24G到底算不算一块“运算加速卡”?以及能不能用它来部署YOLO模型?我一开始没太当回事,觉得这是昇腾生态里的老问题,结果看得多了才发现&a…

阅读更多 →
Atlas 300V 24G实战:YOLOv5/YOLOv8模型转换与推理部署全指南 2026/9/25 8:02:37

Atlas 300V 24G实战:YOLOv5/YOLOv8模型转换与推理部署全指南

最近在搞目标检测服务迁移,手头正好有一批Atlas 300V 24G推理加速卡。说实话,一开始我对这类NPU卡是有偏见的,毕竟训练和调优都在GPU上跑习惯了,换到华为的这套工具链,总感觉要先“脱层皮”。但真正把YOLOv5和YOLOv8的…

阅读更多 →
企业流程管理数字化转型:从流程建模到运营优化的落地指南 2026/9/25 8:02:11

企业流程管理数字化转型:从流程建模到运营优化的落地指南

简介:一份关于企业流程管理的数字智慧方案PPT,共76页,面向企业管理者、流程优化人员及数字化转型相关从业者,系统讲解如何通过流程管理打破部门壁垒、提升组织效率。资源为1个pptx文件,压缩包约814KB。整套内容按七大模…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉