python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python每日天气提醒

使用Python编写一个每日天气提醒

作者:闲人编程

在这个快节奏的时代,一个小小的贴心举动往往能带来意想不到的温暖,本文就将教你如何使用Python构建一个自动化的每日天气提醒系统,有需要的可以参考下

1. 引言

在这个快节奏的时代,一个小小的贴心举动往往能带来意想不到的温暖。想象一下,每天清晨,你的女朋友都能收到一条温馨的天气提醒,不仅包含准确的天气信息,还有你亲自编写的暖心提示——这样的关怀是不是很让人心动?

本文就将教你如何使用Python构建一个自动化的每日天气提醒系统。这个系统不仅实用,更重要的是它融入了你的个人情感,让技术变得有温度。无论你是编程新手还是有经验的开发者,都能通过本文学会如何将技术转化为贴心的浪漫工具。

2. 系统设计与功能规划

2.1 系统架构设计

在开始编码之前,让我们先来规划整个系统的架构。我们的天气提醒系统需要包含以下几个核心模块:

2.2 核心功能特性

我们的天气提醒系统将实现以下特色功能:

2.3 技术选型

3. 准备工作与环境配置

3.1 申请天气API服务

首先,我们需要选择一个天气数据提供商。这里以和风天气(免费版)为例:

其他可选天气服务:

3.2 安装必要的Python库

pip install requests apscheduler python-dotenv sqlalchemy

如果需要微信支持,还需要安装:

pip install itchat-uos

3.3 环境变量配置

创建.env文件来存储敏感信息:

# 天气API配置
WEATHER_API_KEY=你的和风天气API_KEY
WEATHER_CITY=北京

# 邮箱配置(用于发送邮件提醒)
EMAIL_HOST=smtp.qq.com
EMAIL_PORT=587
EMAIL_USER=你的邮箱@qq.com
EMAIL_PASSWORD=你的邮箱授权码

# 接收方配置
RECIPIENT_EMAIL=女朋友的邮箱@gmail.com
RECIPIENT_NAME=亲爱的

4. 核心模块实现

4.1 天气数据获取模块

import requests
import json
from datetime import datetime, timedelta
import logging
from typing import Dict, List, Optional

class WeatherService:
    """天气服务类,负责获取和处理天气数据"""
    
    def __init__(self, api_key: str, city: str):
        """
        初始化天气服务
        
        Args:
            api_key: 和风天气API Key
            city: 城市名称
        """
        self.api_key = api_key
        self.city = city
        self.base_url = "https://devapi.qweather.com/v7"
        self.logger = logging.getLogger(__name__)
        
    def get_city_location(self) -> Optional[Dict]:
        """获取城市位置信息(城市ID、坐标等)"""
        url = f"{self.base_url}/weather/now"
        params = {
            'location': self.city,
            'key': self.api_key
        }
        
        try:
            response = requests.get(url, params=params, timeout=10)
            response.raise_for_status()
            data = response.json()
            
            if data['code'] == '200':
                # 从返回数据中提取位置信息
                location_info = {
                    'city': data.get('location', [{}])[0].get('name', self.city),
                    'id': data.get('location', [{}])[0].get('id', ''),
                    'lat': data.get('location', [{}])[0].get('lat', ''),
                    'lon': data.get('location', [{}])[0].get('lon', '')
                }
                return location_info
            else:
                self.logger.error(f"获取位置信息失败: {data.get('message', '未知错误')}")
                return None
                
        except requests.exceptions.RequestException as e:
            self.logger.error(f"网络请求失败: {str(e)}")
            return None
        except Exception as e:
            self.logger.error(f"获取位置信息异常: {str(e)}")
            return None
    
    def get_current_weather(self) -> Optional[Dict]:
        """获取当前天气情况"""
        url = f"{self.base_url}/weather/now"
        params = {
            'location': self.city,
            'key': self.api_key,
            'lang': 'zh'  # 中文返回
        }
        
        try:
            response = requests.get(url, params=params, timeout=10)
            response.raise_for_status()
            data = response.json()
            
            if data['code'] == '200':
                weather_data = data['now']
                return {
                    'temp': weather_data.get('temp', 'N/A'),  # 温度
                    'feels_like': weather_data.get('feelsLike', 'N/A'),  # 体感温度
                    'text': weather_data.get('text', 'N/A'),  # 天气状况文字
                    'wind_scale': weather_data.get('windScale', 'N/A'),  # 风力等级
                    'wind_dir': weather_data.get('windDir', 'N/A'),  # 风向
                    'humidity': weather_data.get('humidity', 'N/A'),  # 湿度
                    'precip': weather_data.get('precip', '0'),  # 降水量
                    'vis': weather_data.get('vis', 'N/A'),  # 能见度
                    'pressure': weather_data.get('pressure', 'N/A'),  # 气压
                    'update_time': weather_data.get('obsTime', '')  # 更新时间
                }
            else:
                self.logger.error(f"获取当前天气失败: {data.get('message', '未知错误')}")
                return None
                
        except requests.exceptions.RequestException as e:
            self.logger.error(f"获取当前天气网络请求失败: {str(e)}")
            return None
        except Exception as e:
            self.logger.error(f"获取当前天气异常: {str(e)}")
            return None
    
    def get_daily_forecast(self, days: int = 3) -> Optional[List[Dict]]:
        """获取多日天气预报"""
        url = f"{self.base_url}/weather/{days}d"
        params = {
            'location': self.city,
            'key': self.api_key,
            'lang': 'zh'
        }
        
        try:
            response = requests.get(url, params=params, timeout=10)
            response.raise_for_status()
            data = response.json()
            
            if data['code'] == '200':
                forecast_list = []
                for day_data in data.get('daily', []):
                    forecast = {
                        'date': day_data.get('fxDate', ''),
                        'temp_max': day_data.get('tempMax', 'N/A'),
                        'temp_min': day_data.get('tempMin', 'N/A'),
                        'text_day': day_data.get('textDay', 'N/A'),
                        'text_night': day_data.get('textNight', 'N/A'),
                        'wind_scale_day': day_data.get('windScaleDay', 'N/A'),
                        'humidity': day_data.get('humidity', 'N/A'),
                        'precip': day_data.get('precip', '0'),
                        'uv_index': day_data.get('uvIndex', 'N/A')
                    }
                    forecast_list.append(forecast)
                return forecast_list
            else:
                self.logger.error(f"获取天气预报失败: {data.get('message', '未知错误')}")
                return None
                
        except requests.exceptions.RequestException as e:
            self.logger.error(f"获取天气预报网络请求失败: {str(e)}")
            return None
        except Exception as e:
            self.logger.error(f"获取天气预报异常: {str(e)}")
            return None
    
    def get_weather_warning(self) -> Optional[List[Dict]]:
        """获取天气预警信息"""
        url = f"{self.base_url}/warning/now"
        params = {
            'location': self.city,
            'key': self.api_key,
            'lang': 'zh'
        }
        
        try:
            response = requests.get(url, params=params, timeout=10)
            response.raise_for_status()
            data = response.json()
            
            if data['code'] == '200':
                warnings = []
                for warning in data.get('warning', []):
                    warning_info = {
                        'type': warning.get('type', ''),
                        'level': warning.get('level', ''),
                        'title': warning.get('title', ''),
                        'text': warning.get('text', '')
                    }
                    warnings.append(warning_info)
                return warnings
            else:
                # 没有预警信息是正常情况
                return []
                
        except Exception as e:
            self.logger.warning(f"获取天气预警信息失败: {str(e)}")
            return []

4.2 智能分析与建议生成模块

class WeatherAnalyzer:
    """天气分析器,根据天气数据生成智能建议"""
    
    def __init__(self):
        self.advice_templates = {
            'dressing': self._get_dressing_advice,
            'umbrella': self._get_umbrella_advice,
            'outdoor': self._get_outdoor_advice,
            'health': self._get_health_advice,
            'travel': self._get_travel_advice
        }
    
    def analyze_temperature(self, temp: int) -> Dict[str, str]:
        """分析温度并给出建议"""
        temp = int(temp)
        
        if temp >= 30:
            level = "炎热"
            feeling = "今天天气炎热,注意防暑降温"
        elif temp >= 25:
            level = "温暖"
            feeling = "今天天气温暖,适合户外活动"
        elif temp >= 15:
            level = "舒适"
            feeling = "今天天气舒适,是出行的好日子"
        elif temp >= 5:
            level = "凉爽"
            feeling = "今天天气凉爽,记得适当添衣"
        elif temp >= 0:
            level = "寒冷"
            feeling = "今天天气寒冷,注意保暖"
        else:
            level = "严寒"
            feeling = "今天天气严寒,尽量减少外出"
            
        return {'level': level, 'feeling': feeling}
    
    def _get_dressing_advice(self, temp: int, weather_text: str) -> str:
        """生成穿衣建议"""
        temp = int(temp)
        
        if "雨" in weather_text:
            rain_note = "有雨,建议穿防水的衣物"
        else:
            rain_note = ""
            
        if temp >= 28:
            return f"👕 轻薄夏装(短袖、裙子){rain_note}"
        elif temp >= 24:
            return f"👚 夏季服装(短袖、薄长裤){rain_note}"
        elif temp >= 18:
            return f"👔 春秋过渡装(长袖T恤、薄外套){rain_note}"
        elif temp >= 12:
            return f"🧥 春秋装(长袖、薄毛衣、外套){rain_note}"
        elif temp >= 6:
            return f"🧣 初冬装(毛衣、厚外套){rain_note}"
        elif temp >= 0:
            return f"🧤 冬装(羽绒服、厚毛衣){rain_note}"
        else:
            return f"🧥 厚冬装(加厚羽绒服、保暖内衣){rain_note}"
    
    def _get_umbrella_advice(self, weather_text: str, precip: float) -> str:
        """生成雨具建议"""
        precip = float(precip)
        
        if "雨" in weather_text or precip > 0:
            if precip > 10:
                return "🌂 今天有大雨,一定要带伞,建议穿雨鞋"
            elif precip > 5:
                return "☔ 今天有中雨,记得带伞"
            else:
                return "🌧️ 今天有小雨,建议带伞"
        else:
            return "☀️ 今天无雨,可以不用带伞"
    
    def _get_outdoor_advice(self, weather_text: str, temp: int, wind_scale: str) -> str:
        """生成户外活动建议"""
        temp = int(temp)
        
        advice = []
        
        # 温度适宜性
        if 18 <= temp <= 26:
            advice.append("今天的温度非常适合户外活动")
        elif temp > 30:
            advice.append("天气炎热,建议在阴凉处活动")
        elif temp < 5:
            advice.append("天气较冷,户外活动请注意保暖")
        
        # 天气状况
        if "晴" in weather_text:
            advice.append("阳光很好,适合散步和运动")
        elif "雨" in weather_text:
            advice.append("有雨,建议室内活动")
        elif "云" in weather_text:
            advice.append("多云天气,适合各种户外活动")
        elif "雪" in weather_text:
            advice.append("下雪天,注意防滑")
            
        # 风力影响
        if int(wind_scale) >= 6:
            advice.append("今天风较大,注意安全")
            
        return "。".join(advice) if advice else "今天天气平稳,按计划活动即可"
    
    def _get_health_advice(self, temp: int, humidity: int, uv_index: str) -> str:
        """生成健康建议"""
        temp = int(temp)
        humidity = int(humidity)
        
        advice = []
        
        # 温度相关
        if temp >= 30:
            advice.append("天气炎热,注意补充水分,预防中暑")
        elif temp <= 0:
            advice.append("天气寒冷,注意保暖,预防感冒")
            
        # 湿度相关
        if humidity >= 80:
            advice.append("湿度较高,注意防潮")
        elif humidity <= 30:
            advice.append("空气干燥,注意保湿")
            
        # 紫外线
        if uv_index in ['5', '6', '7']:
            advice.append("紫外线中等,建议使用防晒霜")
        elif uv_index in ['8', '9', '10']:
            advice.append("紫外线强,需要做好防晒措施")
        elif uv_index in ['11', '12', '13', '14', '15']:
            advice.append("紫外线很强,尽量避免长时间在阳光下")
            
        return "。".join(advice) if advice else "今天天气条件对健康影响较小"
    
    def _get_travel_advice(self, weather_text: str, vis: str) -> str:
        """生成出行建议"""
        advice = []
        
        if "雨" in weather_text:
            advice.append("雨天路滑,开车请减速慢行")
        elif "雪" in weather_text:
            advice.append("雪天路滑,建议使用公共交通工具")
        elif "雾" in weather_text:
            advice.append("有雾,能见度较低,注意行车安全")
            
        if vis and int(vis) < 1:
            advice.append("能见度很低,出行请特别注意安全")
        elif vis and int(vis) < 5:
            advice.append("能见度一般,小心驾驶")
            
        return "。".join(advice) if advice else "今天出行条件良好"
    
    def generate_comprehensive_advice(self, weather_data: Dict) -> Dict[str, str]:
        """生成综合建议"""
        temp = int(weather_data.get('temp', 20))
        weather_text = weather_data.get('text', '')
        precip = float(weather_data.get('precip', 0))
        wind_scale = weather_data.get('wind_scale', '1')
        humidity = int(weather_data.get('humidity', 50))
        uv_index = weather_data.get('uv_index', '3')
        vis = weather_data.get('vis', '10')
        
        return {
            'temperature_analysis': self.analyze_temperature(temp),
            'dressing': self._get_dressing_advice(temp, weather_text),
            'umbrella': self._get_umbrella_advice(weather_text, precip),
            'outdoor': self._get_outdoor_advice(weather_text, temp, wind_scale),
            'health': self._get_health_advice(temp, humidity, uv_index),
            'travel': self._get_travel_advice(weather_text, vis)
        }

4.3 个性化消息生成模块

import random
from datetime import datetime

class MessageGenerator:
    """消息生成器,创建个性化的天气提醒消息"""
    
    def __init__(self, recipient_name: str):
        self.recipient_name = recipient_name
        self.greetings = [
            "早安,{}!新的一天开始啦",
            "早上好,{}!愿你有美好的一天",
            "{},醒来了吗?来看看今天的天气吧",
            "嗨,{}!今天也要加油哦",
            "{},新的一天充满新的可能"
        ]
        
        self.closing_remarks = [
            "记得按时吃饭,爱你❤️",
            "不管天气如何,我的关心永远不变💕",
            "今天也要开开心心的哦😊",
            "记得想我,就像我想你一样💖",
            "愿你的一天像阳光一样灿烂✨"
        ]
        
        self.special_date_remarks = {
            'anniversary': "今天是我们的纪念日,晚上有惊喜哦🎉",
            'birthday': "生日快乐!我的小公主🎂",
            'valentine': "情人节快乐!我的爱人🌹",
            'weekend': "周末愉快!我们出去约会吧🥰"
        }
    
    def _check_special_date(self) -> Optional[str]:
        """检查是否是特殊日期"""
        today = datetime.now()
        
        # 这里可以添加你们的特殊日期
        special_dates = {
            # 'anniversary': datetime(2024, 2, 14),  # 纪念日示例
            # 'birthday': datetime(2024, 3, 8),      # 生日示例
        }
        
        for occasion, date in special_dates.items():
            if today.month == date.month and today.day == date.day:
                return occasion
                
        # 检查周末
        if today.weekday() >= 5:  # 5=周六, 6=周日
            return 'weekend'
            
        return None
    
    def generate_message(self, weather_data: Dict, advice_data: Dict, 
                        forecast_data: List[Dict] = None) -> str:
        """生成完整的天气提醒消息"""
        
        # 问候语
        greeting = random.choice(self.greetings).format(self.recipient_name)
        
        # 当前天气摘要
        current_temp = weather_data.get('temp', 'N/A')
        weather_text = weather_data.get('text', 'N/A')
        temp_analysis = advice_data['temperature_analysis']
        
        weather_summary = (
            f"🌡️ 当前温度:{current_temp}°C({temp_analysis['level']})\n"
            f"🌤️ 天气状况:{weather_text}\n"
            f"💨 体感温度:{weather_data.get('feels_like', 'N/A')}°C\n"
            f"💧 湿度:{weather_data.get('humidity', 'N/A')}%\n"
            f"🌬️ 风力:{weather_data.get('wind_scale', 'N/A')}级 {weather_data.get('wind_dir', '')}"
        )
        
        # 生活建议
        advice_section = (
            f"\n📋 今日生活建议:\n"
            f"{advice_data['dressing']}\n"
            f"{advice_data['umbrella']}\n"
            f"{advice_data['outdoor']}\n"
            f"{advice_data['health']}\n"
            f"{advice_data['travel']}"
        )
        
        # 天气预报
        forecast_section = ""
        if forecast_data:
            forecast_section = "\n\n📅 未来三天预报:\n"
            for i, forecast in enumerate(forecast_data[:3]):
                date_str = forecast['date'][5:]  # 只显示月日
                day_name = "今天" if i == 0 else "明天" if i == 1 else "后天"
                forecast_section += (
                    f"{day_name}({date_str}):{forecast['text_day']},"
                    f"{forecast['temp_min']}~{forecast['temp_max']}°C\n"
                )
        
        # 预警信息
        warning_section = ""
        warnings = weather_data.get('warnings', [])
        if warnings:
            warning_section = "\n⚠️ 天气预警:\n"
            for warning in warnings:
                warning_section += f"{warning['title']}:{warning['text']}\n"
        
        # 特殊日期备注
        special_remark = ""
        special_occasion = self._check_special_date()
        if special_occasion:
            special_remark = f"\n🎊 {self.special_date_remarks[special_occasion]}"
        
        # 结束语
        closing = random.choice(self.closing_remarks)
        
        # 组合所有部分
        full_message = (
            f"{greeting}\n\n"
            f"{weather_summary}"
            f"{advice_section}"
            f"{forecast_section}"
            f"{warning_section}"
            f"{special_remark}\n\n"
            f"{closing}"
        )
        
        return full_message
    
    def generate_html_message(self, weather_data: Dict, advice_data: Dict,
                            forecast_data: List[Dict] = None) -> str:
        """生成HTML格式的消息(用于邮件)"""
        # 这里实现HTML格式的消息生成
        # 由于篇幅限制,具体实现略
        pass

4.4 消息发送模块

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import logging
from typing import List

class MessageSender:
    """消息发送器,支持多种发送方式"""
    
    def __init__(self):
        self.logger = logging.getLogger(__name__)
    
    def send_email(self, recipient: str, subject: str, content: str,
                  smtp_host: str, smtp_port: int, 
                  username: str, password: str) -> bool:
        """通过邮件发送消息"""
        
        try:
            # 创建邮件对象
            msg = MIMEMultipart()
            msg['From'] = username
            msg['To'] = recipient
            msg['Subject'] = subject
            
            # 添加文本内容
            text_part = MIMEText(content, 'plain', 'utf-8')
            msg.attach(text_part)
            
            # 发送邮件
            with smtplib.SMTP(smtp_host, smtp_port) as server:
                server.starttls()  # 启用TLS加密
                server.login(username, password)
                server.send_message(msg)
                
            self.logger.info(f"邮件发送成功:{recipient}")
            return True
            
        except Exception as e:
            self.logger.error(f"邮件发送失败:{str(e)}")
            return False
    
    def send_wechat(self, content: str) -> bool:
        """通过微信发送消息(需要安装itchat-uos)"""
        try:
            import itchat
            
            # 查找指定备注的好友
            friends = itchat.search_friends(name='女朋友的备注名')  # 修改为实际备注
            if friends:
                friend = friends[0]
                itchat.send(content, toUserName=friend['UserName'])
                self.logger.info("微信消息发送成功")
                return True
            else:
                self.logger.error("未找到指定的微信好友")
                return False
                
        except ImportError:
            self.logger.error("未安装itchat-uos,无法发送微信消息")
            return False
        except Exception as e:
            self.logger.error(f"微信消息发送失败:{str(e)}")
            return False
    
    def send_sms(self, phone_number: str, content: str, 
                api_key: str = None) -> bool:
        """通过短信API发送消息"""
        # 这里可以实现短信发送功能
        # 需要接入短信服务商API
        self.logger.info("短信发送功能需要接入短信服务商API")
        return False
    
    def send_multiple_ways(self, content: str, config: Dict) -> bool:
        """尝试多种方式发送消息"""
        success = False
        
        # 尝试邮件发送
        if config.get('email_enabled', False):
            success = self.send_email(
                recipient=config['recipient_email'],
                subject=config.get('email_subject', '每日天气提醒'),
                content=content,
                smtp_host=config['smtp_host'],
                smtp_port=config['smtp_port'],
                username=config['email_username'],
                password=config['email_password']
            )
        
        # 如果邮件发送失败,尝试微信
        if not success and config.get('wechat_enabled', False):
            success = self.send_wechat(content)
            
        return success

5. 系统集成与完整实现

现在我们将所有模块整合在一起,创建一个完整的天气提醒系统:

#!/usr/bin/env python3
"""
每日天气提醒系统
为你的女朋友提供贴心的天气提醒服务
日期:2024年1月
"""

import os
import logging
from datetime import datetime
from dotenv import load_dotenv
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
import sqlite3
from typing import Dict, Any

# 导入自定义模块
from weather_service import WeatherService
from weather_analyzer import WeatherAnalyzer
from message_generator import MessageGenerator
from message_sender import MessageSender

# 加载环境变量
load_dotenv()

class DailyWeatherReminder:
    """每日天气提醒系统主类"""
    
    def __init__(self, config: Dict[str, Any] = None):
        """初始化系统"""
        
        # 配置日志
        self._setup_logging()
        
        # 加载配置
        self.config = config or self._load_config()
        
        # 初始化组件
        self.weather_service = WeatherService(
            api_key=self.config['weather_api_key'],
            city=self.config['city']
        )
        self.weather_analyzer = WeatherAnalyzer()
        self.message_generator = MessageGenerator(
            recipient_name=self.config['recipient_name']
        )
        self.message_sender = MessageSender()
        
        # 初始化数据库
        self._init_database()
        
        # 初始化调度器
        self.scheduler = BackgroundScheduler()
        
        self.logger.info("每日天气提醒系统初始化完成")
    
    def _setup_logging(self):
        """配置日志系统"""
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
            handlers=[
                logging.FileHandler('weather_reminder.log'),
                logging.StreamHandler()
            ]
        )
        self.logger = logging.getLogger(__name__)
    
    def _load_config(self) -> Dict[str, Any]:
        """从环境变量加载配置"""
        return {
            'weather_api_key': os.getenv('WEATHER_API_KEY'),
            'city': os.getenv('WEATHER_CITY', '北京'),
            'smtp_host': os.getenv('EMAIL_HOST'),
            'smtp_port': int(os.getenv('EMAIL_PORT', 587)),
            'email_username': os.getenv('EMAIL_USER'),
            'email_password': os.getenv('EMAIL_PASSWORD'),
            'recipient_email': os.getenv('RECIPIENT_EMAIL'),
            'recipient_name': os.getenv('RECIPIENT_NAME', '亲爱的'),
            'send_time': os.getenv('SEND_TIME', '07:30'),  # 默认早上7:30发送
            'email_enabled': True,
            'wechat_enabled': False
        }
    
    def _init_database(self):
        """初始化SQLite数据库"""
        self.conn = sqlite3.connect('weather_reminder.db', check_same_thread=False)
        cursor = self.conn.cursor()
        
        # 创建发送记录表
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS send_history (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                send_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                success BOOLEAN,
                message TEXT,
                weather_data TEXT
            )
        ''')
        
        self.conn.commit()
    
    def _log_send_attempt(self, success: bool, message: str, weather_data: Dict):
        """记录发送尝试"""
        cursor = self.conn.cursor()
        cursor.execute(
            'INSERT INTO send_history (success, message, weather_data) VALUES (?, ?, ?)',
            (success, message, str(weather_data))
        )
        self.conn.commit()
    
    def get_weather_data(self) -> Dict[str, Any]:
        """获取完整的天气数据"""
        self.logger.info("开始获取天气数据...")
        
        weather_data = {}
        
        # 获取当前天气
        current_weather = self.weather_service.get_current_weather()
        if current_weather:
            weather_data.update(current_weather)
        else:
            self.logger.error("获取当前天气数据失败")
            return {}
        
        # 获取天气预报
        forecast = self.weather_service.get_daily_forecast(days=3)
        if forecast:
            weather_data['forecast'] = forecast
        else:
            self.logger.warning("获取天气预报数据失败")
        
        # 获取天气预警
        warnings = self.weather_service.get_weather_warning()
        if warnings:
            weather_data['warnings'] = warnings
        
        self.logger.info("天气数据获取完成")
        return weather_data
    
    def generate_weather_report(self) -> str:
        """生成完整的天气报告"""
        try:
            # 获取天气数据
            weather_data = self.get_weather_data()
            if not weather_data:
                return "抱歉,今天无法获取天气信息。请手动查看天气哦~"
            
            # 生成智能建议
            advice_data = self.weather_analyzer.generate_comprehensive_advice(weather_data)
            
            # 生成个性化消息
            forecast_data = weather_data.get('forecast', [])
            message = self.message_generator.generate_message(
                weather_data, advice_data, forecast_data
            )
            
            return message
            
        except Exception as e:
            self.logger.error(f"生成天气报告时发生错误: {str(e)}")
            return f"今天天气服务出了点小问题,但我的关心不会变~记得查看天气哦!"
    
    def send_daily_reminder(self):
        """发送每日提醒(主函数)"""
        self.logger.info("开始发送每日天气提醒...")
        
        try:
            # 生成天气报告
            message = self.generate_weather_report()
            
            # 发送消息
            success = self.message_sender.send_multiple_ways(message, self.config)
            
            # 记录发送结果
            weather_data = self.get_weather_data()  # 重新获取用于记录
            self._log_send_attempt(success, message, weather_data)
            
            if success:
                self.logger.info("每日天气提醒发送成功")
            else:
                self.logger.error("所有发送方式均失败")
                
            return success
            
        except Exception as e:
            self.logger.error(f"发送每日提醒时发生错误: {str(e)}")
            self._log_send_attempt(False, f"Error: {str(e)}", {})
            return False
    
    def setup_schedule(self):
        """设置定时任务"""
        # 解析发送时间
        hour, minute = map(int, self.config['send_time'].split(':'))
        
        # 添加每日定时任务
        self.scheduler.add_job(
            self.send_daily_reminder,
            trigger=CronTrigger(hour=hour, minute=minute),
            id='daily_weather_reminder',
            name='每日天气提醒',
            replace_existing=True
        )
        
        self.logger.info(f"已设置每日 {hour:02d}:{minute:02d} 发送天气提醒")
    
    def send_test_message(self):
        """发送测试消息"""
        self.logger.info("发送测试消息...")
        return self.send_daily_reminder()
    
    def start(self):
        """启动系统"""
        try:
            # 设置定时任务
            self.setup_schedule()
            
            # 启动调度器
            self.scheduler.start()
            
            self.logger.info("天气提醒系统已启动")
            
            # 立即发送一次测试消息
            self.send_test_message()
            
            # 保持程序运行
            try:
                while True:
                    # 主线程可以在这里执行其他任务
                    # 或者简单地等待
                    import time
                    time.sleep(60)
            except KeyboardInterrupt:
                self.logger.info("收到中断信号,正在关闭系统...")
                
        except Exception as e:
            self.logger.error(f"启动系统时发生错误: {str(e)}")
        finally:
            self.shutdown()
    
    def shutdown(self):
        """关闭系统"""
        if hasattr(self, 'scheduler') and self.scheduler.running:
            self.scheduler.shutdown()
        
        if hasattr(self, 'conn'):
            self.conn.close()
        
        self.logger.info("天气提醒系统已关闭")


def main():
    """主函数"""
    
    # 可以在这里覆盖默认配置
    custom_config = {
        'city': '上海',  # 修改为女朋友所在城市
        'send_time': '08:00',  # 修改为合适的发送时间
        # 添加其他自定义配置...
    }
    
    # 创建并启动系统
    reminder_system = DailyWeatherReminder(custom_config)
    reminder_system.start()


if __name__ == "__main__":
    main()

6. 部署与运行

6.1 本地运行

创建项目目录结构:

weather_reminder/
├── main.py
├── weather_service.py
├── weather_analyzer.py
├── message_generator.py
├── message_sender.py
├── .env
├── requirements.txt
└── config.json (可选)

安装依赖:

pip install -r requirements.txt

配置环境变量(.env文件):

WEATHER_API_KEY=你的和风天气API_KEY
WEATHER_CITY=北京
EMAIL_HOST=smtp.qq.com
EMAIL_PORT=587
EMAIL_USER=你的QQ邮箱
EMAIL_PASSWORD=QQ邮箱授权码
RECIPIENT_EMAIL=女朋友的邮箱
RECIPIENT_NAME=亲爱的
SEND_TIME=07:30

运行系统:

python main.py

6.2 服务器部署

对于24小时运行,建议部署到云服务器:

使用systemd服务(Linux):创建/etc/systemd/system/weather-reminder.service

[Unit]
Description=Daily Weather Reminder
After=network.target

[Service]
Type=simple
User=ubuntu
WorkingDirectory=/path/to/weather_reminder
ExecStart=/usr/bin/python3 main.py
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

使用Docker部署

创建Dockerfile:

FROM python:3.9-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

CMD ["python", "main.py"]

6.3 微信版本特别说明

如果要使用微信版本,需要注意:

安装itchat-uos(解决微信网页版登录问题):

pip install itchat-uos

修改代码使用微信发送:

# 在message_sender中启用微信发送
def send_wechat_message(self, content: str) -> bool:
    import itchat
    
    # 自动登录(会弹出二维码)
    itchat.auto_login(hotReload=True, enableCmdQR=2)
    
    # 发送给文件传输助手(测试用)
    itchat.send(content, toUserName='filehelper')
    
    # 或者发送给指定好友
    friends = itchat.search_friends(name='女朋友的微信昵称')
    if friends:
        itchat.send(content, toUserName=friends[0]['UserName'])
    
    itchat.logout()
    return True

7. 个性化定制建议

7.1 添加特殊日期提醒

MessageGenerator类中添加你们的特殊日期:

special_dates = {
    'anniversary': datetime(2024, 5, 20),  # 纪念日
    'birthday': datetime(2024, 8, 15),     # 生日
    'valentine': datetime(2024, 2, 14),    # 情人节
    'first_meet': datetime(2024, 3, 8)     # 初次见面日
}

7.2 自定义关怀语句

丰富问候语和结束语库:

self.greetings = [
    "早安,我的小太阳{}!",
    "{}宝贝,新的一天开始啦",
    "嘿,{}!今天也是爱你的一天",
    "早上好,{}!你是我今天第一个想念的人"
]

self.closing_remarks = [
    "记得按时吃饭,你胃不好~",
    "今天也要想我一千遍哦",
    "工作再忙也要记得休息,心疼你",
    "晚上视频哦,想你😘"
]

7.3 天气相关的情话

根据天气添加不同的情话:

def get_weather_pickup_line(self, weather_text: str) -> str:
    """根据天气生成情话"""
    lines = {
        '晴': "今天的阳光和你一样温暖我的心",
        '雨': "雨水可以打湿街道,但打不湿我想你的心",
        '云': "云朵遮住了太阳,但遮不住我对你的思念",
        '雪': "雪花飘落的时候,我在想你有没有穿暖",
        '风': "风儿吹过,带去我对你的牵挂"
    }
    
    for key, line in lines.items():
        if key in weather_text:
            return line
    return "无论什么天气,你都是我的晴天"

8. 故障排除与优化

8.1 常见问题解决

问题1:API调用失败

问题2:邮件发送失败

问题3:定时任务不执行

8.2 性能优化建议

缓存城市信息:避免重复查询城市位置

错误重试机制:网络请求失败时自动重试

备用数据源:准备多个天气API备用

消息队列:使用Redis等管理发送任务

8.3 监控与维护

9. 完整代码文件

由于完整的代码较长,这里提供各模块的文件划分:

9.1 requirements.txt

requests>=2.28.0
apscheduler>=3.9.0
python-dotenv>=0.19.0
python-dateutil>=2.8.0
itchat-uos>=1.5.0

9.2 weather_service.py

(包含WeatherService类,详见第4.1节)

9.3 weather_analyzer.py

(包含WeatherAnalyzer类,详见第4.2节)

9.4 message_generator.py

(包含MessageGenerator类,详见第4.3节)

9.5 message_sender.py

(包含MessageSender类,详见第4.4节)

9.6 main.py

(包含DailyWeatherReminder主类,详见第5节)

10. 总结

通过本文的指导,你已经学会了如何使用Python构建一个功能完整、贴心实用的每日天气提醒系统。这个系统不仅技术上有价值,更重要的是它承载了你的情感和关怀。

10.1 技术收获

10.2 情感价值

这个天气提醒系统的真正价值在于:

10.3 扩展思路

你可以进一步扩展这个系统:

记住,技术只是工具,真正打动人心的永远是你的用心和关怀。希望这个天气提醒系统能为你们的感情增添一份科技的温暖!

温馨提示:在使用过程中,请尊重女朋友的隐私和偏好,确保她喜欢这样的关怀方式。技术应该服务于感情,而不是替代真实的交流和相处。祝你们的感情像精心编写的代码一样,稳定而美好!

以上就是使用Python编写一个每日天气提醒的详细内容,更多关于Python每日天气提醒的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文