20242308 实验四 《Python程序设计》实验报告
学号 20242308 《Python程序设计》实验四报告
课程:《Python程序设计》
班级:2423
姓名:施俊龙
学号:20242308
实验教师:王志强
实验日期:2025年6月10日
必修/选修:公选课
一、实验内容
每天跑操前我都要确认天气与是否跑操,于是我希望能在前一天不用查网站就知道。但是功能过于单一,后来在此基础上我有希望能在早中晚根据温度确认应该穿什么衣服,在温度变化大时起作用。在此之前做过一个赛车小游戏,从AI那学习了界面的创建方法,于是我将其作成了一个简单的界面,原本希望能输入城市,进行查询,但太过复杂,容错率低,于是只有北京。爬虫爬的是中国天气网。
二、实验过程及结果
首先我创建了一个较为简单的程序,希望能让用户输入城市,但其一直报错,无法实现功能,AI修改后虽然能运行,但其修改部分无法理解,于是放弃,以下为失败的程序(其中的温度数据用AI推荐生成,导入的一部分数据库等由AI指导推荐,其余沿用以前编小游戏时内容):
import requests # 发送请求
from bs4 import BeautifulSoup # 解析HTML
import tkinter as tk # GUI库
from tkinter import ttk, messagebox # GUI组件和消息框
from PIL import Image, ImageTk # 图像处理
import io # 二进制数据操作
import re # 正则表达式
主应用
class WeatherApp:
def init(self, root):
# 初始化
self.root = root # 主窗口
self.root.title("明日天气查询与建议") # 窗口标题
self.root.geometry("600x500") # 窗口大小
设置界面图画
self.style = ttk.Style()
self.style.configure('TLa', font=('Microsoft YaHei', 12)) # 标签
self.style.configure('TButton', font=('Microsoft YaHei', 12)) # 按钮
self.style.configure('Header.TLa', font=('Microsoft YaHei', 16, 'bold')) # 标题
界面组件
self.create_widgets()
def create_widgets(self):
# 城市
ttk.Label(self.root, text="输入城市名称:", style='TLa').pack(pady=5)
# 输入框
self.cty_ent = ttk.Entry(self.root, width=30, font=('Microsoft YaHei', 12))
self.cty_ent.pack(pady=5)
self.cty_ent.insert(0, "北京") # 设置北京为默认
查询按钮
self.qry_btn = ttk.Button(self.root, text="查询明日天气", command=self.qry_wthr)
self.qry_btn.pack(pady=10)
天气信息显示
self.wthr_frm = ttk.Frame(self.root)
self.wthr_frm.pack(pady=10, fill=tk.BOTH, expand=True)
天气图标
self.wthr_ico_lbl = ttk.Label(self.wthr_frm)
self.wthr_ico_lbl.pack()
天气信息
self.wthr_inf = ttk.Label(self.wthr_frm, style='Header.TLa')
self.wthr_inf.pack(pady=5)
跑操建议
self.run_inf = ttk.Label(self.wthr_frm, style='TLa', wraplength=550)
self.run_inf.pack(pady=5)
穿衣建议
self.clth_inf = ttk.Label(self.wthr_frm, style='TLa', wraplength=550)
self.clth_inf.pack(pady=5)
查询天气
def qry_wthr(self):
cty = self.cty_ent.get().strip() # 获取城市名
#去除空格!!!
if not cty: # 检查是否为空
messagebox.showerror("错误", "请输入城市名称")
return
try:
# 获取城市ID
cty_id = self.get_cty_id(cty)
if not cty_id: # 检查是否找到城市
messagebox.showerror("错误", f"未找到城市'{cty}'的天气信息")
return
获取天气数据
wthr_data = self.get_wthr_data(cty_id)
显示
self.show_wthr(wthr_data)
except Exception as e: # 出现异常的处理
messagebox.showerror("错误", f"获取天气信息时出错: {str(e)}")
获取城市ID方法
def get_cty_id(self, cty):
try:
# 构建请求URL
url = f"http://www.weather.com.cn/search/citydata.jsp?word={cty}"
# 发送HTTP请求
rsp = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'})
rsp.encoding = 'utf-8' # 设置编码
data = rsp.json() # 解析JSON
检查数据有效性
if data and 'refs' in data and len(data['refs']) > 0:
# 获取第一个匹配城市的信息
cty_inf = data['refs'][0]
cty_url = cty_inf['url']
# 从URL中提取城市ID
cty_id = cty_url.split('/')[-1].split('.')[0]
return cty_id
return None
except:
return None
获取天气数据方法
def get_wthr_data(self, cty_id):
# 构建天气页面URL
url = f"http://www.weather.com.cn/weather/{cty_id}.shtml"
# 请求头设置
hdrs = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
发送请求获取天气页面
rsp = requests.get(url, headers=hdrs)
rsp.encoding = 'utf-8'
soup = BeautifulSoup(rsp.text, 'html.parser') # 解析HTML
查找明日天气数据
tmrw_wthr = soup.find('div', {'id': '7d'}).find('ul', {'class': 't clearfix'}).find_all('li')[1]
解析天气信息
date = tmrw_wthr.find('h1').get_text() # 日期
wthr = tmrw_wthr.find('p', {'class': 'wea'}).get_text() # 天气状况
temp = tmrw_wthr.find('p', {'class': 'tem'}).get_text().strip() # 温度
获取小时天气数据URL
hrs_url = f"http://www.weather.com.cn/weather1d/{cty_id}.shtml"
hrs_rsp = requests.get(hrs_url, headers=hdrs)
hrs_rsp.encoding = 'utf-8'
hrs_soup = BeautifulSoup(hrs_rsp.text, 'html.parser')
存储小时天气数据
hrs_data = []
hrs_ul = hrs_soup.find('div', {'id': 'todayDiv'}).find('ul', {'class': 'clearfix'})
for li in hrs_ul.find_all('li'): # 遍历小时数据
hr = li.find('span').get_text() # 时间
if hr in ['06时', '07时']: # 只关注6-7点
hr_wthr = li.find('img')['title'] # 天气状况
hr_temp = li.find('em').get_text() # 温度
hrs_data.append({
'hr': hr,
'wthr': hr_wthr,
'temp': hr_temp
})
获取天气图标
ico_url = "http:" + tmrw_wthr.find('img')['src']
ico_rsp = requests.get(ico_url, headers=hdrs)
ico_img = Image.open(io.BytesIO(ico_rsp.content)) # 打开图片
ico_img = ico_img.resize((50, 50), Image.Resampling.LANCZOS) # 改变大小,使其能适配于界面
self.wthr_ico = ImageTk.PhotoImage(ico_img) # AI建议转换为Tkinter图片
提取温度范围
temp_pat = re.compile(r'(-?\d+)℃') # 匹配温度
temps = temp_pat.findall(temp)
if len(temps) == 2:
low_t = int(temps[0]) # 最低温
high_t = int(temps[1]) # 最高温
else:
# 从小时数据中获取温度
temps = [int(h['temp'].replace('℃', '')) for h in hrs_data]
low_t = min(temps) if temps else 0 # 取最小值
high_t = max(temps) if temps else 0 # 取最大值
return {
'date': date,
'wthr': wthr,
'low_t': low_t,
'high_t': high_t,
'hrs_data': hrs_data,
'ico': self.wthr_ico
}
显示天气信息方法
def show_wthr(self, wthr_data):
# 显示天气图标
self.wthr_ico_lbl.config(image=wthr_data['ico'])
显示基本信息
inf_txt = f"{wthr_data['date']} 天气: {wthr_data['wthr']}\n"
inf_txt += f"温度: {wthr_data['low_t']}℃ ~ {wthr_data['high_t']}℃"
self.wthr_inf.config(text=inf_txt)
检查6-7点天气是否适合跑操
run_txt = "跑操建议: "
# 判断是否有雨雪
run_ok = all(
"雨" not in h['wthr'] and "雪" not in h['wthr']
for h in wthr_data['hrs_data']
)
if run_ok: # 适合跑操
run_txt += "明天6:00-7:00天气适宜,建议进行跑操活动。"
else: # 不适合跑操
run_txt += "明天6:00-7:00天气不适宜跑操(可能有雨雪)。"
self.run_inf.config(text=run_txt)
计算平均温度
avg_t = (wthr_data['low_t'] + wthr_data['high_t']) / 2
clth_txt = "穿衣建议:\n"
开始都使用平均温度,发现并不适用,AI建议
上午建议(按最低温)
clth_txt += "上午: " + self.get_clth_sug(wthr_data['low_t']) + "\n"
# 下午建议(按平均温)
clth_txt += "下午: " + self.get_clth_sug(avg_t) + "\n"
# 晚上建议(按最低温)
clth_txt += "晚上: " + self.get_clth_sug(wthr_data['low_t'])
self.clth_inf.config(text=clth_txt)
获取穿衣建议方法(AI建议的搭配方案)
def get_clth_sug(self, tmp):
if tmp >= 28: # 炎热
return "天气炎热,建议穿短袖、短裤、裙子等夏装,注意防晒"
elif 24 <= tmp < 28: # 较热
return "天气较热,建议穿短袖、薄外套或衬衫"
elif 18 <= tmp < 24: # 适宜
return "温度适宜,建议穿长袖T恤、薄外套或针织衫"
elif 12 <= tmp < 18: # 稍凉
return "天气稍凉,建议穿薄毛衣、夹克或风衣"
elif 5 <= tmp < 12: # 较冷
return "天气较冷,建议穿毛衣、厚外套或薄羽绒服"
else: # 寒冷
return "天气寒冷,建议穿羽绒服、厚毛衣,注意保暖"
主程序
if name == "main":
root = tk.Tk() # 创建主窗口
app = WeatherApp(root) # 创建应用实例
root.mainloop() # 启动主循环
安装数据库


然后我修改为了固定为北京的程序,但遇到了JSON格式问题,返回的数据不是有效的JSON格式,而且修改时并未完全修改,出现了一个NONE的无效变量,导致程序无法运行,交给AI检查后根据AI的指示修改。
程序:
import requests # 发送HTTP请求
from bs4 import BeautifulSoup # 解析HTML
import tkinter as tk # GUI库
from tkinter import ttk, messagebox # GUI组件和消息框
from PIL import Image, ImageTk # 图像处理
import io # 二进制数据操作
import re # 正则表达式
class WeatherApp:
def init(self, root):
# 初始化
self.root = root # 主窗口
self.root.title("北京天气查询") # 窗口标题
self.root.geometry("600x500") # 窗口大小
# 设置界面样式
self.style = ttk.Style()
self.style.configure('TLabel', font=('Microsoft YaHei', 12)) # 标签
self.style.configure('TButton', font=('Microsoft YaHei', 12)) # 按钮
self.style.configure('Header.TLabel', font=('Microsoft YaHei', 16, 'bold')) # 标题
# 界面组件
self.create_widgets()
def create_widgets(self):
# 城市标签(固定显示北京)
ttk.Label(self.root, text="当前城市: 北京", style='Header.TLabel').pack(pady=10)
# 查询按钮
self.qry_btn = ttk.Button(self.root, text="查询北京天气", command=self.qry_wthr)
self.qry_btn.pack(pady=10)
# 天气信息显示
self.wthr_frm = ttk.Frame(self.root)
self.wthr_frm.pack(pady=10, fill=tk.BOTH, expand=True)
# 天气图标
self.wthr_ico_lbl = ttk.Label(self.wthr_frm)
self.wthr_ico_lbl.pack()
# 天气信息
self.wthr_inf = ttk.Label(self.wthr_frm, style='Header.TLabel')
self.wthr_inf.pack(pady=5)
# 跑操建议
self.run_inf = ttk.Label(self.wthr_frm, style='TLabel', wraplength=550)
self.run_inf.pack(pady=5)
# 穿衣建议
self.clth_inf = ttk.Label(self.wthr_frm, style='TLabel', wraplength=550)
self.clth_inf.pack(pady=5)
# 查询天气(简化版,直接使用北京的城市ID)
def qry_wthr(self):
try:
# 北京的城市ID固定为101010100
wthr_data = self.get_wthr_data("101010100")
if wthr_data: # 确保数据不为None
self.show_wthr(wthr_data)
else:
messagebox.showerror("错误", "获取天气数据失败,请检查网络连接")
except Exception as e:
messagebox.showerror("错误", f"获取天气信息时出错: {str(e)}")
# 获取天气数据方法(增加错误处理)
def get_wthr_data(self, cty_id):
try:
# 构建天气页面URL
url = f"http://www.weather.com.cn/weather/{cty_id}.shtml"
# 请求头设置
hdrs = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
# 发送请求获取天气页面
rsp = requests.get(url, headers=hdrs, timeout=10)
rsp.encoding = 'utf-8'
soup = BeautifulSoup(rsp.text, 'html.parser') # 解析HTML
# 查找明日天气数据(增加检查)
weather_div = soup.find('div', {'id': '7d'})
if not weather_div:
return None
weather_list = weather_div.find('ul', {'class': 't clearfix'})
if not weather_list or len(weather_list.find_all('li')) < 2:
return None
tmrw_wthr = weather_list.find_all('li')[1]
# 解析天气信息(增加检查)
date = tmrw_wthr.find('h1').get_text() if tmrw_wthr.find('h1') else "未知日期"
wthr = tmrw_wthr.find('p', {'class': 'wea'}).get_text() if tmrw_wthr.find('p', {'class': 'wea'}) else "未知天气"
temp = tmrw_wthr.find('p', {'class': 'tem'}).get_text().strip() if tmrw_wthr.find('p', {'class': 'tem'}) else "0℃/0℃"
# 获取天气图标(增加检查)
ico_img = None
img_tag = tmrw_wthr.find('img')
if img_tag and 'src' in img_tag.attrs:
try:
ico_url = "http:" + img_tag['src']
ico_rsp = requests.get(ico_url, headers=hdrs, timeout=5)
ico_img = Image.open(io.BytesIO(ico_rsp.content))
ico_img = ico_img.resize((50, 50), Image.Resampling.LANCZOS)
except:
pass
# 使用默认图标如果获取失败
if not ico_img:
ico_img = Image.new('RGB', (50, 50), color='lightblue')
self.wthr_ico = ImageTk.PhotoImage(ico_img)
# 提取温度范围(增加容错)
temp_pat = re.compile(r'(-?\d+)℃') # 匹配温度
temps = temp_pat.findall(temp)
if len(temps) == 2:
low_t = int(temps[0]) # 最低温
high_t = int(temps[1]) # 最高温
else:
low_t = high_t = 0 # 默认值
return {
'date': date,
'wthr': wthr,
'low_t': low_t,
'high_t': high_t,
'ico': self.wthr_ico
}
except requests.exceptions.RequestException:
return None
except Exception:
return None
# 显示天气信息方法(保留原有实现)
def show_wthr(self, wthr_data):
# 显示天气图标
self.wthr_ico_lbl.config(image=wthr_data['ico'])
# 显示基本信息
inf_txt = f"{wthr_data['date']} 天气: {wthr_data['wthr']}\n"
inf_txt += f"温度: {wthr_data['low_t']}℃ ~ {wthr_data['high_t']}℃"
self.wthr_inf.config(text=inf_txt)
# 跑操建议
run_txt = "跑操建议: "
if "雨" in wthr_data['wthr'] or "雪" in wthr_data['wthr']:
run_txt += "明天有雨雪天气,不适宜户外跑操。"
elif wthr_data['low_t'] < 0:
run_txt += "明天早晨气温低于0℃,建议室内运动。"
elif wthr_data['high_t'] > 30:
run_txt += "明天气温较高,建议清晨或傍晚进行跑操。"
else:
run_txt += "明天天气适宜,建议进行跑操活动。"
self.run_inf.config(text=run_txt)
# 穿衣建议
avg_t = (wthr_data['low_t'] + wthr_data['high_t']) / 2
clth_txt = "穿衣建议:\n"
clth_txt += "上午: " + self.get_clth_sug(wthr_data['low_t']) + "\n"
clth_txt += "下午: " + self.get_clth_sug(avg_t) + "\n"
clth_txt += "晚上: " + self.get_clth_sug(wthr_data['low_t'])
self.clth_inf.config(text=clth_txt)
# 获取穿衣建议方法(保留原有实现)
def get_clth_sug(self, temp):
if temp >= 28: # 炎热
return "短袖、短裤、裙子等夏装,注意防晒"
elif 24 <= temp < 28: # 较热
return "短袖、薄外套或衬衫"
elif 18 <= temp < 24: # 适宜
return "长袖T恤、薄外套或针织衫"
elif 12 <= temp < 18: # 稍凉
return "薄毛衣、夹克或风衣"
elif 5 <= temp < 12: # 较冷
return "毛衣、厚外套或薄羽绒服"
else: # 寒冷
return "羽绒服、厚毛衣,注意保暖"
if name == "main":
root = tk.Tk() # 创建主窗口
app = WeatherApp(root) # 创建应用实例
root.mainloop() # 启动主循环
运行结果:

三、实验过程中遇到的问题和解决过程
上面已经说明,通过AI以及b站查询,知乎发布询问解决及学习。
其他(感悟、思考等)
通过本次实验,我对爬虫有了更为深刻的认识,也因为过程中各种格式错误,无法访问或无法识别,了解到了之中各种格式的规范及用途,进行了系统的学习。当程序成功运行时,仿佛有一种豁然开朗的感觉,这是我第一次完整编程并实现爬虫功能,了解到了很多,对其有了明确的认识。

浙公网安备 33010602011771号