Python爬虫与数据可视化实战:天气轮播图期末大作业完整案例
2026/9/1 20:12:59 网站建设 项目流程

这次我们来看一个特别适合 Python 期末大作业的完整项目:用 Python 爬取天气数据,再把结果做成数据可视化图表,最后以「天气轮播图」的形式在网页上展示。项目本身不复杂,但覆盖了数据采集、数据清洗、数据存储、可视化、Web 展示、接口调用和批量任务几个环节,正好对应高校 Python 课程设计里常见的得分点。如果你正在找 Python 爬虫期末大作业的项目思路,或者想通过一个 500 行左右的完整案例把「爬虫 + 数据可视化 + Flask」串起来,这篇文章可以直接对照操作。

这个项目最值得关注的点是:天气数据源不需要申请 Key,直接使用 Open-Meteo 的公开免费接口;可视化部分用 ECharts 和 pyecharts 生成折线图、柱状图、指标卡,再用 Flask 做 Web 页面并实现自动轮播。整套流程对电脑配置要求很低,普通笔记本即可,不需要 GPU,也不需要安装数据库。项目代码全部开源逻辑,可以自己改成多城市、多字段、定时更新的版本。

硬件门槛方面,运行环境只需要 Python 3.9 以上版本,网络能访问外部的公开天气接口即可。显存占用不涉及,因为这里没有深度学习模型参与;主要资源消耗来自 requests 抓取、pandas 预处理和 Flask 页面渲染,内存占用通常在几百 MB 以内,具体以本机运行为准。下面我会从环境准备、模块设计、采集实现、存储处理、可视化、Flask 轮播、接口 API 到批量任务,完整拆解这个 Python 爬虫期末大作业项目。

如果你对 Python 爬虫、数据可视化、天气轮播图这类关键词感兴趣,建议把文章收藏起来,后面直接照着代码搭。

1. 核心能力速览

能力项说明
项目类型Python 爬虫 + 数据分析 + 数据可视化 + Flask Web 展示
数据来源Open-Meteo 公开天气接口,无需注册 Key,适合教学演示
主要功能多城市实时天气、未来 7 天预报、温湿度趋势图、天气卡片轮播
支持批量支持多城市循环抓取、定时更新任务
接口能力Flask 路由返回 JSON 数据接口,可单独给前端调用
可视化方案pyecharts 静态图 + ECharts 前端交互图
运行平台Windows / macOS / Linux 均可
语言版本Python 3.9+
第三方依赖requests、pandas、flask、pyecharts、schedule
硬件门槛普通笔记本即可,无需 GPU,无需安装数据库
适用场景Python 期末大作业、课程设计、爬虫入门实战

从材料看,这个项目非常适合作为期末大作业:目录结构清晰,代码按模块拆分,数据采集、数据处理、可视化、Web 展示四个部分各有完整输出,老师演示时不容易出现「只能跑通一部分」的情况。

2. 适用场景与使用边界

这种天气爬虫与可视化项目适合以下几类读者:

  • 正在准备 Python 爬虫期末大作业或课程设计的学生。
  • 需要快速完成「爬虫 + 数据可视化 + Web 页面」组合项目的初学者。
  • 想在简历里放一个完整项目,但又不想引入过重框架的开发者。
  • 想学习 requests、pandas、pyecharts、Flask 配合使用方法的读者。

不太适合的场景也很明确:如果你需要企业级实时天气服务、高并发接口查询、商业数据分析报告,这个项目就不够用了。Open-Meteo 接口适合学习和展示,不代表可以无限量高频调用。在本地做小规模测试完全没问题,但如果部署到公网并提供多人访问,必须控制请求频率、做好缓存,并仔细阅读对方服务条款。

使用边界必须说清楚:爬虫项目涉及数据来源合规问题。调用 Open-Meteo 这类公开接口时,要遵守接口文档中的使用限制;如果课程要求必须爬取网页,请先确认目标站点是否允许爬虫,查看 robots.txt,并且不要对目标站点造成访问压力。天气数据本身也可能涉及版权,课程作业中用于展示没有问题,但不要直接拿去商用或二次分发。完整项目代码应当只用于学习测试,不应用于违法违规用途。

3. 环境准备与前置条件

建议使用 Python 3.9 到 3.11 的稳定版本。Windows 安装时记得勾选Add Python to PATH,Linux/macOS 一般自带 Python 3,但版本过低时需要先升级。

然后是项目依赖。这里最核心的库有:

  • requests:发起 HTTP 请求,获取天气数据。
  • pandas:做数据清洗和表格合并。
  • pyecharts:生成可视化 HTML 图表。
  • flask:提供 Web 服务和数据接口。
  • schedule:实现定时更新。

建议在项目目录下创建虚拟环境,避免包冲突。创建并激活虚拟环境:

python -m venv .venv

Windows 下激活:

.venv\Scripts\activate.bat

Linux / macOS 下激活:

source .venv/bin/activate

激活后安装依赖:

pip install requests pandas flask pyecharts schedule

如果下载速度慢,可以切换国内 pip 镜像:

pip install requests pandas flask pyecharts schedule -i https://pypi.tuna.tsinghua.edu.cn/simple

安装完成后,可以用一行命令检查核心库是否可用:

python -c "import requests, pandas, flask, pyecharts; print('deps ok')"

输出deps ok就说明环境正常。开发工具方面,PyCharm 和 VSCode 都可以。VSCode 需要装 Python 插件,然后选择.venv作为解释器。这里特别提醒:很多人在 PyCharm 里点击运行后看到Process finished with exit code 0,页面没有输出,通常不是项目代码问题,而是没有在终端里启动 Flask,或者没有进入虚拟环境。

4. 项目总体设计

为了让期末大作业有「工程感」,代码不能全部堆在一个文件里。这里把项目拆成四个核心模块加一个入口文件。

weather_project/ ├── src/ │ ├── collector.py # 天气数据采集模块 │ ├── storage.py # 数据存储与读取模块 │ ├── analyzer.py # 数据预处理与指标计算 │ └── visualizer.py # pyecharts 可视化模块 ├── app.py # Flask 入口,提供页面和 JSON 接口 ├── templates/ │ └── index.html # 天气轮播图页面 ├── data/ │ ├── weather.csv # 采集后的原始数据 │ └── weather.json # 接口输出数据 ├── requirements.txt └── README.md

模块之间建议按下面的流程串联:

  1. collector.py负责从天气接口抓取数据,返回原始 JSON。
  2. storage.py把 JSON 扁平化,保存成 CSV 或 JSON 文件。
  3. analyzer.py用 pandas 读取 CSV,做类型转换、缺失值处理、指标计算。
  4. visualizer.py基于处理后的 DataFrame 生成图表文件。
  5. app.py启动 Flask 服务,把图表和天气卡片嵌入网页,实现轮播。
  6. 额外增加schedule定时任务,每天自动抓取并更新 CSV。

这样的结构在一个期末大作业中已经属于比较完整的设计。答辩时老师问你「模块之间怎么解耦的」,你也可以直接按照模块职责说明白。

5. 天气数据采集模块实现

5.1 数据源选择

这里选择 Open-Meteo 作为天气数据源。理由很直接:接口公开、免费、无需注册 Key、支持按经纬度查询,返回标准 JSON,非常适合教学演示。

以北京为例,请求https://api.open-meteo.com/v1/forecast,传入经纬度和参数,就能拿到实时天气和未来 7 天预报。可以用 curl 先验证接口是否可用:

curl "https://api.open-meteo.com/v1/forecast?latitude=39.9042&longitude=116.4074&current_weather=true&daily=temperature_2m_max,temperature_2m_min,weathercode&forecast_days=7&timezone=Asia%2FShanghai"

返回的 JSON 中,current_weather包含当前温度、风速、风向、天气代码;daily包含未来 7 天的最高温、最低温和天气代码。这里 weathercode 需要做一个语义映射,比如 0 表示晴,1 表示多云,2 表示阴天,3 表示阴,61 表示小雨,71 表示小雪,95 表示雷阵雨等。

如果要换成国内天气接口,例如和风天气或高德天气,思路完全一致:读取官方文档,替换接口地址和参数,再把 key 配置在环境变量或配置文件中,不要硬编码到代码里。因为 Open-Meteo 不需要 Key,项目演示更顺滑,下面代码均按这个数据源展开。

5.2 城市配置

代码中先维护一个城市列表,每个城市保存名称和经纬度。期末作业一般选择 5 到 8 个城市即可,既能看到轮播效果,又不会让接口请求太多:

# src/collector.py import time from typing import Dict, List, Any import requests CITIES = [ {"city": "北京", "lat": 39.9042, "lon": 116.4074}, {"city": "上海", "lat": 31.2304, "lon": 121.4737}, {"city": "广州", "lat": 23.1291, "lon": 113.2644}, {"city": "深圳", "lat": 22.5431, "lon": 114.0579}, {"city": "成都", "lat": 30.5728, "lon": 104.0668}, {"city": "武汉", "lat": 30.5928, "lon": 114.3055}, ] class WeatherCollector: """天气数据采集器""" def __init__(self, timeout: int = 10): self.base_url = "https://api.open-meteo.com/v1/forecast" self.timeout = timeout self.session = requests.Session() def fetch_city(self, city: Dict[str, Any]) -> Dict[str, Any]: """抓取单个城市的天气数据""" params = { "latitude": city["lat"], "longitude": city["lon"], "current_weather": "true", "daily": "temperature_2m_max,temperature_2m_min,weathercode", "forecast_days": "7", "timezone": "auto", } response = self.session.get(self.base_url, params=params, timeout=self.timeout) response.raise_for_status() data = response.json() data["city"] = city["city"] return data def fetch_batch(self, cities: List[Dict[str, Any]] = None) -> List[Dict[str, Any]]: """批量抓取多个城市天气数据""" cities = cities or CITIES results = [] for city in cities: try: print(f"开始抓取 {city['city']} 的天气数据...") data = self.fetch_city(city) results.append(data) print(f"完成抓取 {city['city']}") except Exception as exc: print(f"抓取 {city['city']} 失败: {exc}") # 控制访问频率,避免请求过于密集 time.sleep(1) return results

这段代码的关键点有三个:用requests.Session()复用连接,多城市循环时更稳定;raise_for_status()能快速暴露 HTTP 错误;每个城市抓取后睡 1 秒,避免对公共接口造成并发压力。期末答辩时如果老师问「反爬怎么办」,也可以这样解释:对公共接口要主动限速,而不是依赖随机 User-Agent 去绕反爬。

5.3 手动测试采集模块

在项目根目录写一个临时测试脚本不太优雅,建议直接在collector.py末尾加一个简单的运行入口,方便第一次验证:

if __name__ == "__main__": collector = WeatherCollector() result = collector.fetch_batch() print(f"成功抓取 {len(result)} 个城市的数据") if result: print(result[0]["city"], result[0]["current_weather"])

在终端执行:

python -m src.collector

正常情况下会看到类似输出,表示已经成功拿到每个城市的当前温度和 daily 预报。由于每次运行的数据都不同,这里不固定输出内容。如果你运行后直接结束且没有任何输出,多半是模块导入路径问题,改为python src/collector.py,或者在项目根目录pip install -e .安装为本地包即可。

6. 数据存储与预处理

6.1 扁平化保存 CSV

接口返回的 JSON 是嵌套结构,直接存盘时不好看。先做一个扁平化函数,把城市名、当前温度、天气代码、未来 7 天最高温、最低温整理成表格:

# src/storage.py import csv import json from pathlib import Path from typing import Dict, Any, List def flatten_weather_data(data: Dict[str, Any]) -> Dict[str, Any]: """将接口返回的嵌套 JSON 扁平化""" current = data.get("current_weather", {}) daily = data.get("daily", {}) max_temps = daily.get("temperature_2m_max", []) min_temps = daily.get("temperature_2m_min", []) codes = daily.get("weathercode", []) dates = daily.get("time", []) flat = { "city": data.get("city", ""), "time": current.get("time", ""), "current_temp": current.get("temperature", ""), "wind_speed": current.get("windspeed", ""), } # 把未来7天预报展开成字段,最多保留7天 for i in range(7): if i < len(dates): flat[f"day_{i + 1}_date"] = dates[i] flat[f"day_{i + 1}_max"] = max_temps[i] if i < len(max_temps) else "" flat[f"day_{i + 1}_min"] = min_temps[i] if i < len(min_temps) else "" flat[f"day_{i + 1}_code"] = codes[i] if i < len(codes) else "" return flat

再提供两个存储函数,分别写 CSV 和 JSON:

def save_to_csv(results: List[Dict[str, Any]], path: str = "data/weather.csv") -> None: """把多个城市的扁平化结果写入 CSV""" Path(path).parent.mkdir(parents=True, exist_ok=True) rows = [flatten_weather_data(item) for item in results] if not rows: print("没有可保存的数据") return fieldnames = list(rows[0].keys()) with open(path, "w", newline="", encoding="utf-8-sig") as f: writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(rows) print(f"CSV 保存成功: {path}") def save_to_json(results: List[Dict[str, Any]], path: str = "data/weather.json") -> None: """把原始 JSON 保存下来,供 Flask 接口直接读取""" Path(path).parent.mkdir(parents=True, exist_ok=True) with open(path, "w", encoding="utf-8") as f: json.dump(results, f, ensure_ascii=False, indent=2) print(f"JSON 保存成功: {path}")

CSV 使用utf-8-sig编码,可以直接用 Excel 打开,不会出现中文乱码。这个细节在期末大作业展示中很加分。

6.2 pandas 读取与指标计算

数据预处理放到analyzer.py中。用 pandas 读 CSV,然后把需要计算的字段转成数值类型,避免后面画图时报错:

# src/analyzer.py import pandas as pd def load_weather_data(csv_path: str = "data/weather.csv") -> pd.DataFrame: """读取 CSV 并做基础类型转换""" df = pd.read_csv(csv_path, dtype={"city": str}) for col in ["current_temp", "wind_speed"]: if col in df.columns: df[col] = pd.to_numeric(df[col], errors="coerce") return df def calculate_summary(df: pd.DataFrame) -> pd.DataFrame: """计算每个城市的平均最高温和最低温""" summary = df.groupby("city").agg( current_temp=("current_temp", "mean"), avg_max_7d=("day_1_max", "mean"), avg_min_7d=("day_1_min", "mean"), ).round(2).reset_index() return summary

这里并不产出过于复杂的聚合结果,因为期末项目更重要的是把流程讲清楚:读取、转换、聚合、输出。如果你希望展示更多分析能力,可以再补一个「温差最大城市」排名:

def top_temp_diff(df: pd.DataFrame) -> pd.DataFrame: """计算未来7天温差最大的城市""" diff_cols = [f"day_{i}_max" for i in range(1, 8)] df["max_7d"] = df[diff_cols].max(axis=1) df["min_7d"] = df[diff_cols].min(axis=1) df["temp_diff"] = df["max_7d"] - df["min_7d"] return df.sort_values("temp_diff", ascending=False)[["city", "max_7d", "min_7d", "temp_diff"]]

这样期末大作业的核心输出就不只是「画了几张图」,而是能从数据中发现规律,比如某个城市昼夜温差大、某两天降温明显。这是数据可视化项目里很重要的加分点。

7. 数据可视化:静态图表

7.1 生成折线图与柱状图

pyecharts 是生成 Python 图表比较方便的方式。它会输出独立 HTML 文件,双击即可在浏览器查看,非常适合课程演示。这里以绘制「未来 7 天最高温最低温折线图」为例:

# src/visualizer.py import pandas as pd from pyecharts.charts import Line, Bar from pyecharts import options as opts def make_temperature_line(city: str, df: pd.DataFrame, output_path: str = "outputs/") -> str: """绘制单个城市的未来7天温度趋势折线图""" city_df = df[df["city"] == city] if city_df.empty: raise ValueError(f"没有找到 {city} 的数据") dates = [str(city_df.iloc[0][f"day_{i}_date"]) for i in range(1, 8)] high = [round(city_df.iloc[0][f"day_{i}_max"]) for i in range(1, 8)] low = [round(city_df.iloc[0][f"day_{i}_min"]) for i in range(1, 8)] line = ( Line() .add_xaxis(dates) .add_yaxis("最高温度", high, is_smooth=True) .add_yaxis("最低温度", low, is_smooth=True) .set_global_opts( title_opts=opts.TitleOpts(title=f"{city} 未来7天温度趋势"), tooltip_opts=opts.TooltipOpts(trigger="axis"), yaxis_opts=opts.AxisOpts(name="温度 / ℃"), xaxis_opts=opts.AxisOpts(name="日期"), ) ) return line.render(output_path + f"{city}_temperature_line.html")

再看一个柱状图,用来比较多个城市的当前温度:

def make_current_temp_bar(df: pd.DataFrame, output_path: str = "outputs/") -> str: """绘制多城市当前温度柱状图""" data = df.groupby("city")["current_temp"].mean().round(1).reset_index() bar = ( Bar() .add_xaxis(data["city"].tolist()) .add_yaxis("当前温度", data["current_temp"].tolist()) .set_global_opts( title_opts=opts.TitleOpts(title="多城市当前温度对比"), yaxis_opts=opts.AxisOpts(name="温度 / ℃"), ) ) return bar.render(output_path + "current_temp_bar.html")

注意output_path目录要先创建,否则渲染时会报错。可以在visualizer.py顶部加一行:

from pathlib import Path Path("outputs").mkdir(parents=True, exist_ok=True)

7.2 批量生成所有城市图表

由于期末作业里有多个城市,应该提供一个批量生成入口,直接把所有城市的折线图都生成出来:

def generate_all_charts(df: pd.DataFrame, output_path: str = "outputs/") -> list: """批量生成所有城市的可视化 HTML 文件""" paths = [] Path(output_path).mkdir(parents=True, exist_ok=True) for city in df["city"].unique(): path = make_temperature_line(city, df, output_path) paths.append(path) paths.append(make_current_temp_bar(df, output_path)) return paths if __name__ == "__main__": df = pd.read_csv("data/weather.csv") files = generate_all_charts(df) for f in files: print(f"已生成: {f}")

运行后会在outputs/目录下生成多个 HTML 文件。这里生成的图表是静态的,适合放在作业报告里截图,也可以作为答辩演示材料。

8. 天气轮播图实现

8.1 Flask 服务启动

静态图表是展示的一部分,但“天气轮播图”需要放到 Web 页面里自动切换。这里用 Flask 启动一个本地 Web 服务。app.py的核心逻辑:启动时先确保数据存在,然后提供首页路由和 JSON 数据接口。

# app.py import json import pandas as pd from flask import Flask, render_template, jsonify from src.collector import WeatherCollector, CITIES from src.storage import save_to_csv, save_to_json from src.visualizer import generate_all_charts app = Flask(__name__) WEATHER_CSV = "data/weather.csv" WEATHER_JSON = "data/weather.json" def refresh_data() -> None: """重新抓取天气数据并保存""" collector = WeatherCollector() results = collector.fetch_batch(CITIES) save_to_csv(results, WEATHER_CSV) save_to_json(results, WEATHER_JSON) @app.route("/") def index(): """首页:展示天气轮播图页面""" return render_template("index.html") @app.route("/api/weather") def api_weather(): """JSON 接口:返回天气数据,供前端轮播图使用""" try: with open(WEATHER_JSON, "r", encoding="utf-8") as f: data = json.load(f) return jsonify({"code": 0, "data": data}) except FileNotFoundError: return jsonify({"code": 1, "message": "数据文件不存在,请先抓取数据"}), 404 if __name__ == "__main__": import os if not os.path.exists(WEATHER_JSON): refresh_data() app.run(host="127.0.0.1", port=5000, debug=True)

在终端启动:

python app.py

启动后访问http://127.0.0.1:5000。如果端口被占用,可以改成 5001:

python app.py --port=5001

但这种写法需要额外参数处理,更直接的方法是修改app.run(port=5001)。平时本地测试建议用127.0.0.1,不要在公网随意开放这个服务。

8.2 数据接口 API 调用示例

/api/weather是纯 JSON 接口,前端可以用 fetch 调用,也可以直接用 curl 验证:

curl http://127.0.0.1:5000/api/weather

Python 侧也能调用:

import requests url = "http://127.0.0.1:5000/api/weather" response = requests.get(url, timeout=10) if response.status_code == 200: data = response.json() print(data["code"]) print(len(data["data"]))

这个接口的价值在于把「爬虫数据」和「前端展示」解耦。即使以后换一套前端,后端接口不用改。

8.3 前端轮播页面

templates/index.html中,先放一个天气卡片容器,再用 JavaScript 定时切换城市。这里使用原生 fetch 获取数据,然后用setInterval实现轮播。为了控制篇幅,CSS 只写核心样式:

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>天气轮播图</title> <style> body { font-family: "Microsoft YaHei", sans-serif; background: #1e2b3a; color: #fff; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; } .weather-card { width: 520px; background: rgba(255, 255, 255, 0.1); border-radius: 16px; padding: 24px; text-align: center; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3); } .city-name { font-size: 32px; font-weight: bold; } .temp { font-size: 56px; margin: 8px 0; } .desc { font-size: 20px; color: #ddd; } </style> </head> <body> <div class="weather-card" id="weatherCard"> <div class="city-name" id="cityName">--</div> <div class="temp" id="temp">-- ℃</div> <div class="desc" id="weatherDesc">--</div> </div> <script> const WEATHER_CODE_MAP = { 0: "晴", 1: "多云", 2: "阴", 3: "阴", 45: "雾", 51: "毛毛雨", 61: "小雨", 71: "小雪", 80: "阵雨", 95: "雷阵雨" }; let weatherList = []; let currentIndex = 0; function formatWeatherCode(code) { return WEATHER_CODE_MAP[code] || "未知"; } function displayCity(index) { if (!weatherList.length) return; const item = weatherList[index]; const current = item.current_weather; document.getElementById("cityName").textContent = item.city; document.getElementById("temp").textContent = current.temperature + " ℃"; document.getElementById("weatherDesc").textContent = formatWeatherCode(current.weathercode); } function startRotation() { setInterval(() => { currentIndex = (currentIndex + 1) % weatherList.length; displayCity(currentIndex); }, 3000); } fetch("/api/weather") .then(res => res.json()) .then(data => { if (data.code === 0) { weatherList = data.data; displayCity(0); startRotation(); } }) .catch(err => { console.error("加载天气数据失败:", err); }); </script> </body> </html>

轮播的核心逻辑很简单:setInterval每 3 秒把城市索引 +1,取模后重新渲染页面上的城市名、温度和天气描述。如果以后想加入未来 7 天趋势,可以在卡片下方放一个 ECharts 图表容器,切换城市时同步更新图表数据。

到这里,一个完整的天气轮播图项目已经能跑通:抓取数据保存 CSV/JSON,Flask 提供页面和接口,浏览器自动轮播多个城市天气。

9. 批量任务与定时更新

期末大作业如果能展示「定时更新」,工程完成度会明显提高。这个项目里可以加入两个扩展:

9.1 用 schedule 做每日定时抓取

在主程序里加一个定时任务,每天上午 8 点自动刷新一次数据:

# tasks.py import schedule import time from app import refresh_data def start_scheduler(): schedule.every().day.at("08:00").do(refresh_data) print("定时任务已启动,每天 08:00 更新天气数据") while True: schedule.run_pending() time.sleep(60)

执行时单独起一个终端:

python tasks.py

这样做的好处是:期末报告里可以写「本项目支持每日定时采集,采集结果自动落盘」,而不是只做一次性抓取。如果你的课程不要求定时调度,也可以跳过这一步,不影响主流程。

9.2 批量任务日志与失败重试

批量抓取天气数据时,网络波动可能导致某个城市失败。建议在采集模块里加入失败重试机制:

def fetch_with_retry(self, city, retries: int = 3): """带重试机制的抓取函数""" for attempt in range(1, retries + 1): try: return self.fetch_city(city) except Exception as exc: print(f"第 {attempt} 次尝试失败: {exc}") time.sleep(2) return None

fetch_batch里调用这个带重试的函数,如果返回None就把该城市跳过,不影响其他城市。批量任务需要做好日志记录,最好写一个log.txt,把每次抓取成功和失败的情况记录下来,方便排查问题。不要把失败信息只打在控制台,因为控制台内容在关闭后不会保留。

10. 资源占用与运行性能

这个项目不需要 GPU,显存占用不涉及。运行时主要观察以下三个指标:

内存占用方面,pandas加载一份几 KB 到几十 KB 的天气 CSV,内存占用通常在几十 MB;Flask 服务本身也很轻量。普通笔记本完全跑得动。

网络请求方面,抓取 6 个城市,每个城市 1 个请求,加上time.sleep(1),整个过程会持续 10 到 20 秒。如果批量城市数量扩大,请求时间会线性增长。建议控制并发,不要同时发起几十个请求打公共接口。你在答辩时可以主动说明这一点:项目中没有盲目使用多线程,而是选择了稳定优先的串行采集,这是对公共数据源负责的做法。

CPU 占用方面,更多发生在 pyecharts 渲染图表时。渲染多个城市的 HTML 图表通常需要几秒钟,属于正常现象。如果你发现图表生成很慢,可以检查是否同时打开了多个大型 HTML 文件,或者把outputs/目录清空后再生成。

页面加载方面,Flask 默认单线程工作,如果开启 debug,每次代码变更会自动重启。真实演示时,建议关闭 debug,只运行app.run(host="127.0.0.1", port=5000),避免浏览器请求时服务重启导致卡顿。

端口冲突也是一个常见问题。如果启动app.py时提示端口被占用,可以用下面的命令检查:

netstat -ano | findstr :5000

找到占用进程后结束进程,或者直接把端口改成 5001、5002。

11. 常见问题与排查方法

问题现象可能原因排查方式解决方案
点击运行后只显示Process finished with exit code 0没有启动 Flask 服务,只是执行了脚本看终端是否有Running on http://127.0.0.1:5000输出切换到项目终端执行python app.py
请求天气接口超时网络环境无法访问外部接口,或接口响应慢用 curl 直接测试接口地址检查网络;重试;换用代理环境时注意合规
CSV 中中文乱码编码写成了 utf-8 而不是 utf-8-sig用记事本或 Excel 打开检查写入 CSV 时encoding='utf-8-sig'
pyecharts 图表打开为空白浏览器未加载 ECharts 静态资源查看浏览器控制台报错确认输出 HTML 文件路径正确;不用 iframe 嵌套
Flask 页面能打开但接口 404数据文件不存在或路由写错访问/api/weather看返回先运行refresh_data(),或检查data/weather.json是否存在
端口被占用5000 端口已被其他程序使用netstat -ano | findstr :5000换端口app.run(port=5001)
批量抓取部分城市失败网络波动或请求参数异常查看控制台打印的失败信息增加重试机制,失败城市跳过继续
pandas 读取时报字段不存在CSV 字段名不匹配print(df.columns)检查列名统一字段名,避免手动改表头
前端轮播不切换城市fetch 没拿到数据或 JS 报错打开浏览器开发者工具看 Console确认接口返回 JSON 格式;检查weatherList是否为空
页面显示天气代码不是中文weathercode 映射不完整检查接口返回的 weathercode 值补全映射字典,或输出原始 code 调试

这里需要特别强调:如果接口请求返回 429 或 403,大概率是请求频率过高或缺少必要的请求头。本项目使用 Open-Meteo 免费接口时,要遵守其服务条款,不要高频请求。如果换成国内天气接口,则需要注册账号并申请 API Key,把 Key 配置到环境变量中,而不是提交到公开仓库。

12. 最佳实践与合规提醒

这个 Python 爬虫期末大作业项目虽然代码量不大,但要写成一个合格的项目,还要注意以下几点。

第一,配置文件与代码分离。不要把 API Key、数据库密码等敏感信息写死在代码里。可以用.env文件或环境变量,比如:

import os WEATHER_API_KEY = os.getenv("WEATHER_API_KEY", "")

第二,数据目录与输出目录分离。data/放原始数据,outputs/放生成的可视化 HTML,日志单独放logs/。这样项目看起来更专业,也方便清理临时文件。

第三,抓取数据时要控制频率。即使接口免费,也不能无限循环请求。批量抓取时至少加入 1 秒延迟,设置单次任务的总请求数上限。

第四,版权与合规。天气数据来自公开接口或网页时,要注意服务条款。用于课程作业、学习测试没问题,但如果要把项目发布到 GitHub 或部署到公网,需要去除所有敏感信息,并确认数据源允许使用。涉及用户隐私的数据坚决不爬、不存、不展示。

第五,代码要加注释和 README。期末大作业的评分老师通常关注代码可读性。在每个模块文件顶部写清楚模块作用,函数内部加适量注释,README 中写清运行步骤、依赖、截图,这种「额外文档」往往是拿高分的关键。

第六,输出内容要复核。天气轮播图里展示的城市名、温度、天气描述,都应在发布前人工检查一遍。可视化项目里出现过数据单位不一致、时间字段偏移等问题,都可能影响最终展示效果。

13. 总结与下一步

这个项目最值得尝试的点在于:它不依赖付费 API Key,不依赖 GPU,不依赖复杂的数据库,用一套简单清晰的代码就把「爬虫采集、数据存储、数据分析、可视化、Web 展示、定时任务」串了起来。无论是期末大作业,还是想快速入门 Python 爬虫与数据可视化,都值得照着跑一遍。

第一个要验证的功能,是src/collector.py能不能成功抓取到天气 JSON。只要这个模块通了,后面的数据存储、图表和轮播页面就都有数据支撑。最容易踩的坑是直接运行app.py时发现没有data/weather.json,页面打开后是空的。解决办法也很简单:第一次运行前先执行一次refresh_data(),或者直接运行python app.py,让入口函数自动判断并完成首次抓取。

后续可以继续扩展的方向包括:接入国内天气接口,增加空气质量指数字段;在轮播页面中加入未来 7 天 ECharts 趋势图;使用 MySQL 或 SQLite 代替 CSV 做数据持久化;用 Redis 做接口缓存,降低抓取频率;加上简单的用户登录和收藏城市功能。每一步扩展都不需要重构现有代码,模块化设计已经为后续留下了空间。

建议直接把这个项目保存到本地,按文中的目录结构创建文件,依次运行采集、存储、可视化和 Flask 服务,然后用浏览器打开天气轮播图页面,把效果截图放进期末报告。剩下的时间可以优化样式、增加城市、补全测试用例,这样交上去的期末大作业会显得完整且扎实。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询