1 阶段知识串讲 —— 把 Day 22~30 串成一张网
是什么?为什么学?
今天是收官日,不学新知识——把 30 天学的所有东西组装成一辆能跑的车:从网络拿数据(Day 26/27)、用表格分析(Day 22/23)、画图(Day 25)、存数据库(Day 28)、做成网页(Day 29)。做完这个项目,你就从”会写脚本”升级成”能独立交付一个小产品”了。如果中间卡住,回头翻对应天数的文档,这本身就是复盘。
底层原理
一句话串法:NumPy 打底 → Pandas 管表 → 数据清洗保质量 → matplotlib 出图 → 爬虫取网页数据 → API 调服务数据 → SQLite 落地存储 → Flask 展示成网页。阶段知识地图:
Day 22 NumPy(数组与向量化运算)────────────┐
Day 23 Pandas(DataFrame 表格分析)──────────┤
Day 24 数据清洗(缺失值/重复值/类型转换)──────┤
Day 25 matplotlib(折线/柱状/散点/饼图)──────┤
Day 26 爬虫(requests + BeautifulSoup)──────┤
Day 27 Web API(GET/POST/JSON 调用)─────────┤
Day 28 SQLite(建表/CRUD/事务/防注入)────────┤
Day 29 Flask(路由/模板/表单/JSON 接口)──────┤
└──→ 综合项目:天气数据综合分析
天气项目把这八样按流水线顺序组装:API 拿数据(Day 27)→ Pandas 分析(Day 23/24)→ 画图(Day 25)→ 入库(Day 28)→ 网页展示(Day 29)——每一步都在用前面某天的知识,缺一环整条链就断。
生活类比
像做一道完整的菜:买菜(API 取数据)→ 洗菜切菜(Pandas 分析)→ 摆盘(matplotlib 画图)→ 放冰箱冷藏(SQLite 存储)→ 端上桌(Flask 展示)。少了任何一环,这道菜都端不上来。
注意事项总结
每个知识点在本项目里干什么:
- **NumPy(Day 22)——数值计算的”引擎”**:Pandas 的底层就是 NumPy——
df["tmax"]取出的每一列本质是 NumPy 数组;df["tmax"].mean()、idxmax()这些统计背后都是 NumPy 在算。你直接写 NumPy 的地方不多,但它支撑着上面所有的分析。 - Pandas(Day 23)——分析的主战场:
pd.DataFrame(data)把天气数据装进表格;df.shape看形状、df.head(3)看样例、describe()出统计、df.loc[df["tmax"].idxmax()]找最热一天、nlargest(3, "rain")找降雨前三。 - 数据清洗(Day 24)——保证数据质量:
resp.raise_for_status()拦截失败请求、describe()函数翻译天气代码、INSERT OR IGNORE去重。真实项目里 80% 的时间在清洗数据。 - matplotlib(Day 25)——把数字变成图形:
charts.py生成温度折线图和降水柱状图;plt.xticks(rotation=30)防日期重叠、tight_layout防裁切、savefig存 PNG、close释放内存。 - 爬虫(Day 26)——从网页拿数据:项目用官方 API,但合规意识和错误处理仍然适用——
timeout=10防卡死、raise_for_status()检查状态、不频繁请求。 - Web API(Day 27)——和服务器对话:**这是项目的”水源”**——
weather_data.py用requests.get+params发请求,resp.json()["daily"]取出数据,zip打包成列表。没有这一步,后面全部是空谈。 - SQLite(Day 28)——数据的”仓库”:
date做主键、INSERT OR IGNORE保证同一日期只存一次(幂等:跑两次第二次新增 0 条);load_weather()读回数据供网页展示;全程参数化查询。 - Flask(Day 29)——把成果展示出去:
/路由跑一遍完整流水线,把统计结果传给report.html渲染;summary["table"]用{% for %}循环渲染成表格;图片从/static/提供。
项目拆解:五步流水线 → 知识点对照
| 环节 | 文件 | 用到的知识点 | 来自哪几天 | 在本项目中的作用 |
|---|---|---|---|---|
| 1. 数据获取 | weather_data.py | requests、params、JSON 解析、状态码、函数 | Day 27 / 8 / 13 | 从 Open-Meteo 拿到北京 7 天天气,翻译天气代码 |
| 2. 数据分析 | analysis.py | DataFrame、describe、loc、idxmax、nlargest | Day 23 / 22 | 统计、找最热一天、降雨前三、平均温度 |
| 3. 可视化 | charts.py | plot、bar、savefig、close、中文字体 | Day 25 | 生成温度折线图和降水柱状图 PNG |
| 4. 数据存储 | database.py | 建表、INSERT OR IGNORE、主键、参数化查询、事务 | Day 28 | 数据入库且同日期去重(幂等),可反复运行 |
| 5. 网页展示 | app.py + report.html | 路由、render_template、Jinja2 循环、static | Day 29 / 18 | 把分析结果 + 图表渲染成浏览器报告页 |
观察这张表:项目的「数据能力」来自 Day 22~28,项目的「出口」来自 Day 29。能讲清这张表,你就真正学会了这 30 天。
易错点
- 错误写法:一口气写完五个文件再运行 → 问题:出问题范围大、难定位 → 正确写法:每完成一个环节就运行一次、验证一次(小步迭代)。
- 错误写法:找最大值用
df[df["tmax"].max()]→ 问题:拿”最大值”去和整列比较,条件永远不成立,返回空 → 正确写法:df.loc[df["tmax"].idxmax()](idxmax 找行号 + loc 取整行)。 - 错误写法:每刷新一次页面就请求一次外部 API → 问题:慢、费流量、有频率限制,可能 502 → 正确写法:优先从数据库读,没有数据才请求 API 并入库(缓存思路)。
记忆口诀:取数分析画出图,入库展示五步走;一环扣一环,缺一就断链。
2 第 1 步:数据获取(weather_data.py)
是什么?为什么学?
用 Open-Meteo 拿北京 7 天的最高温、最低温、降水、天气代码,返回成列表。这是项目的”水源”——没有这一步,后面全部是空谈。
底层原理
requests.get + params 把经纬度、天数、需要的气象字段发给 Open-Meteo 官方 API;resp.raise_for_status() 拦截失败请求;resp.json()["daily"] 取出 7 天的温度、降水、天气代码;zip 把多个列表打包成一条条记录。
代码演示
# weather_data.py —— 从 Open-Meteo 获取北京 7 天天气
import requests
# 北京经纬度
BEIJING = (39.9042, 116.4074)
def fetch_weather(days=7):
"""返回 [{date, tmax, tmin, rain, code}, ...] 列表"""
params = {
"latitude": BEIJING[0],
"longitude": BEIJING[1],
"daily": ("temperature_2m_max,temperature_2m_min,"
"precipitation_sum,weather_code"),
"timezone": "Asia/Shanghai",
"forecast_days": days,
}
resp = requests.get("https://api.open-meteo.com/v1/forecast",
params=params, timeout=10)
resp.raise_for_status()
daily = resp.json()["daily"]
# weather_code:0 晴,1~3 多云/阴,51+ 雨,71+ 雪(Open-Meteo 官方编码)
def describe(code):
if code == 0:
return "晴"
if code in (1, 2, 3):
return "多云"
if 51 <= code <= 67:
return "雨"
if 71 <= code <= 86:
return "雪"
return "其它"
result = []
for d, tmax, tmin, rain, code in zip(
daily["time"], daily["temperature_2m_max"],
daily["temperature_2m_min"], daily["precipitation_sum"],
daily["weather_code"]):
result.append({
"date": d,
"tmax": tmax,
"tmin": tmin,
"rain": rain,
"weather": describe(code),
})
return result
if __name__ == "__main__":
data = fetch_weather()
for item in data:
print(item)
运行结果(示例,数据随当天变化):
{'date': '2026-08-04', 'tmax': 34.1, 'tmin': 25.5, 'rain': 0.4, 'weather': '雨'}
{'date': '2026-08-05', 'tmax': 37.2, 'tmin': 25.6, 'rain': 0.0, 'weather': '多云'}
...
注意事项总结:weather_code 是 Open-Meteo 官方天气代码,在 describe 函数里翻译成中文——数据在源头清理好,下游全都省事(Day 24 数据清洗的核心思路 + Day 15 单一职责)。运行成功说明第 1 步完成。
易错点
- 错误写法:不设
timeout→ 问题:网络卡住时程序无限等待 → 正确写法:requests.get(..., timeout=10)。 - 错误写法:不检查状态码直接
resp.json()→ 问题:请求失败时解析报错 → 正确写法:先resp.raise_for_status()。 - 错误写法:天气代码不翻译直接存 → 问题:下游每个环节都要自己翻译一遍 → 正确写法:
fetch_weather返回时已带中文weather字段。
记忆口诀:源头先把数据清,下游全都省力气;代码翻译放一处,处处都不用再译。
3 第 2 步:数据分析(analysis.py)
是什么?为什么学?
把数据放进 Pandas DataFrame(Day 22/23 学的),做统计、排序、筛选。这是项目的”分析大脑”。
代码演示
# analysis.py —— 用 Pandas 分析天气数据
import pandas as pd
from weather_data import fetch_weather
def analyze(data):
"""把天气数据转成 DataFrame 并做统计"""
df = pd.DataFrame(data)
print("数据形状:", df.shape)
print("\n前 3 行:")
print(df.head(3))
print("\n基本统计:")
print(df[["tmax", "tmin", "rain"]].describe())
print("\n最热的一天:")
hottest = df.loc[df["tmax"].idxmax()]
print(f"{hottest['date']},最高 {hottest['tmax']}℃")
print("\n降雨最多的三天:")
print(df.nlargest(3, "rain")[["date", "rain"]])
print("\n平均温度(摄氏度,保留 1 位):")
avg = (df["tmax"] + df["tmin"]) / 2
print(round(avg.mean(), 1))
return df
if __name__ == "__main__":
df = analyze(fetch_weather())
运行结果(示例,数据随当天变化):
数据形状: (7, 5)
前 3 行:
date tmax tmin rain weather
0 2026-08-04 34.1 25.5 0.4 雨
1 2026-08-05 37.2 25.6 0.0 多云
2 2026-08-06 39.3 27.6 0.0 多云
...
最热的一天:
2026-08-06,最高 39.3℃
注意事项总结:df.loc[df["tmax"].idxmax()] 是先找最大值的行号(idxmax),再用 loc 取那一行——Pandas 里最常用的”找极值”套路。describe() 出统计、nlargest(3, "rain") 找降雨前三、(df["tmax"] + df["tmin"]) / 2 是向量化计算平均温度。
易错点
- 错误写法:
df[df["tmax"].max()]→ 问题:拿”最大值 39.3″和整列比较,没有任何一行”等于”最大值本身,返回空 → 正确写法:df.loc[df["tmax"].idxmax()](先找位置再取行)。 - 错误写法:忘了
round(avg.mean(), 1)→ 问题:平均温度一长串小数 → 正确写法:保留 1 位小数展示。 - 错误写法:直接在
print里反复算df["tmax"].mean()→ 问题:重复计算、代码啰嗦 → 正确写法:先用avg = (df["tmax"] + df["tmin"]) / 2存变量再用。
记忆口诀:找极值先 idxmax 定位,再用 loc 取整行;Pandas 套路记心头。
4 第 3 步:可视化(charts.py)
是什么?为什么学?
用 matplotlib 生成两张图:温度折线图、降水柱状图,保存成 PNG 供网页展示。图是给”人”看的,这一步让项目有了”门面”。
代码演示
# charts.py —— 生成天气图表
import matplotlib.pyplot as plt
# 中文字体设置(画图脚本必备,见 Day 25)
plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei"]
plt.rcParams["axes.unicode_minus"] = False
def make_charts(df):
"""根据 DataFrame 生成两张图:temperature.png 和 rain.png"""
dates = df["date"].tolist()
# 图 1:最高/最低温折线图
plt.figure(figsize=(8, 4))
plt.plot(dates, df["tmax"], marker="o", label="最高温")
plt.plot(dates, df["tmin"], marker="s", label="最低温")
plt.title("北京未来 7 天温度走势")
plt.xlabel("日期")
plt.ylabel("温度(℃)")
plt.legend()
plt.grid(True, linestyle="--", alpha=0.5)
plt.xticks(rotation=30) # 日期斜着显示,避免重叠
plt.tight_layout()
plt.savefig("temperature.png", dpi=150)
plt.close() # 关闭画布,释放内存
# 图 2:降水量柱状图
plt.figure(figsize=(8, 4))
plt.bar(dates, df["rain"], color="steelblue")
plt.title("北京未来 7 天降水量")
plt.xlabel("日期")
plt.ylabel("降水量(mm)")
plt.xticks(rotation=30)
plt.tight_layout()
plt.savefig("rain.png", dpi=150)
plt.close()
print("已生成 temperature.png 和 rain.png")
if __name__ == "__main__":
import pandas as pd
from weather_data import fetch_weather
make_charts(pd.DataFrame(fetch_weather()))
运行结果:
已生成 temperature.png 和 rain.png
注意事项总结:工程化注意——生成图表用 plt.close() 关闭画布,否则在脚本里多次 plt.figure() 会累计内存。图片保存在和 charts.py 相同的目录(project/backend/ 下),第 5 步的 Flask 直接引用(需挪到 static/)。
易错点
- 错误写法:画图脚本少了中文字体设置 → 问题:图表中文显示成方框 → 正确写法:开头加
plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei"]。 - 错误写法:
plt.figure()后不plt.close()→ 问题:多次画图内存累计 → 正确写法:savefig后plt.close()。 - 错误写法:日期标签横排 → 问题:7 个日期挤在一起看不清 → 正确写法:
plt.xticks(rotation=30)斜着显示 +tight_layout防裁切。
记忆口诀:中文字体先设好,画完记得 close 掉;日期斜放不重叠,savefig 存图网页要。
5 第 4 步:数据存储(database.py)
是什么?为什么学?
把天气数据存进 SQLite,关键点:同一日期只存一次(重复入库会跳过)。用 Day 28 学的参数化查询和事务。这一步让数据”沉淀”下来,不依赖每次现抓。
代码演示
# database.py —— 天气数据存入 SQLite
import sqlite3
DB_FILE = "weather.db"
def init_db():
conn = sqlite3.connect(DB_FILE)
conn.execute("""
CREATE TABLE IF NOT EXISTS daily_weather (
date TEXT PRIMARY KEY, -- 日期做主键,天然去重
tmax REAL,
tmin REAL,
rain REAL,
weather TEXT
)
""")
conn.commit()
conn.close()
def save_weather(data):
"""把天气数据写入数据库;同一日期已存在则跳过(INSERT OR IGNORE)"""
conn = sqlite3.connect(DB_FILE)
inserted = 0
for item in data:
cur = conn.execute(
"INSERT OR IGNORE INTO daily_weather "
"(date, tmax, tmin, rain, weather) VALUES (?, ?, ?, ?, ?)",
(item["date"], item["tmax"], item["tmin"],
item["rain"], item["weather"]),
)
inserted += cur.rowcount # 0 表示已存在,跳过
conn.commit()
conn.close()
return inserted
def load_weather():
"""从数据库读回全部数据(用于网页展示)"""
conn = sqlite3.connect(DB_FILE)
rows = conn.execute(
"SELECT date, tmax, tmin, rain, weather "
"FROM daily_weather ORDER BY date"
).fetchall()
conn.close()
return [
{"date": r[0], "tmax": r[1], "tmin": r[2], "rain": r[3], "weather": r[4]}
for r in rows
]
if __name__ == "__main__":
from weather_data import fetch_weather
init_db()
new = save_weather(fetch_weather())
print(f"新增 {new} 条(重复日期会自动跳过)")
print("数据库中现有数据:")
for row in load_weather():
print(row)
运行结果(示例):
新增 7 条(重复日期会自动跳过)
数据库中现有数据:
{'date': '2026-08-04', 'tmax': 34.1, 'tmin': 25.5, 'rain': 0.4, 'weather': '雨'}
...
注意事项总结:INSERT OR IGNORE 配合主键 date:日期已存在就忽略。再运行一次,新增是 0 条——这就是”幂等”,重复执行不产生副作用。相比每次先 SELECT 查重,这一行 SQL 更简洁可靠。全程参数化查询防注入 + commit() 事务。
易错点
- 错误写法:直接
INSERT撞了主键 → 问题:报sqlite3.IntegrityError: UNIQUE constraint failed→ 正确写法:用INSERT OR IGNORE,或先查重再插入。 - 错误写法:忘了
conn.commit()→ 问题:数据没真正写入文件 → 正确写法:写操作后conn.commit()。 - 错误写法:SQL 里字符串拼接 → 问题:SQL 注入风险 → 正确写法:一律
?占位 + 元组传参。
记忆口诀:主键天然去重,IGNORE 跳过重复;幂等跑两遍,新增仍是零。
6 第 5 步:网页展示(app.py + report.html)
是什么?为什么学?
最后用 Flask 把分析结果和两张图展示成网页(端口 2050)。这一步让整个项目的成果变成浏览器里的一页报告。
代码演示
# app.py —— Flask 展示天气分析报告
from flask import Flask, render_template
import pandas as pd
from weather_data import fetch_weather
from analysis import analyze
from charts import make_charts
from database import init_db, save_weather, load_weather
app = Flask(__name__)
def run_pipeline():
"""执行完整流水线:获取 -> 分析 -> 画图 -> 入库 -> 返回数据"""
data = fetch_weather()
df = analyze(data)
make_charts(df)
init_db()
save_weather(data)
return df
@app.route("/")
def report():
# 首次访问自动跑一遍流水线;也可以改成从数据库读
df = run_pipeline()
hottest = df.loc[df["tmax"].idxmax()]
avg_temp = round(((df["tmax"] + df["tmin"]) / 2).mean(), 1)
total_rain = round(df["rain"].sum(), 1)
summary = {
"days": len(df),
"avg_temp": avg_temp,
"total_rain": total_rain,
"hottest_date": hottest["date"],
"hottest_max": hottest["tmax"],
"table": df.to_dict("records"), # DataFrame 转成列表给模板用
}
return render_template("report.html", summary=summary)
if __name__ == "__main__":
app.run(host="127.0.0.1", port=2050, debug=True)
模板 templates/report.html 要点:数据卡片(平均温度 / 总降水量 / 最热一天)+ 每日天气明细表格({% for row in summary["table"] %} 循环)+ 两张图(<img src="/static/temperature.png">)。
注意事项总结:Flask 默认从 static/ 目录提供图片,所以把两张图挪过去:
# 在 project/backend 下执行
mkdir static
move temperature.png rain.png static/
启动:
cd project/backend
python app.py
浏览器打开 http://127.0.0.1:2050/ 就能看到完整的分析报告。升级挑战:把 run_pipeline 改成”优先从数据库读,没有数据才去请求 API”;或加 ?city= 参数支持多个城市。
易错点
- 错误写法:图表保存后网页显示 404 → 问题:Flask 默认只从
static/目录提供静态文件,而图在根目录 → 正确写法:把 PNG 挪进static/,模板路径写/static/...。 - 错误写法:每次访问页面都请求外部 API → 问题:慢、费流量、可能 502 → 正确写法:优先
load_weather()从数据库读,没有才请求并入库。 - 错误写法:
move命令在 PowerShell 不识别 → 问题:命令执行失败 → 正确写法:用mv temperature.png rain.png static/(Git Bash)或直接在文件管理器拖动。
记忆口诀:静态图片放 static,网页才能找得到;数据先读库里存,省时省力不怕断。
7 30 天复盘清单 —— 查漏补缺
是什么?为什么学?
复盘是今天最重要的事:对照清单逐条打勾,勾不上的回去重看对应天数的文档。四个阶段的产出递进:阶段一「会写脚本」→ 阶段二「会封装模块」→ 阶段三「会设计类与用第三方库」→ 阶段四「能交付数据产品」。
注意事项总结
四个阶段自查清单:
- 语法基础(Day 1~7):变量与类型转换、
if / elif / else与逻辑运算、for / while循环与break / continue、字符串方法、列表/元组/字典/集合的增删改查。 - 函数与进阶(Day 8~14):函数定义与参数(默认值/关键字/可变)、模块与包(
import、__main__)、文件读写与 CSV/JSON、异常处理(try / except / else / finally)、f-string。 - 面向对象与标准库(Day 15~21):类与对象、
__init__、实例/类/静态方法、继承封装多态、魔法方法、os/pathlib/datetime、pip install、列表推导式与生成器。 - 数据分析与实战(Day 22~30):Pandas(DataFrame 创建、筛选/统计/排序)、matplotlib(四种图 + 保存)、requests + BeautifulSoup(爬数据 + 合规)、Web API(GET/POST/JSON)、SQLite(建表/CRUD/防注入/事务)、Flask(路由/模板/表单/JSON 接口)、综合项目全流程跑通。
易错点
- 错误写法:复盘只”看”不”动手” → 问题:眼睛会了手不会,勾上也没用 → 正确写法:勾不上的条目回去重做对应天数的练习。
- 错误写法:项目做不出来就硬扛今天 → 问题:浪费一天还打击信心 → 正确写法:项目做不出来往往不是今天的问题,而是前面某天的基础没打牢,回头翻对应天数的今日总结。
- 错误写法:以为”会做题”就是”能交付” → 问题:只懂知识不懂组装 → 正确写法:对照项目拆解表,能讲清每个环节用到了哪几天的哪些知识点。
记忆口诀:四个阶段递进走,脚本到产品一条线;勾不上的回去补,能讲清表才算完。
8 后续进阶路线 —— 30 天只是起点
是什么?为什么学?
30 天只是起点。按兴趣选一个方向深入,每个方向都给出先学什么、再做什么。
注意事项总结
五个方向:
- 方向一:自动化办公 —— 学习
openpyxl(Excel)、python-docx(Word)、PyPDF2(PDF)、smtplib(邮件);练手批量整理成绩单、自动生成周报、批量重命名文件。 - 方向二:数据分析 —— 学习 Pandas 进阶(groupby / merge / 透视表)、
matplotlib+seaborn、numpy;练手分析 Kaggle 真实数据集,出可视化报告。 - 方向三:Web 开发 —— 学习 Flask 进阶(ORM
SQLAlchemy、用户登录、部署)或 Django;练手把 day29 天气网页升级成”用户系统 + 收藏城市”并部署。 - 方向四:爬虫 —— 学习
scrapy框架、Selenium(动态网页)、反爬应对与合规边界;练手爬公开资讯站点的分类列表(务必遵守 robots.txt)。 - 方向五:人工智能 —— 前置 Python + numpy 矩阵运算 + 数学基础(线性代数、概率论);学习
scikit-learn、pytorch;练手房价预测、手写数字识别(MNIST)。
通用建议:
- 项目驱动:每学一个新库,立刻做一个能跑的小项目,别只看文档。
- Git 与代码托管:把代码放到 GitHub,养成版本管理习惯(找一天补上 Git)。
- 坚持写代码:每天至少 30 分钟,算法题网站(LeetCode 简单题)练手感。
- 学会提问:报错先自己搜(复制报错信息到搜索引擎),搜不到再问人。
- 写博客记录:把学到的写成笔记,输出是最好的巩固。
易错点
- 错误写法:五个方向全都想学 → 问题:贪多嚼不烂,一个都学不深 → 正确写法:按兴趣和职业方向选一个,先学进去再扩展。
- 错误写法:学新库只看文档不写代码 → 问题:看完就忘 → 正确写法:项目驱动,学一个做一个能跑的小项目。
- 错误写法:AI 方向基础没打牢就硬上 → 问题:数学和矩阵不过关,代码全是抄 → 正确写法:先补 NumPy 和数学基础再进 AI。
记忆口诀:三十天是起点不是终点,选一个方向深挖;项目驱动写博客,每天半小时练手。
今日总结
把 30 天学的知识组装成五步流水线:API 获取 → Pandas 分析 → matplotlib 可视化 → SQLite 存储 → Flask 展示,五个环节全部跑通
每个环节独立成文件(weather_data / analysis / charts / database / app),每步都能单独运行验证(if __name__ == "__main__")
阶段知识地图:NumPy 打底 → Pandas 管表 → 数据清洗保质量 → matplotlib 出图 → 爬虫取网页 → API 调服务 → SQLite 落地 → Flask 展示,一环扣一环
幂等入库:INSERT OR IGNORE + 主键去重,重复运行新增 0 条
图表 404 排查:Flask 静态文件必须在 static/ 目录
网页 http://127.0.0.1:2050/ 展示完整分析报告(数据卡片 + 明细表格 + 两张图)
30 天复盘清单查漏补缺:四个阶段(语法基础 / 函数进阶 / 面向对象 / 数据分析实战)逐项打勾
选定一个进阶方向:自动化办公 / 数据分析 / Web 开发 / 爬虫 / AI,写下下一步学习计划
| 报错 | 原因 | 修复 |
|---|---|---|
ModuleNotFoundError: No module named 'pandas' | 没安装 Pandas | pip install pandas(第 2 步需要) |
| 网页上图片不显示(404) | 图片没在 static/ 目录,或路径不对 | 确认 project/backend/static/temperature.png 存在,模板路径写 /static/... |
| 页面访问很慢或报 502 | 每次访问都请求 Open-Meteo,网络慢或失败 | 改成”数据库有数据就不请求 API”;加 timeout |
| 图表中文显示成方框 | 画图脚本少了中文字体设置 | 在 charts.py 开头加 plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei"] |
sqlite3.IntegrityError: UNIQUE constraint failed | 直接 INSERT 撞了主键 | 用 INSERT OR IGNORE,或先查重再插入 |
| 导入自己写的模块报错(No module named ‘weather_data’) | 运行目录不对 | 在 project/backend 目录下运行 python app.py,让脚本和模块在同一目录 |