FEATURED · 精选文章

Python天气爬虫课程设计:从Requests到ECharts可视化

发布时间 / 2026/9/17 4:47:58
来源 / 创域科博编辑部
栏目 / 资讯中心
Python天气爬虫课程设计:从Requests到ECharts可视化 简介一份基于Python的天气数据爬取与可视化分析课程设计/期末大作业完整方案定位清晰适合计算机相关专业本科生完成期末项目、课程设计或毕业设计也适合想通过项目实战巩固爬虫与可视化技能的学习者。项目经导师指导并审核评审达到98分所有源码均本地编译调试通过可正常运行。压缩包内共514个文件约22.33MB主要包括499个SVG图表资源、2个HTML可视化页面、2个CSS样式、3个JavaScript交互文件、Python爬虫脚本、SQL数据文件以及课程设计报告和说明文档SVG资源覆盖天气图标或多类统计图便于直接复用展示。目前已有201人浏览学习。通过该方案可系统了解天气数据采集、清洗、入库到ECharts可视化展示的完整流程同时可参考高分报告结构撰写自己的课程设计是兼具代码可运行性与文档参考价值的实用项目。1. 一个98分的Python天气爬虫课程设计先拆开看它做了几件事一个Python爬虫课程设计爬虫脚本能跑只是拿到及格分评阅老师点开页面看到空白图表分照样不高——数据从requests拿到手到ECharts渲染出来中间隔着编码转换、字段映射、JSON格式三道坎。这个拿了98分的项目把完整链路串通了爬虫抓天气数据清洗后落地成前端可读的JSON再由jQuery拉数据喂给ECharts画图外层还有登录页和配套报告。适合两类人正在找Python期末大作业参考的学生想复现爬虫数据分析与可视化这套流水线的练习者。后面按数据流动顺序拆关键代码和参数取舍。2. 爬虫链路从requests请求到BeautifulSoup解析天气数据先讲选型。这个项目为什么用网页爬虫而不是直接调用天气API我的习惯是目标站点有开放接口就优先用接口字段稳定、省去解析成本。但课程设计的评审点通常落在“是否体现网络爬虫原理”HTML解析反而是加分项另外很多天气信息站的JSON接口字段说明不完整网页版详情页结构反而多年稳定。两种方案在课程设计场景下的取舍如下表所示。选型优点代价适合场景网页HTML爬虫能完整展示请求、解析、清洗流程页面改版会导致选择器失效课程设计、毕设展示爬虫原理公开JSON接口数据稳定、代码量少可展示的技术点偏少、限流未知需要长期稳定的线下数据我一般选网页爬虫作为演示主体抓取对象用城市7天天气详情页再把解析后的数据流传给可视化层。2.1 构造请求URL、请求头与编码以抓取城市7天天气预报为例先把请求封装成独立函数方便后续循环调用。import requests from bs4 import BeautifulSoup import re import json import time def fetch_weather_page(city_id: str) - str: url fhttps://www.weather.com.cn/weather/{city_id}.html headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36, Referer: https://www.weather.com.cn/, } resp requests.get(url, headersheaders, timeout10) resp.encoding utf-8 return resp.text这里三个参数值得单独说。city_id是城市编号天气站点给每个城市分配了一串数字ID比如北京是101010100实际使用时要换成目标城市User-Agent伪装成Chrome浏览器能过滤掉一部分最简单的反爬拦截Referer带上站内来源避免部分网关校验。编码处理是新手最容易忽略的点国内网页多数是utf-8但个别站点仍是gbk如果requests自动猜测出错中文会变成乱码。我拿到response后习惯先看resp.apparent_encoding确实与utf-8不一致就在代码里手动指定。2.2 用BeautifulSoup抽取温度、天气与风力拿到HTML后用BeautifulSoup配合lxml解析器提取字段。下面这段代码是项目里解析函数的核心写法。def parse_weather(html: str) - list[dict]: soup BeautifulSoup(html, lxml) # 选择器需要按目标页面的实际结构调整 rows soup.select(ul.t.clearfix li) weather_list [] for item in rows: weather_list.append({ date: item.select_one(h1).text.strip(), high: re.sub(r\D, , item.select_one(.tem span).text), low: re.sub(r\D, , item.select_one(.tem i).text), weather: item.select_one(.wea).text.strip(), wind: item.select_one(.wind).text.strip(), }) return weather_list这段代码里BeautifulSoup(html, lxml)中的lxml是解析器容错性比Python自带的html.parser更好速度也更快但需要单独安装。ul.t.clearfix li这类CSS选择器是页面结构决定的示例里的结构来自常见天气页换一个站点就要打开浏览器开发者工具先定位日期、温度、天气现象所在的节点把class名替换掉。用re.sub(r\D, , ...)把“31℃”里的“℃”剔除是为了让温度字段干净落成数字后续前端绘图才不会因为单位混入而把坐标轴搞乱。解析时如果某个节点缺失select_one会返回None所以代码里要在外层加异常处理避免一个坏数据拖垮整个脚本。2.3 连续抓取与频率控制课程设计要体现“爬虫”而不是“一次性请求”所以常见做法是维护一个城市列表循环抓取并保存。city_list [101010100, 101020100, 101280101] all_data {} for cid in city_list: html fetch_weather_page(cid) all_data[cid] parse_weather(html) time.sleep(2) # 避免高频请求触发风控time.sleep(2)在这里不是摆设。演示项目通常只抓几个城市间隔2秒足够如果爬取周期跨度大比如要抓一整年的历史数据间隔至少提到5秒以上并且不要用多线程并发打同一个站点。课程设计报告里把“遵守robots协议、控制抓取频率”写进设计说明是答辩时很加分的细节。运行前先在VSCode里配置好Python环境终端执行python -m pip install requests beautifulsoup4 lxml把依赖装到当前虚拟环境而不是系统Python里避免污染全局环境。3. 数据落地与字段规整把爬到的天气数据变成ECharts认识的JSON爬虫跑完只是拿到原始文本真正决定可视化页面能不能顺滑展示的是字段怎么命名、数值怎么清洗、文件路径怎么组织。很多项目卡在这一步爬虫输出的是“31℃”“多云转晴”这种混合字符串前端图表拿到后根本没法定量绘制。3.1 文件目录与字段设计我习惯在项目根目录下建data/和output/两个目录原始页面存data/raw/清洗后的可视化数据放output/。课程设计项目里目录叫别的名字也可以关键是README里写清楚每个文件是谁的数据、多久更新一次。字段建议直接对齐前端图表的维度避免二次映射。下面这张字段表是项目里实际使用的结构。字段类型示例说明datestring2025-06-01用作x轴highint31最高气温画y轴lowint22最低气温画y轴weatherstring多云白天天气现象用于tooltipwindstring东南风3级辅助信息展示在详情框按这个结构存储前端$.getJSON拿到什么就渲染什么不用在JavaScript里再做一轮数据类型转换也能减少前后端字段不一致的排查成本。3.2 清洗逻辑缺失值、单位混入与异常兜底爬虫数据最常见的脏数据类型有三种字段缺失、单位混入、极端异常值。我一般用一个独立清洗函数统一处理。def clean_weather(raw_list: list[dict]) - list[dict]: cleaned [] for item in raw_list: try: high int(re.sub(r\D, , item[high]) or 0) low int(re.sub(r\D, , item[low]) or 0) except (ValueError, KeyError): high, low 0, 0 # 解析失败时给0而不是让脚本崩溃 if high 60 or low -60: # 粗粒度兜底过滤明显异常 continue cleaned.append({ date: item[date], high: high, low: low, weather: item[weather] if item[weather] else 未知, wind: item[wind] or 暂无, }) return cleanedre.sub(r\D, , item[high])这一步把“31℃”处理成“31”再用int()转成整型后面的or 0专门处理空字符串因为有些页面某天温度字段渲染失败时就是空值。if high 60 or low -60是一个简单的合理性校验防止网站偶发返回“99℃”这类乱码数据把图表纵轴撑爆。很多同学只做正则替换不做异常兜底结果爬虫跑一半因为KeyError中断或者前端折线图出现一个离谱的尖峰这些都能在清洗层提前挡住。3.3 输出JSON编码和缩进是关键清洗后的数据要落盘供前端读取写出时必须保证中文可读、缩进清晰。def save_json(data: list[dict], path: str output/beijing_weather.json) - None: with open(path, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse, indent2)ensure_asciiFalse让“多云”写入文件时保持原样不加这个参数“多云”会变成\u591a\u4e91虽然ECharts能解析但报告截图和人工排错时阅读体验很差。indent2是给人工检查用的写完JSON后直接在终端验证一次python -c import json; print(json.load(open(output/beijing_weather.json, encodingutf-8)))能正常打印出列表结构就说明后端链路已经通了。4. 前端可视化与登录页从keshihua.html到ECharts渲染项目的资源文件里有keshihua.html、keshihua.js、keshihua.css、login.html、login.css、echarts.min.js和jquery-3.6.0.min.js还有一张beijing2.jpg背景图。这一套结构对应的是一条“采集-存储-展示”的演示系统login页面做入口keshihua页面做数据展示。文件分工如下表所示。文件作用关键点login.html / login.css登录入口本地校验后跳转keshihua.htmlkeshihua.html可视化主页面引用echarts.min.js、jquery、keshihua.jskeshihua.css页面样式控制卡片布局与图表容器尺寸keshihua.js图表绘制逻辑异步读取JSON并初始化图表beijing2.jpg页面背景增加可视化大屏感echarts.min.js / jquery-3.6.0.min.js前端依赖本地引入不依赖CDNREADME.md项目说明写清运行方式与依赖前端依赖用本地文件而不是CDN链接这个细节在课程设计里很实用演示现场往往没有外网CDN加载失败会导致整个页面空白。4.1 登录页跳转与本地校验课程设计里的登录页通常不接后端鉴权我一般这样处理页面里写一个演示账号点击登录后先做非空校验再跳转到可视化主页面。input idusername placeholder账号 input idpassword typepassword placeholder密码 button idloginBtn登录/button$(#loginBtn).click(function () { const u $(#username).val().trim(); const p $(#password).val().trim(); if (u admin p 123456) { location.href keshihua.html; } else { alert(账号或密码错误); } });.val().trim()先去除首尾空格避免用户误输入空格导致校验失败。账号密码直接写在前端只能用于课程设计展示真正的生产系统必须把凭证放在后端校验这里只是用最轻量的方式模拟“登录后进入系统”这个交互闭环。如果想在报告里体现安全意识到位可以补充一句“本模块为演示实现生产环境需替换为会话鉴权”。4.2 在keshihua.js里用ECharts绘制温度曲线主页面最关键的是图表代码。用jQuery读取上一章生成的JSON再交给ECharts初始化。$.getJSON(output/beijing_weather.json, function (rows) { const dates rows.map(r r.date); const highs rows.map(r r.high); const lows rows.map(r r.low); const chart echarts.init(document.getElementById(tempChart)); chart.setOption({ tooltip: { trigger: axis }, legend: { data: [最高温度, 最低温度] }, xAxis: { type: category, data: dates, axisLabel: { rotate: 30 } }, yAxis: { type: value, name: 温度(℃) }, series: [ { name: 最高温度, type: line, data: highs, smooth: true }, { name: 最低温度, type: line, data: lows, smooth: true } ] }); });rows.map负责从JSON数组里抽日期和温度字段抽出来的数组直接对应ECharts的data。tooltip: { trigger: axis }让鼠标悬停时按日期联动显示两条线的数值。axisLabel.rotate: 30解决日期标签重叠的问题这跟matplotlib里旋转横坐标刻度是同一个思路——如果读者在Python绘图时也遇到横坐标太密集把rotation参数调大就行。series数组里每项对应一条折线smooth: true让曲线更平滑观感更接近专业可视化。这里有一个隐藏依赖浏览器用file://协议打开HTML时$.getJSON请求本地JSON一般可用但如果部署到远程服务器就需要同源或配置跨域课程设计演示中直接双击打开即可。4.3 前端展示不出来的常见坑最容易翻车的三个点JSON文件编码不是UTF-8$.getJSON拿到中文变成乱码图表标题正常但tooltip显示乱码。解决办法就是上一章写文件时强制指定encodingutf-8。high字段是字符串“31”而不是数字31折线图会把数值当分类数据处理坐标轴刻度错乱。我在清洗层用int()强转就是提前把类型钉死。beijing2.jpg路径写错CSS正常但页面没有背景图。检查相对路径时以HTML文件所在目录为基准不是以浏览器打开的当前路径为基准。5. 增量爬取与课程设计报告的收尾技巧爬虫和可视化跑通只完成80%最后两件事决定了项目能不能拿到高分数据是不是可持续更新、报告和源码结构是不是适合答辩。5.1 让爬虫只抓增量而不是全量重跑一个实用做法是把“城市日期”作为文件索引写成幂等逻辑。from pathlib import Path def fetch_with_cache(city_id: str, target_date: str) - bool: out_path Path(foutput/{city_id}_{target_date}.json) if out_path.exists(): return False # 已有当天数据跳过避免重复请求 html fetch_weather_page(city_id) rows parse_weather(html) cleaned clean_weather(rows) save_json(cleaned, out_path) return True文件名里的日期就是幂等键重复执行脚本时已抓过的城市自动跳过既省时间也降低被封风险。如果想做定时更新Windows上用任务计划程序运行python weather_spider.py或者用GitHub Actions定时触发这些对课程设计来说都是加分项但需要在README里写明触发方式。5.2 报告和源码包的配合方式项目里那份“数据可视化期末结课报告.docx”是另一个隐藏得分点。据我接触的同类项目高分报告通常按这个结构组织背景与目标、需求与总体架构、爬虫设计与关键代码、数据清洗与存储、可视化展示、总结。每章配一到两张截图、核心代码块和参数说明不要整段贴源码。README则只需要写清四件事运行环境、依赖安装命令、爬虫和页面各自的启动方式、数据文件的位置。这样评阅老师按README能十分钟跑通项目答辩时也只用顺着报告讲一遍数据流动的链路。本文还有配套的精品资源点击获取
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻