资讯动态

中国天气网天气数据采集实战:从页面解析到结构化数据集

发布时间:2026/10/17 8:40:42 来源:尧图企业网站定制
简介这份资源围绕“中国天气网获取天气数据”这一典型开发任务展开面向具备一定C#基础、希望在VS2008环境下练习网络请求与数据解析的开发者。内容涉及API接口调用、HTTP请求与响应处理、JSON/XML解析、API密钥注册管理以及Windows Forms界面数据显示等核心知识点适合作为气象类小工具或课程设计的参考案例。资源包共34个文件以cs源码、config配置、dll与exe程序、resx资源及sln解决方案为主另有少量缓存与项目设置文件整体约259KB结构完整可直接在VS2008中打开运行。目前已有2311人学习下载。通过这份资料读者可以对照完整工程理解HttpWebRequest发起请求、JavaScriptSerializer或XDocument解析返回数据的实现方式并借鉴错误处理与界面展示的写法快速搭建自己的天气数据获取程序。1. 中国天气网获取天气数据从页面结构到可用数据集的落地路径做气象相关项目的开发者大概都经历过这个场景需要一个城市的逐日天气数据做分析或建模第一反应是找开放 API结果要么额度不够要么字段残缺要么稳定性堪忧。这时候中国天气网就成了一个绕不开的备选——它页面信息全、覆盖城市广、更新及时但官方并没有提供面向普通开发者的公开数据接口。于是「获取天气数据」这件事就变成了一个需要自己动手拆页面、理字段、做清洗的工程活。这份资源要解决的正是从中国天气网页面中稳定提取结构化天气数据的问题。它适合两类人一类是需要批量采集历史与实时天气数据做研究或产品原型的开发者另一类是正在学习网页数据提取、想找一个真实且结构清晰的练手项目的同学。核心难点不在「能不能抓到」而在「抓得稳不稳、字段全不全、后续能不能直接用」。2. 拆解页面结构天气数据到底藏在哪一层2.1 先分清三类页面别一上来就写代码中国天气网的页面并不是铁板一块不同入口对应的数据粒度和加载方式差别很大。我一般会先把目标页面分成三类来看第一类是城市天气详情页通常展示当前实况加上未来几天预报字段包括温度、湿度、风力、空气质量等。这类页面结构相对规整是采集的主力目标。第二类是城市列表或省份索引页用来拿到城市名称和对应的页面地址。这一步是批量采集的前提很多新手直接跳过结果只能手动一个个城市去抓。第三类是历史天气或统计类页面数据往往按月份或年份组织表格化程度高但入口较深需要先定位到具体城市再逐层进入。分清楚这三类之后采集流程就清晰了先从索引页拿到城市清单再逐个进入详情页提取字段最后按统一结构落库。常见做法是先用少量城市跑通全流程再放大到全量。2.2 用浏览器开发者工具定位真实数据节点打开目标城市页面后按 F12 进入开发者工具重点看 Elements 面板。不要急着看 Network因为很多天气字段是直接写在 HTML 里的并不需要额外请求。定位时有个小技巧在页面上选中某个温度数字右键选择「检查」工具会直接跳到对应的 DOM 节点。顺着这个节点往上找通常能找到一个包含多个天气字段的父容器。把这个容器的 class 或 id 记下来它就是后续解析的锚点。需要注意页面里可能存在多个相似容器比如「今天」「明天」「后天」各有一块结构相同的区域。这时候不能只靠 class 抓第一个要结合索引或日期字段做区分。我一般会先把父容器列表全部取出来再逐个解析内部字段避免漏掉某一天的数据。2.3 请求头与编码两个最容易翻车的地方直接用一个裸请求去访问大概率会拿到不完整的内容或者乱码。原因通常有两个一是缺少必要的请求头二是编码没处理对。请求头里最关键的是 User-Agent很多站点会据此判断是否为正常浏览器访问。建议至少带上 User-Agent 和 Accept-Language模拟一个常见的浏览器环境。不要用默认的 python-requests 标识那基本等于告诉对方「我是脚本」。编码方面中国天气网部分页面使用 UTF-8但也有页面是 GBK。判断方法很简单看响应内容里 meta 标签声明的 charset或者直接看响应头里的 Content-Type。如果拿到的中文是乱码先检查这一步而不是急着改解析逻辑。import requests headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Accept-Language: zh-CN,zh;q0.9, } url https://www.weather.com.cn/weather/101010100.shtml resp requests.get(url, headersheaders, timeout10) # 先看响应头里的编码声明再决定用哪种方式解码 resp.encoding resp.apparent_encoding html resp.text print(html[:500])这段代码做了三件事构造一个接近真实浏览器的请求头、发起带超时控制的请求、根据响应内容推断编码。timeout参数建议不要省否则遇到慢响应会一直挂住。apparent_encoding是一种兜底推断实际使用时如果已知页面编码直接指定会更稳。3. 解析与结构化把 HTML 变成能用的字段3.1 用解析库提取核心天气字段拿到 HTML 之后下一步是把它变成结构化数据。常用的解析库有 BeautifulSoup 和 lxml前者上手快后者速度快。对于天气页面这种结构相对固定的场景两者都够用。解析的核心思路是先定位到包含天气信息的父容器再在容器内部按字段逐个提取。不要一上来就用全局搜索那样容易抓到导航栏或广告里的干扰内容。from bs4 import BeautifulSoup soup BeautifulSoup(html, html.parser) # 定位未来几天预报的列表容器 day_list soup.select(ul.t.clearfix li) results [] for li in day_list: # 日期和星期 date_tag li.find(h1) # 天气现象 weather_tag li.find(p, class_wea) # 温度范围 temp_tag li.find(p, class_tem) # 风力 wind_tag li.find(p, class_win) if not (date_tag and weather_tag and temp_tag): continue item { date: date_tag.get_text(stripTrue), weather: weather_tag.get_text(stripTrue), temp: temp_tag.get_text(stripTrue).replace(\n, ), wind: wind_tag.get_text(stripTrue) if wind_tag else , } results.append(item) for r in results: print(r)这段代码的关键点在于用select定位列表容器再在循环里逐字段提取。stripTrue去掉多余空白replace(\n, )处理温度字段里常见的换行。if not (...)那行是防御性判断避免某个字段缺失时直接报错。参数方面html.parser是 Python 内置解析器不需要额外安装速度一般但足够用。如果数据量大可以换成lxml需要先安装对应库。选择器ul.t.clearfix li是根据页面实际结构写的不同页面可能不同需要按 2.2 的方法重新定位。3.2 字段清洗温度、风力、日期都要单独处理原始提取出来的字段往往带着多余字符直接入库会很难用。常见需要清洗的有三类温度字段通常形如「-3/6℃」或「6℃」需要拆成最低温和最高温两个数值。风力字段可能是「3-4级」或「微风」需要统一成可比较的格式。日期字段可能只有「今天」「明天」这种相对表述需要结合采集时间换算成具体日期。import re from datetime import datetime, timedelta def parse_temp(temp_str): 把温度字符串拆成最低温和最高温 nums re.findall(r-?\d, temp_str) if len(nums) 2: return int(nums[0]), int(nums[1]) elif len(nums) 1: return int(nums[0]), int(nums[0]) return None, None def parse_date(date_str, base_dateNone): 把相对日期转成具体日期 base base_date or datetime.now() mapping {今天: 0, 明天: 1, 后天: 2} for key, offset in mapping.items(): if key in date_str: return (base timedelta(daysoffset)).strftime(%Y-%m-%d) # 如果是「X日」这种格式按当前月份补全 m re.search(r(\d)日, date_str) if m: day int(m.group(1)) return base.replace(dayday).strftime(%Y-%m-%d) return date_str # 对前面提取的结果做清洗 for r in results: low, high parse_temp(r[temp]) r[temp_low] low r[temp_high] high r[date_full] parse_date(r[date]) print(results[0])parse_temp用正则抓出所有数字根据数量判断是单值还是范围。parse_date先处理相对日期再用正则兜底处理「X日」格式。这两个函数都不复杂但能省掉后续大量手工整理的时间。实际使用时建议把清洗逻辑单独放一个模块方便复用和测试。3.3 批量采集城市清单与循环控制单城市采集跑通后下一步是批量。批量采集的核心是拿到城市清单然后循环调用前面的采集逻辑。城市清单可以从省份索引页提取也可以自己维护一份常用城市列表。前者更全后者更可控。我一般会先用索引页生成一份城市-URL 映射表存成 CSV后续采集直接读表避免每次重复解析索引页。import csv import time def load_cities(pathcities.csv): 从 CSV 读取城市清单 cities [] with open(path, r, encodingutf-8) as f: reader csv.DictReader(f) for row in reader: cities.append(row) return cities def crawl_all(cities, delay1.5): 批量采集带间隔控制 all_data [] for city in cities: try: html fetch(city[url]) # 复用前面的请求逻辑 data parse_weather(html) # 复用前面的解析逻辑 for item in data: item[city] city[name] all_data.extend(data) print(f完成{city[name]}{len(data)} 条) except Exception as e: print(f失败{city[name]}原因{e}) time.sleep(delay) # 控制请求频率 return all_datadelay参数是批量采集里最重要的一个。设得太短容易触发限制设得太长效率低。常见做法是 1 到 2 秒具体看目标站点的响应情况。try/except保证单个城市失败不影响整体流程失败信息打印出来方便后续补采。4. 避坑与排查采集天气数据时最容易踩的五个坑4.1 现象返回内容里没有天气数据只有一段脚本原因页面数据是通过后续请求加载的直接抓初始 HTML 拿不到。解决打开 Network 面板筛选 XHR 或 Fetch 请求找到真正返回数据的接口直接请求该接口。注意接口可能需要特定的 Referer 或参数。4.2 现象中文全部变成乱码原因编码判断错误常见于 GBK 页面被当成 UTF-8 处理。解决先看响应头 Content-Type再看页面 meta 声明。如果两者不一致以实际内容为准手动指定resp.encoding gbk或utf-8。4.3 现象前几天能跑后面全部超时或返回空原因请求频率过高被限制或者请求头被识别。解决加大请求间隔补充 Referer、Accept 等请求头必要时轮换 User-Agent。不要用多线程猛冲天气数据不是抢购稳定比速度重要。4.4 现象温度字段解析出来是空值原因页面结构变了或者选择器写得太死。解决重新用开发者工具定位检查 class 名是否变化。建议把选择器集中配置方便统一修改而不是散落在代码各处。4.5 现象日期字段对不上今天的数据标成了昨天原因相对日期换算时用了 UTC 时间而页面用的是本地时间。解决统一用本地时间做基准或者在换算时加上时区偏移。这个坑很隐蔽数据量小的时候不容易发现。5. 从采集到可用数据校验与增量更新的一点经验采集跑通只是第一步真正让数据能用起来还得做校验和更新。我一般会在入库前加一层校验温度范围是否合理、日期是否连续、城市字段是否为空。任何一条不满足就标记出来单独处理而不是直接混进数据集。增量更新方面天气数据的特点是每天都会变但历史数据不变。所以策略很简单历史部分只采一次当天和未来几天的数据每天定时刷新。刷新时按城市加日期做去重避免重复入库。def validate(item): 基础校验返回 True 或 False if item[temp_low] is None or item[temp_high] is None: return False if item[temp_low] item[temp_high]: return False if item[temp_low] -60 or item[temp_high] 60: return False if not item[city]: return False return True # 入库前过滤 clean_data [item for item in all_data if validate(item)] print(f原始 {len(all_data)} 条校验后 {len(clean_data)} 条)校验逻辑不复杂但能挡掉大部分明显异常。温度上下限根据实际地理范围调整-60到60是一个比较宽松的兜底。去重可以用「城市日期」作为唯一键写入时做冲突处理。还有一个习惯每次采集完把原始 HTML 和解析结果各存一份。原始 HTML 用于事后排查解析结果用于直接使用。存储成本不高但出问题时能省很多事。从那以后我每次做页面采集都会强制走一遍「先存原始、再解析、后校验」的流程宁可多占点空间也不想到时候没有后悔药。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑