修复 CMA 温度提取:改用纯文本数字匹配替代 HTML 标签猜测,天气优先 CMA、温度回退 OM
This commit is contained in:
@@ -115,14 +115,13 @@ def _fetch_cma_forecast(city_key: str) -> Optional[Dict[str, Any]]:
|
|||||||
low_str: Optional[str] = None
|
low_str: Optional[str] = None
|
||||||
|
|
||||||
if tem_text:
|
if tem_text:
|
||||||
# Patterns: <span>25℃</span> or <span>25°C</span>
|
# Strip all HTML tags, then find numbers followed by degree
|
||||||
high_match = re.search(r"<span[^>]*>(-?\d+)\s*(?:℃|°C|°c)?</span>", tem_text)
|
clean = re.sub(r"<[^>]+>", " ", tem_text)
|
||||||
if high_match:
|
nums = re.findall(r"(-?\d+)\s*(?:℃|°C|°c|°)?", clean)
|
||||||
high_str = high_match.group(1)
|
if len(nums) >= 1:
|
||||||
# Night temp in <i>: <i>19℃</i>
|
high_str = nums[0]
|
||||||
low_match = re.search(r"<i[^>]*>(-?\d+)\s*(?:℃|°C|°c)?</i>", tem_text)
|
if len(nums) >= 2:
|
||||||
if low_match:
|
low_str = nums[1]
|
||||||
low_str = low_match.group(1)
|
|
||||||
|
|
||||||
if not weather and not high_str:
|
if not weather and not high_str:
|
||||||
return None
|
return None
|
||||||
@@ -161,64 +160,57 @@ def _fetch_city_data(
|
|||||||
) -> Optional[Dict[str, Any]]:
|
) -> Optional[Dict[str, Any]]:
|
||||||
name = CITY_NAME_ZH.get(city_key, city_key)
|
name = CITY_NAME_ZH.get(city_key, city_key)
|
||||||
|
|
||||||
# 1. Try CMA first for weather description + official forecast high
|
# 1. Try CMA first for weather description
|
||||||
cma = _fetch_cma_forecast(city_key)
|
cma = _fetch_cma_forecast(city_key)
|
||||||
if cma and cma.get("weather") and cma.get("forecast_high") is not None:
|
|
||||||
logger.info(
|
|
||||||
"daily_weather_report: {} using CMA weather={} high={}",
|
|
||||||
city_key,
|
|
||||||
cma["weather"],
|
|
||||||
cma["forecast_high"],
|
|
||||||
)
|
|
||||||
return {
|
|
||||||
"city": city_key,
|
|
||||||
"name": name,
|
|
||||||
"weather": cma["weather"],
|
|
||||||
"forecast_high": cma["forecast_high"],
|
|
||||||
}
|
|
||||||
|
|
||||||
# 2. Fall back to Open-Meteo
|
# 2. Get Open-Meteo data for temperature fallback
|
||||||
|
om_high = None
|
||||||
info = CITY_REGISTRY.get(city_key)
|
info = CITY_REGISTRY.get(city_key)
|
||||||
if not info:
|
if info:
|
||||||
return None
|
try:
|
||||||
|
results = collector.fetch_all_sources(
|
||||||
|
city_key,
|
||||||
|
lat=info["lat"],
|
||||||
|
lon=info["lon"],
|
||||||
|
include_taf=False,
|
||||||
|
include_ensemble=False,
|
||||||
|
include_multi_model=False,
|
||||||
|
)
|
||||||
|
if isinstance(results, dict):
|
||||||
|
om = results.get("open-meteo", {})
|
||||||
|
daily = om.get("daily", {}) if isinstance(om, dict) else {}
|
||||||
|
daily_highs = daily.get("temperature_2m_max", []) or []
|
||||||
|
om_high = daily_highs[0] if daily_highs else None
|
||||||
|
except Exception as exc:
|
||||||
|
logger.warning(
|
||||||
|
"daily_weather_report: OM fetch failed for {}: {}", city_key, exc
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
# Use CMA weather + CMA high, fall back to OM high
|
||||||
results = collector.fetch_all_sources(
|
weather: Optional[str] = None
|
||||||
city_key,
|
forecast_high: Optional[float] = None
|
||||||
lat=info["lat"],
|
|
||||||
lon=info["lon"],
|
|
||||||
include_taf=False,
|
|
||||||
include_ensemble=False,
|
|
||||||
include_multi_model=False,
|
|
||||||
)
|
|
||||||
except Exception as exc:
|
|
||||||
logger.warning(f"daily_weather_report: OM fetch failed for {city_key}: {exc}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
if not isinstance(results, dict):
|
if cma:
|
||||||
return None
|
weather = cma.get("weather")
|
||||||
|
forecast_high = cma.get("forecast_high")
|
||||||
om = results.get("open-meteo", {}) if isinstance(results, dict) else {}
|
|
||||||
current = om.get("current_weather", {}) if isinstance(om, dict) else {}
|
|
||||||
daily = om.get("daily", {}) if isinstance(om, dict) else {}
|
|
||||||
|
|
||||||
daily_highs = daily.get("temperature_2m_max", []) or []
|
|
||||||
today_high = daily_highs[0] if daily_highs else None
|
|
||||||
|
|
||||||
# Use CMA weather if available, fall back to WMO code translation
|
|
||||||
weather = (
|
|
||||||
cma.get("weather")
|
|
||||||
if (cma and cma.get("weather"))
|
|
||||||
else _wmo_to_weather(current.get("weathercode"))
|
|
||||||
)
|
|
||||||
forecast_high = cma.get("forecast_high") if cma else None
|
|
||||||
if forecast_high is None:
|
if forecast_high is None:
|
||||||
forecast_high = today_high
|
forecast_high = om_high
|
||||||
|
|
||||||
|
if not weather and not forecast_high:
|
||||||
|
return None
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"daily_weather_report: {} weather={} high={} (cma_ok={})",
|
||||||
|
city_key,
|
||||||
|
weather or "?",
|
||||||
|
forecast_high,
|
||||||
|
bool(cma and cma.get("weather")),
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"city": city_key,
|
"city": city_key,
|
||||||
"name": name,
|
"name": name,
|
||||||
"weather": weather,
|
"weather": weather or "?",
|
||||||
"forecast_high": forecast_high,
|
"forecast_high": forecast_high,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user