D:\python313\python.exe C:\Users\86178\PycharmProjects\PythonProject\正统代码修炼\测试.py
Traceback (most recent call last):
File "C:\Users\86178\PycharmProjects\PythonProject\正统代码修炼\测试.py", line 262, in <module>
w = WeatherWin()
File "C:\Users\86178\PycharmProjects\PythonProject\正统代码修炼\测试.py", line 190, in __init__
self.status_label.setAlignment(Qt.AlignRight)
^^
NameError: name 'Qt' is not defined
进程已结束,退出代码为 1# desktop_weather_app.py
import sys
import csv
from datetime import datetime
import re
import requests
from bs4 import BeautifulSoup
from PySide6.QtWidgets import (
QApplication, QWidget, QLabel, QLineEdit, QPushButton,
QVBoxLayout, QHBoxLayout, QTableWidgetItem, QMessageBox, QTableWidget
)
# ---------------- Spider ----------------
# 更新后的城市映射表,包含更多城市和备用URL
def chinese_to_cityinfo(name: str):
name = name.strip()
mapping = {
# 格式:城市名: (拼音, 城市代码, 备用URL)
"北京": ("beijing", "101010100", "bj"),
"上海": ("shanghai", "101020100", "sh"),
"广州": ("guangzhou", "101280101", "gz"),
"深圳": ("shenzhen", "101280601", "sz"),
"杭州": ("hangzhou", "101210101", "hz"),
"南京": ("nanjing", "101190101", "nj"),
"苏州": ("suzhou", "101190401", "su"),
"武汉": ("wuhan", "101200101", "wh"),
"成都": ("chengdu", "101270101", "cd"),
"西安": ("xian", "101110101", "xa"),
"重庆": ("chongqing", "101040100", "cq"),
"天津": ("tianjin", "101030100", "tj"),
"长沙": ("changsha", "101250101", "cs"),
"合肥": ("hefei", "101220101", "hf"),
"郑州": ("zhengzhou", "101180101", "zz"),
"青岛": ("qingdao", "101120201", "qd"),
"大连": ("dalian", "101070201", "dl"),
"沈阳": ("shenyang", "101070101", "sy"),
"厦门": ("xiamen", "101230201", "xm"),
"福州": ("fuzhou", "101230101", "fz"),
# 添加更多城市...
}
return mapping.get(name)
def _first_text(soup: BeautifulSoup, selectors):
"""改进的选择器查找,支持正则匹配"""
for sel in selectors:
els = soup.select(sel) if isinstance(sel, str) else [soup.find(*sel)]
for el in els:
if el:
txt = el.get_text(strip=True)
if txt:
return txt
return None
def _extract_temperature(text):
"""改进的温度提取,支持多种格式"""
if not text:
return None
# 匹配数字和℃/C符号
match = re.search(r'(-?\d+\.?\d*)\s*[℃°C]?', text)
if match:
return f"{match.group(1)}℃"
return None
def _extract_humidity(text):
"""改进的湿度提取"""
if not text:
return None
# 匹配百分比数字
match = re.search(r'(\d+)\s*%?', text)
if match:
return f"{match.group(1)}%"
return None
def crawl_weather(city_cn: str):
"""
改进的爬虫函数,支持多种页面结构和备用URL
返回: dict 或 None
"""
info = chinese_to_cityinfo(city_cn)
if not info:
return None
pinyin, code, short_name = info if len(info) == 3 else (*info, None)
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Accept-Language": "zh-CN,zh;q=0.9",
}
# 准备要尝试的URL列表(按优先级排序)
urls_to_try = [
f"https://www.weather.com.cn/weather1d/{code}.shtml", # PC端
f"https://e.weather.com.cn/d/top/{code}.shtml", # 备用PC端
f"https://m.weather.com.cn/mweather/{code}.shtml", # 移动端
f"https://m.weather.com.cn/mweather1d/{code}.shtml", # 移动端备用
]
if short_name:
urls_to_try.extend([
f"https://{short_name}.weather.com.cn/weather1d/{code}.shtml",
f"https://{short_name}.weather.com.cn/weather/{code}.shtml",
])
for url in urls_to_try:
try:
resp = requests.get(url, headers=headers, timeout=10)
resp.raise_for_status()
soup = BeautifulSoup(resp.text, "lxml")
# 改进的选择器集合,覆盖更多可能的页面结构
temp_selectors = [
"span.tmp", "span.temperature", "span.tem", "i#current_temp",
".sk .tem span", ".tem.now .tem_num", ".weather .tem span",
".temp", ".temperature", ".now .temp", ".nowtemp",
{"name": "div", "class": "tem"},
{"name": "div", "class": "temperature"}
]
hum_selectors = [
"span.hum", "span.humidity", ".sk .sd", ".sk .hum",
".humidity", ".shidu", ".zs .sd", ".sd",
{"name": "div", "class": "humidity"},
{"name": "div", "class": "shidu"}
]
wind_selectors = [
"span.wind", "span.w", ".sk .wind", ".sk .win",
".wind", ".zs .win", ".zs .wind", ".win",
{"name": "div", "class": "wind"},
{"name": "div", "class": "win"}
]
temp_raw = _first_text(soup, temp_selectors)
hum_raw = _first_text(soup, hum_selectors)
wind_raw = _first_text(soup, wind_selectors)
# 使用改进的数据提取函数
temp = _extract_temperature(temp_raw)
humidity = _extract_humidity(hum_raw)
# 风力处理
wind = None
if wind_raw:
wind = re.sub(r'[风力风向::]+', '', wind_raw).strip()
if temp or humidity or wind:
return {
"city": city_cn,
"time": datetime.now().strftime("%Y-%m-%d %H:%M"),
"temp": temp or "—",
"humidity": humidity or "—",
"wind": wind or "—",
}
except Exception as e:
print(f"尝试URL {url} 失败: {e}")
continue
return None
# ---------------- GUI ----------------
class WeatherWin(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("桌面天气查询(增强版爬虫)")
self.resize(800, 500)
# 改进的UI布局
self.city = QLineEdit()
self.city.setPlaceholderText("请输入中文城市名,例如:北京、上海、广州…")
self.btn = QPushButton("查询")
self.table = QTableWidget(0, 5)
self.table.setHorizontalHeaderLabels(["城市", "时间", "温度", "湿度", "风力/风向"])
self.table.horizontalHeader().setStretchLastSection(True)
self.table.setColumnWidth(0, 120)
self.table.setColumnWidth(1, 150)
self.table.setColumnWidth(2, 100)
self.table.setColumnWidth(3, 100)
# 添加状态标签
self.status_label = QLabel("就绪")
self.status_label.setAlignment(Qt.AlignRight)
# 布局
lay = QVBoxLayout(self)
top = QHBoxLayout()
top.addWidget(QLabel("城市:"))
top.addWidget(self.city)
top.addWidget(self.btn)
lay.addLayout(top)
lay.addWidget(self.table)
lay.addWidget(self.status_label)
# 连接信号
self.btn.clicked.connect(self.query)
self.city.returnPressed.connect(self.query)
# CSV文件
self.csv_file = "weather_history.csv"
self._ensure_csv_header()
def _ensure_csv_header(self):
try:
with open(self.csv_file, "r", encoding="utf-8") as f:
has_header = f.readline()
if has_header:
return
except FileNotFoundError:
pass
with open(self.csv_file, "w", encoding="utf-8", newline="") as f:
writer = csv.writer(f)
writer.writerow(["城市", "时间", "温度", "湿度", "风力/风向"])
def query(self):
city = self.city.text().strip()
if not city:
QMessageBox.warning(self, "提示", "请输入城市名(中文)")
return
self.status_label.setText("正在查询中...")
self.btn.setEnabled(False)
QApplication.processEvents() # 更新UI
data = crawl_weather(city)
self.btn.setEnabled(True)
if not data:
self.status_label.setText("查询失败")
QMessageBox.warning(self, "提示",
"未能获取该城市天气数据,可能原因:\n"
"1. 城市名称不在支持列表中\n"
"2. 网站结构已更新\n"
"3. 网络连接问题")
return
# 显示结果
self.status_label.setText("查询成功")
row = self.table.rowCount()
self.table.insertRow(row)
for col, key in enumerate(["city", "time", "temp", "humidity", "wind"]):
self.table.setItem(row, col, QTableWidgetItem(data[key]))
# 写入CSV
try:
with open(self.csv_file, "a", encoding="utf-8", newline="") as f:
writer = csv.writer(f)
writer.writerow([data["city"], data["time"], data["temp"], data["humidity"], data["wind"]])
except Exception as e:
QMessageBox.warning(self, "提示", f"写入CSV失败: {str(e)}")
if __name__ == "__main__":
app = QApplication(sys.argv)
w = WeatherWin()
w.show()
sys.exit(app.exec())
最新发布