ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python每日天气推送助手:从定时任务到部署全攻略

Python每日天气推送助手:从定时任务到部署全攻略 之前一直在给业务做各种定时通知小工具后来发现每次都是一样的套路拉数据、拼文案、发推送。于是把其中一个最常用的每日推送脚本抽出来做成了一个可以复用的小项目名字就叫“天上掉下一只小日和”。这篇文章会完整拆解这个项目从设计到部署的整个过程包含天气信息采集、Server酱推送、定时调度、systemd 守护等核心环节。不管你是刚开始学 Python 的小白还是想做一个个人自动化服务的后端开发都能照着一步步把代码跑起来。1. 项目背景为什么需要“从天而降”的每日助手1.1 这个项目到底想解决什么问题每天早上的固定动作无非是看天气、看日期、看有没有重要安排。这些事情单独做并不难但总是容易忘尤其是天气突变的时候出门才发现降温就来不及了。“天上掉下一只小日和”这个名字其实是一个比喻。我们做一个常驻运行的小脚本每天早上 7 点 30 分准时把天气、日期、今日提醒推送到手机上。它不显眼也不需要你主动打开就像一份“从天而降”的小惊喜所以叫“小日和”。项目背后的真实需求是避免每天重复打开天气 App。让推送消息在固定时间触达形成习惯。为后续接入更多信息源新闻、待办、股票等打基础。这类需求在很多场景下都会用到比如个人提醒助手、企业群机器人消息、运维巡检日报。掌握这套“采集 组装 推送”的通用流程对日常开发帮助很大。1.2 技术选型定位整个项目采用 Python 编写核心思路是轻量、可复用、易扩展。主要模块如下模块作用选用原因requests请求第三方天气接口生态成熟上手简单datetime拼接日期与星期信息Python 标准库APScheduler定时调度任务支持 cron 表达式代码内可配置python-dotenv读取 .env 配置文件避免密钥硬编码Server酱推送消息到微信个人开发者友好对接简单这里选择 Server酱而不是自建消息推送是因为个人项目不需要维护 App 和长连接一个 HTTP 请求就能把消息推送到微信成本很低。如果你的团队有企业微信机器人或钉钉机器人也可以替换为对应的 webhook 地址。2. 项目整体设计2.1 功能拆分在写代码之前先想清楚项目要拆成哪些部分。如果不拆分全部堆在一个 main.py 里后续扩展会很痛苦。功能拆分如下配置管理模块负责读取环境变量和配置。天气采集模块调用天气接口解析天气数据。消息组装模块把日期、天气、提醒语组合成推送文案。推送模块把最终文案发送到 Server酱。定时任务模块统一管理每日执行时间。入口脚本分别提供单次执行和定时执行两种方式。这样拆的好处是每个文件职责单一。比如未来想把天气源从 wttr.in 换成其他服务商只需要修改天气采集模块其他部分完全不用动。2.2 架构流程整个项目的执行流程如下scheduler.py启动定时任务到达指定时间后触发主流程。主流程读取配置获取城市和推送密钥。天气模块请求wttr.in拿到当前天气和当日气温范围。消息模块拼接日期信息、天气信息和固定提醒文案。推送模块把文案发送到 Server酱。Server酱将消息推送到微信。日志中记录本次执行结果。整个过程只有一个外部依赖点也就是天气接口。推送通道是否稳定取决于 Server酱服务器的响应。后面会专门说明如何对接口故障做兜底处理。3. 环境准备与依赖说明3.1 运行环境项目不依赖复杂的运行时只要是 Python 3.8 及以上版本都能正常运行。本文示例以 Ubuntu 22.04 Python 3.10 为主但 Windows、macOS 同样可以本地运行。需要提前准备的工具和账号如下Python 3.8pip 包管理工具一个 Server酱账号用于获取 SendKey能访问外网的网络环境3.2 安装依赖建议创建一个虚拟环境避免与其他项目产生依赖冲突。mkdir xiaorihe cd xiaorihe python3 -m venv .venv source .venv/bin/activate激活虚拟环境后创建requirements.txt写入以下依赖requests2.31.0 python-dotenv1.0.0 APScheduler3.10.4然后安装pip install -r requirements.txt如果你的环境网络受限可以直接使用pip install requests python-dotenv APScheduler让 pip 自动选择当前环境下可用的版本。只要功能不冲突锁定大版本即可不必完全依赖上述固定版本号。3.3 推送渠道准备Server酱的对接流程非常简单。登录官网后在“消息通道”中复制自己的 SendKey一般形如SCTxxxxxxxxxxxxxxxxxx。后续我们会把 SendKey 写入.env文件同时用.gitignore忽略它避免泄露。4. 核心代码实现下面进入正文核心部分。我会按照文件路径逐个给出完整代码并解释关键逻辑。4.1 配置文件与 .env 示例首先创建.env.example这是配置模板提交到 Git 仓库时不会包含真实密钥。# 文件路径.env.example CITY北京 PUSH_KEY你的Server酱SendKey PUSH_CHANNELserverchan再创建.gitignore# 文件路径.gitignore .venv/ .env __pycache__/ logs/接下来创建真正的.env文件CITY北京 PUSH_KEYSCTxxxxxxxxxxxxxxxxxx PUSH_CHANNELserverchan注意PUSH_KEY是敏感信息千万不要提交到公开仓库。4.2 配置管理模块配置管理模块的作用是读取.env文件并向其他模块提供统一的配置入口。文件路径config.pyimport os from dotenv import load_dotenv load_dotenv() class Config: # 查询天气的城市注意 wttr.in 支持中文城市名但部分城市可能解析不了 # 如果请求异常可以改为拼音例如 Beijing CITY os.getenv(CITY, 北京) # Server酱推送密钥 PUSH_KEY os.getenv(PUSH_KEY, ) # 推送渠道先固定为 serverchan PUSH_CHANNEL os.getenv(PUSH_CHANNEL, serverchan)这里使用load_dotenv()自动加载.env文件。所有配置项都提供默认值避免某个环境变量缺失时报错。4.3 天气采集模块天气信息来自wttr.in这是一个免费、无需注册 API Key 的天气服务。使用formatj1参数可以获取 JSON 格式数据langzh可以请求中文天气描述。文件路径weather.pyimport requests def get_weather(city: str) - dict: 从 wttr.in 获取指定城市的当前天气与今日预报。 参数 city: 城市名称或拼音例如 北京 / Beijing 返回 包含天气信息的字典 url fhttps://wttr.in/{city}?formatj1langzh resp requests.get(url, timeout10) resp.raise_for_status() data resp.json() current data[current_condition][0] today data[weather][0] result { city: city, temp: current[temp_C], feels: current[FeelsLikeC], humidity: current[humidity], text: current[lang_zh][0][value], wind: current[windspeedKmph], high: today[maxtempC], low: today[mintempC], } return result代码说明current_condition数组里保存了当前天气数据。temp_C是摄氏度温度。FeelsLikeC是体感温度。humidity是相对湿度。lang_zh是中文天气描述由于接口结构是数组所以取第一个元素。weather[0]对应今天的气候预报maxtempC和mintempC是今天的最高气温和最低气温。有个细节需要注意wttr.in的字段结构偶尔会调整如果运行时报KeyError可以先用浏览器直接访问接口地址查看实际返回的 JSON 结构再调整解析逻辑。4.4 消息组装模块消息组装模块不直接请求接口只负责把天气数据和日期拼接成一段可读性强的文案。文件路径message.pyimport datetime from weather import get_weather def build_message(city: str) - str: 组装每日推送文案。 weather get_weather(city) now datetime.datetime.now() weekdays [一, 二, 三, 四, 五, 六, 日] weekday weekdays[now.weekday()] lines [ f今天是 {now.month} 月 {now.day} 日星期{weekday}, f地点{weather[city]}, f天气{weather[text]}, f温度{weather[temp]}℃体感 {weather[feels]}℃, f今日气温{weather[low]}℃ ~ {weather[high]}℃, f湿度{weather[humidity]}%, f风力{weather[wind]} km/h, , 小日和提醒你新的一天也要元气满满, ] return \n.join(lines)这里用f-string做字符串格式化可读性比较强。weekdays列表用来把weekday()返回的 0-6 数字转换为中文星期。4.5 推送模块推送模块负责把文案发送到 Server酱。Server酱的推送地址是https://sctapi.ftqq.com/{SendKey}.send请求方式为 POST参数为title和desp。title是消息标题desp是消息内容支持 Markdown 格式。文件路径push.pyimport requests from config import Config def send_serverchan(title: str, desp: str, key: str) - bool: 通过 Server酱发送推送。 参数 title: 标题 desp: 内容 key: Server酱 SendKey 返回 是否推送成功 url fhttps://sctapi.ftqq.com/{key}.send payload { title: title, desp: desp, } resp requests.post(url, datapayload, timeout10) body resp.json() if body.get(code) 0: return True print(Server酱返回错误, body) return False def push_message(title: str, content: str) - bool: 根据配置选择推送渠道。 if Config.PUSH_CHANNEL serverchan: return send_serverchan(title, content, Config.PUSH_KEY) raise ValueError(f暂不支持的推送渠道: {Config.PUSH_CHANNEL})Server酱的成功响应中code为 0。其他错误码可以结合官方文档排查常见的是 SendKey 错误、推送频率超限、内容长度超限等。4.6 单次执行入口单次执行入口main.py适合手动测试也适合配置到 crontab 中。文件路径main.pyfrom config import Config from message import build_message from push import push_message def main(): content build_message(Config.CITY) ok push_message(每日小日和, content) if ok: print(推送成功) else: print(推送失败) if __name__ __main__: main()此时项目目录结构如下xiaorihe/ ├── .env ├── .env.example ├── .gitignore ├── requirements.txt ├── config.py ├── weather.py ├── message.py ├── push.py └── main.py4.7 定时调度模块如果希望脚本常驻后台每天自动执行可以用 APScheduler 实现定时调度。文件路径scheduler.pyimport datetime from apscheduler.schedulers.blocking import BlockingScheduler from main import main def scheduled_job(): print(f开始执行定时任务{datetime.datetime.now()}) try: main() except Exception as e: print(f任务执行异常{e}) if __name__ __main__: scheduler BlockingScheduler() # cron 表达式6 个字段分别对应秒、分、时、日、月、星期 # 这里表示每天 7 点 30 分执行一次 scheduler.add_job(scheduled_job, cron, hour7, minute30) print(定时任务已启动等待执行...) scheduler.start()BlockingScheduler会阻塞当前进程适合部署在服务器上的独立任务。如果你已经有一个 Web 服务可以使用BackgroundScheduler把任务挂载到现有进程里。5. 部署与运行验证5.1 本地运行测试在项目根目录执行python main.py如果一切正常输出如下推送成功同时微信会收到一条消息。使用wttr.in接口时需要注意请求如果过于频繁可能触发限流。手动测试间隔建议至少 5 分钟以上。5.2 使用 Crontab 实现定时任务如果你不想常驻进程可以直接使用 Linux 系统自带的 crontab。输入crontab -e添加一行30 7 * * * cd /home/ubuntu/xiaorihe /home/ubuntu/xiaorihe/.venv/bin/python main.py /home/ubuntu/xiaorihe/logs/run.log 21注意这里的路径要改成你的真实项目路径。使用虚拟环境的 python 绝对路径是为了避免 cron 环境下找不到 Python 模块。创建日志目录mkdir -p logs以后每次执行结果都会写入logs/run.log排查问题非常方便。5.3 使用 systemd 实现开机自启常驻式任务推荐用 systemd 管理。创建一个 service 文件文件路径/etc/systemd/system/xiaorihe.service[Unit] DescriptionXiao Rihe Daily Push Service Afternetwork-online.target Wantsnetwork-online.target [Service] Userubuntu WorkingDirectory/home/ubuntu/xiaorihe ExecStart/home/ubuntu/xiaorihe/.venv/bin/python /home/ubuntu/xiaorihe/scheduler.py Restartalways RestartSec10 [Install] WantedBymulti-user.target执行以下命令启动并开机自启sudo systemctl daemon-reload sudo systemctl start xiaorihe sudo systemctl enable xiaorihe查看运行状态sudo systemctl status xiaorihe查看日志journalctl -u xiaorihe -f采用 systemd 方案时进程会一直保持运行。如果进程意外退出Restartalways会自动拉起比 crontab 更稳定。6. 常见问题与排查思路项目代码本身不复杂但部署和运行中容易遇到几个典型问题。问题现象常见原因解决思路微信收不到推送SendKey 错误或额度超限检查.env中密钥登录 Server酱后台确认额度推送返回 code 非 0标题或内容超长缩短标题或精简内容天气接口超时网络受限或接口限流增加超时时间添加重试机制定时任务不执行cron 环境缺少系统 PATH使用 python 绝对路径写入日志文件systemd 服务启动失败工作目录或虚拟环境路径错误执行journalctl -u xiaorihe查看具体报错中文字符乱码终端编码不是 UTF-8设置PYTHONIOENCODINGutf-8以天气接口超时为例可以简单改造weather.py加入重试逻辑import time import requests def get_weather(city: str, retry: int 3) - dict: url fhttps://wttr.in/{city}?formatj1langzh for attempt in range(retry): try: resp requests.get(url, timeout10) resp.raise_for_status() data resp.json() return data except Exception as e: print(f第 {attempt 1} 次请求失败{e}) time.sleep(2) raise RuntimeError(天气接口请求失败)这样即使第一次请求失败也会等待 2 秒后重试最多重试 3 次。7. 最佳实践与工程建议个人工具虽小但工程规范不能丢。7.1 密钥安全绝对不要把PUSH_KEY硬编码在代码里。项目中的.env文件要加入.gitignore。如果需要备份配置使用.env.example作为模板只保留变量名不保留真实密钥。7.2 日志与可观测性建议在关键步骤都输出日志例如任务开始执行时间。天气接口请求是否成功。推送返回码。异常堆栈。日志写到logs/目录后还可以配置logrotate做日志轮转避免日志文件无限膨胀。7.3 异常兜底推送服务要保证“主流程失败不能影响下一次执行”。当前scheduler.py中已经用try-except包住了main()但更推荐在单次执行入口中做兜底避免某个数据字段解析异常导致整个进程崩溃。7.4 配置外部化城市、推送密钥、推送渠道都属于可变配置都应该从环境变量或配置文件中读取。这样部署到不同环境时只需修改.env不用改代码。7.5 接口字段变化风险第三方接口难免调整字段。建议在代码中解析字段时给每个字段做一次“友好兜底”例如temp current.get(temp_C, 未知)这样即使接口少返回某个字段也不会直接让整个推送任务失败。7.6 最小权限部署生产环境部署时建议使用独立用户运行服务不要直接用 root。用户只需要项目目录的读写权限即可不要给不必要的 sudo 权限。8. 总结与扩展方向到这里“天上掉下一只小日和”这个项目已经可以正常工作了。它虽然简单但覆盖了一个自动化任务从开发、测试、部署到排错的全过程。你掌握的这套“请求接口 拼装文案 定时推送”模式可以直接复用到很多场景。如果想继续扩展可以考虑以下几个方向接入更多数据源比如早报新闻、微博热榜、待办事项。把 Server酱换成企业微信群机器人实现团队每日日报推送。增加一个简单的 Web 管理页面用来修改推送时间和城市配置。引入 Redis 或数据库记录历史推送记录方便查看推送成功率。实际部署时优先关注几个关键点密钥不要泄露、天气接口做好重试、定时任务日志必须保留、推送失败要有感知。把这几点做好这个“小日和”就不再是一个玩具脚本而是一个真正能长期稳定运行的个人服务。希望这篇文章能帮你把每日推送脚本从零跑起来。如果你在这个过程中遇到问题可以对照常见问题部分逐项排查也欢迎收藏备用。
返回列表