20254101 实验四《Python程序设计》实验报告
# 20254101 2025-2026-2 《Python程序设计》实验四报告
课程:《Python程序设计》
班级: 2541
姓名: 李若涵
学号:20254101
实验教师:王志强
实验日期:2026年5月19日
必修/选修:专选课
1.实验内容
(一)实验要求
Python综合应用:爬虫、数据处理、可视化、机器学习、神经网络、游戏、网络安全等。
例如:编写从社交网络爬取数据,实现可视化舆情监控或者情感分析。
例如:利用公开数据集,开展图像分类、恶意软件检测等
例如:利用Python库,基于OCR技术实现自动化提取图片中数据,并填入excel中。
例如:爬取天气数据,实现自动化微信提醒
例如:利用爬虫,实现自动化下载网站视频、文件等。
例如:编写小游戏:坦克大战、贪吃蛇、扫雷等等
注:在Windows/Linux系统上使用VIM、PDB、IDLE、Pycharm等工具编程实现。
(二)实验分析
1、实验目的
结合老师对爬虫的讲述,我知道借助网络爬虫,可以从互联网上提取出我需要的内容。对于大学生来说跑操实为生活中重要一环,历史天气和未来天气预报信息为最基本的信息。因此我选择了编写爬虫,用python程序爬取北京市丰台区历史30天的天气数据,当前系统日期为基准,预测未来三天的天气,生成温度趋势图和预测曲线,并每天早晨8点通过微信发送天气预报和穿衣建议。
2、实验分析
①实验功能:
a、实时天气数据获取
b、未来七天天气预报
c、可存储并随时提取过去3天的天气数据
d、温度预测,可为衣物准备提供参考
e、每天早晨8点通过PushPlus渠道自动推送到微信,可便捷收取到信息
②实验原理:
a、网络爬虫技术:请求调用和风天气API接口,获取天气数据,并解析出所需温度、湿度、风力等
b、数据存储与管理:使用Pandas库处理表格数据
c、数据可视化:在同一坐标系中绘制历史温度折线、官方预报折线和预测折线,并添加图例、网格和今天标记线。
e、定时任务与消息推送:通过PushPlus平台提供的API,以POST请求发送Markdown格式的消息到绑定的微信账号。
2. 实验过程及结果
(一)实验过程
1. 获取天气数据,准备数据
利用和风天气api,注册登录后创建项目,获取API HOST和API KEY,编写同时获取实时天气和7天预报的程序后,将前两项内容填入程序后下载软件包,运行程序。

① 确定API可用性
② 分析返回数据格式:实时天气接口返回 code、updateTime、now 字段(含温度、天气、体感温度等)。7天预报接口返回 daily 列表,包含日期、最高温、最低温、天气等。


③ 准备历史温度数据文件:设计CSV文件 fengtai_history.csv,存储历史每日最高温。

2. 编写代码
① 导入所需模块
需要 requests 进行API调用,pandas 和 numpy 处理数据,matplotlib.pyplot 绘图,sklearn.linear_model.LinearRegression 做预测,datetime 处理日期,schedule 实现定时任务,time 控制循环。

② 配置区域
定义API域名、Key、丰台区ID、微信推送token(可选)、历史数据文件名、以及一次运行开关 RUN_ONCE。方便用户直接测试或部署为定时任务。

③ 获取实时天气函数 get_current_weather()
构造请求URL,发送GET请求,解析JSON并提取所需字段(温度、体感温度、湿度、风向、风力等)。增加异常处理(超时、接口报错),返回字典或None。
④ 获取7天预报函数 get_7day_forecast()
类似方式请求未来7天数据,整理为DataFrame,包含日期、最高温、最低温、天气。若失败则返回None。
(截图可见准备数据②)
⑤ 历史数据管理
load_history():读取CSV文件,处理日期格式兼容性,返回DataFrame。
save_today_temperature():将当天官方预报的最高温追加保存到CSV,并避免重复记录。这一步为后续积累训练样本。
(截图可见准备数据③)
⑥ 线性回归预测函数 predict_next_3days()
基于历史数据(至少5天),以“距起始日期天数”为特征,最高温为目标,训练线性回归模型。然后计算今天之后第1、2、3天的偏移,预测未来三天的最高温,返回四舍五入保留一位小数的数组。

⑦ 温度趋势绘图函数 plot_temperature()
在同一张图上绘制:历史实际最高温(蓝色实线圆点)官方预报未来三天最高温(绿色虚线方块)机器学习预测未来三天最高温(红色点划线圆点)保存为fengtai_temperature_forecast.png。

以下是生成的图表:

⑧ 生成日报文本函数 generate_report()
输出Markdown格式的微信消息,包括:实时天气、未来三天官方预报、机器学习预测值、以及基于当前温度的穿衣建议(分为寒冷、凉爽、温暖、炎热四档)。
(对应程序位置:def generate_report(...): 函数体)

⑨获取token:登录,得到专属个人token

⑩ 微信推送函数 send_wechat()
调用 pushplus.plus 的API,使用POST方式发送JSON数据,模板设为markdown。如未配置token则跳过推送。

⑪主任务函数 daily_job()
按顺序执行,每一步都有成功或失败日志。:

(二)代码运行成果:


程序运行视频链接:
!自编码程序实验
(三)源代码如下:
丰台区未来三天天气预测与微信日报(动态日期,自动推送)
功能:
1. 爬取实时天气 + 未来7天预报
2. 基于历史温度数据,用线性回归预测未来三天最高温
3. 生成温度趋势图(历史 + 官方预报 + 预测曲线)
4. 每天早晨8点自动通过微信发送日报和穿衣建议
"""
import requests
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime, timedelta
from sklearn.linear_model import LinearRegression
import schedule
import time
import warnings
warnings.filterwarnings('ignore')
# ==================== 配置区域 ====================
API_HOST = "https://ku7fc3jdr2.re.qweatherapi.com" # 你的专属API域名
API_KEY = "3d7e7be02b7f4b2eb984ffd85034fa3d" # 你的API Key
FENGTAI_ID = "101010900" # 丰台区代码
# 微信推送 token(从 https://www.pushplus.plus 获取,留空则不推送)
PUSHPLUS_TOKEN = "8e83a266d90e4db981f6a6e9af52a382" # 请填写你的 token
# 历史数据文件(自动创建)
HISTORY_CSV = "fengtai_history.csv"
# 是否立即运行一次(用于测试),设为 True 则直接执行一次,不启动定时任务
RUN_ONCE = True # 测试时设为 True,正式定时请改为 False 并注释掉最后的 while 循环
# ==================================================
def get_current_weather():
"""获取丰台区实时天气"""
url = f"{API_HOST}/v7/weather/now?location={FENGTAI_ID}&key={API_KEY}"
try:
resp = requests.get(url, timeout=10)
data = resp.json()
if data.get("code") == "200":
now = data["now"]
return {
"时间": data["updateTime"],
"天气": now["text"],
"温度": int(now["temp"]),
"体感温度": int(now["feelsLike"]),
"湿度": int(now["humidity"]),
"风向": now["windDir"],
"风力等级": int(now["windScale"])
}
else:
print(f"实时天气接口错误,code: {data.get('code')}")
return None
except Exception as e:
print(f"实时天气请求异常: {e}")
return None
def get_7day_forecast():
"""获取未来7天天气预报,返回DataFrame(日期,最高温,最低温,天气)"""
url = f"{API_HOST}/v7/weather/7d?location={FENGTAI_ID}&key={API_KEY}"
try:
resp = requests.get(url, timeout=10)
data = resp.json()
if data.get("code") == "200":
forecast = []
for day in data["daily"]:
forecast.append({
"日期": day["fxDate"],
"最高温": int(day["tempMax"]),
"最低温": int(day["tempMin"]),
"天气": day["textDay"]
})
return pd.DataFrame(forecast)
else:
print(f"7天预报接口错误,code: {data.get('code')}")
return None
except Exception as e:
print(f"预报请求异常: {e}")
return None
def load_history():
"""加载历史温度数据,容错处理日期格式"""
try:
df = pd.read_csv(HISTORY_CSV)
# 如果文件为空或缺少列,则返回空DataFrame
if '日期' not in df.columns or len(df) == 0:
return pd.DataFrame(columns=['日期', '最高温'])
# 尝试多种日期格式转换
df['日期'] = pd.to_datetime(df['日期'], errors='coerce')
# 删除无效日期行
df = df.dropna(subset=['日期']).reset_index(drop=True)
return df
except (FileNotFoundError, pd.errors.EmptyDataError):
return pd.DataFrame(columns=['日期', '最高温'])
def save_today_temperature(today_max):
"""将今天的预报最高温保存到历史记录(用于积累训练数据)"""
df = load_history()
today_str = datetime.now().strftime("%Y-%m-%d")
# 检查是否已存在今天的数据
if not df.empty and today_str in df['日期'].dt.strftime('%Y-%m-%d').values:
print(f"{today_str} 数据已存在,跳过记录")
return
new_row = pd.DataFrame({'日期': [today_str], '最高温': [today_max]})
df = pd.concat([df, new_row], ignore_index=True)
df.to_csv(HISTORY_CSV, index=False)
print(f"已记录 {today_str} 最高温 {today_max}℃")
def predict_next_3days(history_df, base_date):
"""
基于历史数据,使用线性回归预测从 base_date 之后三天的最高温
base_date: datetime 对象(例如今天)
"""
if len(history_df) < 5:
print("历史数据不足5天,无法进行可靠预测,返回None")
return None
# 按日期排序
hist = history_df.sort_values('日期').copy()
start = hist['日期'].min()
hist['day_index'] = (hist['日期'] - start).dt.days
X = hist[['day_index']].values
y = hist['最高温'].values
model = LinearRegression()
model.fit(X, y)
# 计算 base_date 相对于起始日期的偏移
base_offset = (base_date - start).days
future_offsets = np.array([base_offset+1, base_offset+2, base_offset+3]).reshape(-1, 1)
preds = model.predict(future_offsets)
return preds.round(1)
def plot_temperature(history_df, forecast_df, future_dates, pred_temps=None):
"""
绘制温度趋势图:历史温度 + 官方预报(未来三天) + 预测曲线
future_dates: list of str, 未来三天的日期(YYYY-MM-DD)
pred_temps: list/array, 预测的最高温值(可选)
"""
plt.figure(figsize=(12, 5))
# 历史数据
if not history_df.empty:
hist_dates = history_df['日期']
hist_temps = history_df['最高温']
plt.plot(hist_dates, hist_temps, 'b-o', label='历史实际最高温', markersize=4)
# 官方预报(未来三天)
fut_forecast = forecast_df[forecast_df['日期'].isin(future_dates)]
if not fut_forecast.empty:
f_dates = pd.to_datetime(fut_forecast['日期'])
f_temps = fut_forecast['最高温']
plt.plot(f_dates, f_temps, 'g--s', label='官方预报最高温', markersize=6, linewidth=2)
# 机器学习预测
if pred_temps is not None:
pred_dates = pd.to_datetime(future_dates)
plt.plot(pred_dates, pred_temps, 'r-.o', label='机器学习预测最高温', markersize=6, linewidth=2)
# 标记今天
today = datetime.now().date()
plt.axvline(x=pd.Timestamp(today), color='gray', linestyle=':', alpha=0.7, label='今天')
plt.xlabel('日期')
plt.ylabel('温度 (℃)')
plt.title('丰台区温度趋势(历史 → 今天 → 未来三天预测)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig('fengtai_temperature_forecast.png', dpi=150)
plt.close()
print("温度趋势图已保存为 fengtai_temperature_forecast.png")
def generate_report(current, forecast_df, future_dates, pred_temps):
"""生成微信日报文本(Markdown格式)"""
today_str = datetime.now().strftime("%Y-%m-%d")
report = f"## 📍 丰台区天气日报 ({today_str})\n\n"
if current:
report += f"**当前天气**\n"
report += f"- 🌡️ 温度:{current['温度']}℃ (体感 {current['体感温度']}℃)\n"
report += f"- ☁️ 天气:{current['天气']}\n"
report += f"- 💧 湿度:{current['湿度']}%\n"
report += f"- 🌬️ 风向风力:{current['风向']} {current['风力等级']}级\n\n"
# 官方预报未来三天
report += "**📅 未来三天预报(官方)**\n"
for date in future_dates:
row = forecast_df[forecast_df['日期'] == date]
if not row.empty:
report += f"- {date}:{row.iloc[0]['天气']},{row.iloc[0]['最低温']}~{row.iloc[0]['最高温']}℃\n"
# 机器学习预测
if pred_temps is not None:
report += "\n**🔮 机器学习预测最高温**\n"
for date, temp in zip(future_dates, pred_temps):
report += f"- {date}:{temp}℃\n"
# 穿衣建议
if current:
temp = current['温度']
if temp < 10:
advice = "❄️ 寒冷,建议穿羽绒服、厚棉衣"
elif temp < 20:
advice = "🍂 凉爽,建议穿长袖、薄外套"
elif temp < 28:
advice = "🌞 温暖,建议穿短袖、T恤"
else:
advice = "🔥 炎热,注意防暑,穿轻薄衣物"
report += f"\n💡 **出行建议**:{advice}"
return report
def send_wechat(title, content):
"""通过pushplus发送微信消息"""
if not PUSHPLUS_TOKEN:
print("未配置微信推送token,跳过推送")
return
url = "http://www.pushplus.plus/send"
data = {
"token": PUSHPLUS_TOKEN,
"title": title,
"content": content,
"template": "markdown"
}
try:
resp = requests.post(url, json=data, timeout=10)
result = resp.json()
if result.get("code") == 200:
print("微信推送成功")
else:
print(f"微信推送失败: {result}")
except Exception as e:
print(f"推送异常: {e}")
def daily_job():
"""每日执行的主任务:获取数据、预测、绘图、推送"""
print(f"\n=== {datetime.now()} 开始执行天气日报任务 ===")
# 1. 获取实时天气
current = get_current_weather()
if not current:
print("无法获取实时天气,终止任务")
return
# 2. 获取未来7天预报
forecast_df = get_7day_forecast()
if forecast_df is None:
print("无法获取预报数据,终止任务")
return
# 3. 保存今天的预报最高温到历史(用于积累数据)
today_str = datetime.now().strftime("%Y-%m-%d")
today_row = forecast_df[forecast_df['日期'] == today_str]
if not today_row.empty:
save_today_temperature(today_row.iloc[0]['最高温'])
# 4. 定义未来三天的日期(明天、后天、大后天)
base = datetime.now().date()
future_dates = [(base + timedelta(days=i)).strftime("%Y-%m-%d") for i in range(1, 4)]
# 5. 机器学习预测未来三天最高温
history_df = load_history()
pred_temps = predict_next_3days(history_df, datetime.now())
# 6. 生成温度趋势图(包含历史、官方预报、预测曲线)
plot_temperature(history_df, forecast_df, future_dates, pred_temps)
# 7. 生成日报文本
report = generate_report(current, forecast_df, future_dates, pred_temps)
print("\n" + report)
# 8. 微信推送
send_wechat("丰台区天气日报", report)
print("=== 任务完成 ===\n")
def main():
if RUN_ONCE:
# 测试模式:立即执行一次
daily_job()
else:
# 定时模式:每天早晨8:00执行
schedule.every().day.at("08:00").do(daily_job)
print("定时任务已启动,每天早晨8:00运行。")
while True:
schedule.run_pending()
time.sleep(60)
if __name__ == "__main__":
main()
3. 实验过程中遇到的问题和解决过程
- 问题1:温度趋势图无法保存,图片保存的路径不存在。我之前按照网上建议把保存路径改成了自定义的绝对路径,后来发现我根本没有创建这个文件夹
- 问题1解决方案:我先把保存路径改回最简单的相对路径,即只写文件名 'fengtai_temperature_forecast.png',这样图片会直接保存在程序运行的当前目录下,也就是我的项目文件夹里,不再报错。
- 问题2:微信推送失败,提示“用户令牌不正确”,去 PushPlus 官网注册拿到了 token,但运行后微信始终收不到消息。
- 问题2解决方案:我重新登录 PushPlus 官网,发现新注册的账号需要先进行认证才能使用推送功能。之前我只关注了公众号但没有完成认证,所以 token 一直无效。
4、实验总结
这次实验让我把爬虫、数据分析、机器学习预测和微信推送串了起来,真正感受到了代码改变生活的乐趣,虽然过程磕磕绊绊。比如先是图片保存路径不对导致程序崩溃,又是微信token填错收不到消息,但一步步查资料、改代码、最终看到手机弹出天气日报的那一刻,真的很有成就感。以前觉得天气预报就是看看App,现在自己写的程序每天早上准时给我推送穿衣建议,还能用历史数据预测未来温度,感觉像是拥有一个私人气象助理,可以有效帮我预测明天会不会跑操了哈哈哈哈。总之,实验让我明白:编程不只是应付作业,而是能实实在在解决生活小问题的工具。
5、课程总结
一、课程感悟
坦白说,初识Python时,我内心颇为忐忑,不仅是文科生的身份与身为电脑痴的自治,更多的是对从未涉猎领域的恐惧。第一节课尝试编写程序因误用了中文冒号而导致程序报错。面对屏幕上的红色提示,我一时不知所措,甚至怀疑自己是否适合学习编程。然而随着课程深入,我逐渐发现Python的趣味所在——当我第一次独立完成“猜数字”小游戏,并能与朋友分享、获得他们的认可时,那种由代码创造出的成就感让我真切体会到编程的魅力。
这学期带给我最大的转变是心态。以往遇到报错信息,我第一反应往往是慌乱,不知从何下手,并且逐渐怀疑自己。如今,我已习惯冷静地阅读错误提示,按照逻辑逐步排查。例如在天气实验中,程序因图片保存路径不存在而崩溃,我主动查阅了相关用法,通过自动创建目录解决了问题;微信推送功能迟迟无法生效,反复调试后才意识到是Token未完成平台认证。虽然每个问题都耗费了不少时间,但当最终手机顺利收到推送消息时,那种依靠自己查阅资料、独立思考并亲手修复Bug所带来的满足感,远比直接复制他人代码来得深刻。与此同时,我也学会了不过度依赖AI工具。有时从AI获取的代码片段看似正确,实际运行却频频出错。唯有静下心来逐行理解逻辑、亲手修改细节,才能真正掌握其中的原理。编程归根结底是一场自我实践与思考的修行,也许只有自己动手、自己推敲,方能有所精进。
老师在讲课的过程中无数次对原有代码进行“精装”,我起初并不是很理解。因为我认为原初的代码简单又清晰效率还高,为什么要补充那么多看似繁冗的内容呢?直到自己与各个程序展开博弈,一次次的运行后我发现确实应重视代码的整体性和美观性,毕竟,工具最终是由人来掌控而由人使用。“有机事者必有机心”我想这是当下ai时代浪潮中最应秉持的原则,在这段学习过程中一次次提醒我。
(附一张结课图,感谢每周二下午的python课,让我在北京最好的春光里望着窗外云卷云舒,白天到黑夜,也能一睹窗外鲜绿的生命,我想我对python的学习也将不会止步,会在无数的春天里续写这一学期的故事!)

二、意见和建议
① 老师讲课速度有点快,尤其讲爬虫和socket的时候,稍微一走神就跟不上了。希望以后能稍微慢一点,或者在关键步骤上停留几秒,让大家记一下。
② 对于文科生来说,有些概念确实抽象。建议老师提前一两天发个简单的预习提纲或者重点代码片段,这样上课时不会太懵。
③ 希望增加一些课上现场调试代码的环节,带着大家一步步改bug,这样比光看PPT印象深。
最后,谢谢老师一学期的耐心教导!orz
浙公网安备 33010602011771号