简介这份资源面向具备少量编程基础、希望为伴侣定制每日问候的开发者围绕微信公众号定时推送天气与纪念日提醒这一场景提供可落地的实现方案。资源包共5个文件以py主程序、yml自动化工作流配置、txt配置与说明、md文档为主压缩包约5KB体积轻巧便于直接部署到GitHub Actions实现每日线上自动运行。内容涵盖最新完整版、简化版、星座单独版、星座加天气版以及php版本等多套成品与消息模板需自行申请天行、和风等天气接口的API密钥。目前已有7695人学习下载读者可据此快速搭建属于自己的推送服务理解定时任务、接口调用与消息模板配置的完整链路并参考多版本差异按需裁剪功能省去从零摸索的时间。1. 从一条“今天降温记得加衣”说起公众号天气推送到底怎么跑通很多人第一次动念头做天气推送不是因为想学开发而是因为某天早上收到一句“今天降温记得加衣”比闹钟还管用。微信公众号每日消息推送天气给女朋友制作教程本质上就是一套“定时抓天气 → 拼成文案 → 调公众号接口发出去”的最小闭环。它不复杂但涉及三个容易翻车的环节天气数据从哪来、模板消息怎么发、定时任务怎么稳。适合会一点 Python、想给身边人做点小工具的人也适合想练手服务号接口的开发者。下面按我实际拆过的顺序讲能直接抄。2. 选型与准备天气接口、公众号类型和服务器怎么定2.1 先确认你的公众号有没有发模板消息的资格微信公众号分订阅号和服务号个人主体一般只能注册订阅号而订阅号没有模板消息接口权限。这是第一个卡点很多人代码写完了才发现调不通。常见做法是如果只是发给一个人用服务号企业主体最顺如果只有订阅号可以退而求其次用“客服消息”但客服消息要求用户 48 小时内有过互动不适合每天定时推。所以选型阶段先登录公众平台在“功能 → 模板消息”里看有没有入口没有就别往下写代码了换方案。模板消息本身需要先申请一个模板模板里用{{keyword.DATA}}占位。天气推送一般申请两个字段城市天气概况、温馨提示。审核通过后你会拿到一个template_id这个后面要写进配置。2.2 天气数据源免费接口够不够用天气数据我用的是和风天气的免费版注册后拿到key接口返回 JSON字段清晰。免费版每天有调用次数限制个人推送一天一次完全够。请求地址形如# 实时天气接口location 填城市 ID 或经纬度 https://devapi.qweather.com/v7/weather/now?location101010100key你的KEY返回里now.temp是温度now.text是天气现象。注意免费版部分字段比如生活指数要单独申请别一股脑全调容易超限。如果你不想注册也可以用 OpenWeatherMap但国内访问偶尔慢自己权衡。2.3 服务器和运行环境定时任务需要一个能常驻的地方。最省事的是云函数比如某云厂商的定时触发器不用自己维护服务器其次是买一台最低配的云服务器装 Python 3.8 和requests、apscheduler两个库。我一般用云服务器 crontab因为调试方便日志能直接看。目录结构建议这样weather-push/ ├── config.py # 存 key、template_id、用户 openid ├── weather.py # 抓天气、拼文案 ├── push.py # 调公众号接口 └── main.py # 入口串起来把敏感信息放config.py别硬编码在业务代码里后面换 key 不用翻全文件。3. 核心实现抓天气、拼模板、调接口三步走3.1 抓天气并组装成模板需要的字段先写weather.py负责请求和解析。关键是把接口返回的字段映射成模板占位符别在推送时才拼字符串容易乱。import requests def get_weather(city_id, key): # 实时天气接口超时设 5 秒避免卡死定时任务 url fhttps://devapi.qweather.com/v7/weather/now?location{city_id}key{key} resp requests.get(url, timeout5) data resp.json() # 接口返回 code 为 200 才算成功其他都是异常 if data.get(code) ! 200: raise RuntimeError(f天气接口返回异常: {data.get(code)}) now data[now] return { temp: now[temp], # 温度字符串 text: now[text], # 天气现象如“晴” feels: now[feelsLike] # 体感温度 }逻辑说明timeout5是防止接口挂起导致整个任务卡住code判断不能省免费接口偶尔会返回限流码。参数city_id用和风天气的城市列表查比如北京是101010100别用中文城市名容易匹配错。3.2 拼模板文案把冷冰冰的数字变成人话模板消息的data字段是键值对每个值要符合模板定义的格式。我一般把天气概况和提示分开def build_template_data(weather): # 根据温度给一句提示阈值可以自己调 temp int(weather[temp]) if temp 5: tip 今天很冷出门记得穿厚外套 elif temp 15: tip 有点凉带件外套别感冒 else: tip 温度还行但别贪凉 return { weather: {value: f{weather[text]} {weather[temp]}℃}, tip: {value: tip} }这里weather和tip必须和你在公众号后台申请的模板字段名一致大小写都不能错。value是固定写法别直接写字符串。温度阈值我试过 5/15 两档够用想更细可以加湿度、风力但模板字段要提前申请。3.3 调公众号接口发送access_token 是绕不开的坑发送前要先拿access_token它有效期 7200 秒且每天获取次数有限不能每次推送都重新拿。常见做法是缓存到本地文件或 Redis过期再刷新。import requests, json, time, os def get_access_token(appid, secret): cache_file token_cache.json # 先读缓存没过期直接用 if os.path.exists(cache_file): with open(cache_file) as f: cache json.load(f) if cache[expire_at] time.time(): return cache[token] # 缓存失效重新请求 url fhttps://api.weixin.qq.com/cgi-bin/token?grant_typeclient_credentialappid{appid}secret{secret} resp requests.get(url, timeout5).json() if access_token not in resp: raise RuntimeError(f获取 token 失败: {resp}) token resp[access_token] # 提前 300 秒过期留出网络延迟余量 with open(cache_file, w) as f: json.dump({token: token, expire_at: time.time() resp[expires_in] - 300}, f) return token def send_template(openid, template_id, data, token): url fhttps://api.weixin.qq.com/cgi-bin/message/template/send?access_token{token} payload { touser: openid, template_id: template_id, data: data } resp requests.post(url, jsonpayload, timeout5).json() # errcode 为 0 才算发送成功 if resp.get(errcode) ! 0: raise RuntimeError(f发送失败: {resp}) return resp参数说明touser是接收者的 openid不是微信号需要用户先关注公众号并授权获取template_id是模板申请后拿到的 IDdata就是上一步拼好的字典。errcode判断必须做常见错误 40001 是 token 失效40037 是模板 ID 不对。3.4 用定时任务串起来最后main.py把三步串起来再用crontab每天固定时间跑from weather import get_weather from push import get_access_token, send_template from config import CITY_ID, KEY, APPID, SECRET, TEMPLATE_ID, OPENID def main(): weather get_weather(CITY_ID, KEY) data build_template_data(weather) token get_access_token(APPID, SECRET) send_template(OPENID, TEMPLATE_ID, data, token) if __name__ __main__: main()crontab配置# 每天早上 7 点执行日志写到 push.log 0 7 * * * /usr/bin/python3 /home/user/weather-push/main.py /home/user/weather-push/push.log 21日志重定向别省出问题全靠它。时间用服务器时区如果服务器是 UTC记得换算成北京时间。4. 避坑与排查那些让我半夜爬起来改代码的瞬间4.1 现象接口返回 40001token 明明刚拿的原因access_token被其他程序刷新了或者你本地缓存和服务器缓存不一致。公众号的 token 是全局的同一时间只能有一个有效。解决统一用一处缓存别多个脚本各拿各的如果用了云函数把 token 存到云数据库而不是本地文件。4.2 现象模板消息发出去用户收不到原因用户取关了或者 openid 不对。openid 是公众号下的唯一标识换公众号就变了。解决发送前先调user/info接口查一下关注状态取关了就别发省得浪费调用次数。4.3 现象天气接口偶尔超时任务直接挂原因免费接口有波动requests没设重试。解决加一层重试用tenacity库或者手写循环重试 2 次间隔 1 秒。别无限重试会把定时任务拖死。4.4 现象crontab 不执行手动跑却正常原因crontab 的环境变量和登录 shell 不一样python3路径可能找不到。解决在 crontab 里写绝对路径或者在脚本开头source一下环境。我一般直接在 crontab 里写/usr/bin/python3省事。4.5 现象推送时间不准偶尔晚几分钟原因服务器负载或者 crontab 调度延迟。解决对时间要求高就改用apscheduler常驻进程精度到秒但常驻进程要处理异常退出各有利弊。5. 进阶技巧让推送更稳、更省、更像“人发的”跑通之后我一般会做三件事。第一加一个“失败告警”如果发送失败给自己发一封邮件或者一条 Server 酱消息别等女朋友问“今天怎么没发”才发现。第二把天气接口的返回缓存 10 分钟防止同一天多次调试触发限流。第三文案别太机械可以按星期几换句式比如周五加一句“周末愉快”成本极低但体验差很多。验证方法很简单手动跑一次main.py看日志里errcode是不是 0再看手机有没有收到。如果收到但字段错位多半是模板字段名和data的 key 没对上回公众号后台核对。检查项正常表现异常处理access_token缓存文件存在且未过期删除缓存重新获取天气接口 code返回 200检查 key 和城市 ID发送 errcode返回 0按错误码查文档crontab 日志有执行记录检查路径和权限从那以后我每次上线定时任务前都强制先手动跑三遍确认 token 缓存、接口重试、日志输出都正常再交给 crontab。希望帮到你。本文还有配套的精品资源点击获取