简介:这是一份面向零基础或略有编程经验用户的微信公众号天气推送制作资源,核心解决如何为伴侣定时发送每日天气与纪念日提醒的问题,支持在GitHub上每日自动线上运行。资源包共5个文件,包含py主程序、yml工作流配置、txt配置与说明、md文档等,压缩包仅5KB,轻量易部署,需自行申请天行、和风等天气API。内容覆盖最新完整版、简化版、星座版、星座加天气版及PHP版本,附带成品、源码与消息模板,方便按需选用。已有7695人学习下载,说明该方案在情侣互动场景中接受度较高。读者可从中获得可直接运行的推送脚本、GitHub Actions自动执行配置、消息模板与多版本对照思路,既能快速搭建属于自己的天气通知公众号,也能借此理解定时任务与接口调用的基本流程,适合想用低成本方式制造日常惊喜的开发者尝试。
1. 从一条“今天记得带伞”开始:公众号天气推送到底怎么跑通
有个做后端的朋友,女朋友嫌他每天只会发“早安”,他干脆花了一个周末,用微信公众号做了个每天定时推送天气的小工具。上线第一周,女朋友觉得挺新鲜;第二周开始,她出门前会先看一眼推送里的降温和降雨提醒。这件事的技术含量其实不高,但它把“微信公众号 天气通知”这条链路完整跑通了:定时任务触发、第三方天气接口拉数据、模板消息组装、调用微信接口下发。整套东西一台最便宜的云服务器就能跑,成本几乎为零。
这份教程适合三类人:一是想练手微信公众号开发、但不知道做什么练手项目的新手;二是想给身边人做点实用小工具的开发者;三是已经会写代码,但没接触过微信模板消息、订阅通知、定时任务调度的熟手。它不涉及复杂算法,难点全在“配置对不对、参数有没有过期、接口返回码看不看得懂”这些琐碎地方。下面按“先搞清楚原理,再动手复现,最后避开坑”的顺序拆开讲。
2. 把链路拆开:公众号、天气接口、定时任务各管什么
2.1 三个角色和一次推送的完整生命周期
先建立一个心智模型。整个系统里有三个角色:微信公众号(消息出口)、天气数据源(消息内容)、你的服务器(调度中枢)。一次推送的生命周期是这样的:服务器上的定时任务在每天早上 7 点被唤醒,它拿着预先配置好的城市编码去请求天气接口,拿到 JSON 数据后解析出温度、天气现象、风力、降雨概率,再按模板格式拼成一段文本,最后调用微信的接口把这段文本推给指定用户。
这里有个关键认知:公众号本身不会主动去查天气,它只是一个消息通道。所有逻辑都在你自己的服务器上。所以“推送天气”这件事,本质是“你的程序定时跑一次,把结果通过微信的通道发出去”。理解这一点,后面所有配置就都顺了。
微信这边目前主流有两种下发方式:模板消息和订阅通知。模板消息适合服务号,用户关注后即可长期接收;订阅通知适合订阅号,需要用户每次授权一次或多次。个人做给女朋友用,通常用测试号或服务号走模板消息最省事,因为测试号不需要认证就能拿到模板消息权限,调试阶段非常友好。
2.2 天气数据源怎么选:免费接口的字段与限制
天气接口的选择直接决定你后面解析代码怎么写。常见做法是选一家提供免费额度的天气 API,重点看三个东西:是否支持城市编码/经纬度查询、返回字段是否包含未来天气和降雨概率、免费调用次数够不够每天一次。
以常见的返回结构为例,核心字段一般长这样:
| 字段名 | 含义 | 用途 |
|---|---|---|
| city / location | 城市名 | 推送开头显示 |
| temp / temperature | 当前温度 | 正文核心数据 |
| text / weather | 天气现象 | 晴/雨/阴 |
| humidity | 湿度 | 可选展示 |
| windScale / wind | 风力 | 可选展示 |
| forecast | 未来几天预报 | 做“明天降温”提醒 |
选接口时有个血泪经验:别只看文档写的字段名,一定要用 curl 实际请求一次看真实返回。很多接口文档和实际返回的字段名对不上,或者免费版砍掉了 forecast 字段。先跑通一次真实请求,再写解析代码,能省掉大量返工。
提示:免费天气接口通常有 QPS 限制和每日调用上限。个人使用每天一次完全够,但如果你打算给多个人推送,记得把结果缓存下来复用,别每个人查一次。
2.3 定时任务:cron 表达式与调度方式
定时任务负责“每天几点触发”。最轻量的做法是在服务器上写一个 Python 脚本,用系统自带的 cron 调度。cron 表达式五个字段分别是:分、时、日、月、周。比如每天早上 7 点整触发:
# 编辑当前用户的定时任务 crontab -e # 每天早上 7:00 执行推送脚本,日志追加到 push.log 0 7 * * * /usr/bin/python3 /home/user/weather_push.py >> /home/user/push.log 2>&1这段配置的含义:0 7 * * *表示每天 7 点 0 分执行;后面是脚本的绝对路径,必须用绝对路径,因为 cron 的环境变量和你登录 shell 的环境变量不一样,用相对路径或依赖 PATH 经常会找不到命令;>> push.log 2>&1把标准输出和错误都写进日志,方便排查。
如果你不想用 cron,也可以用 Python 的schedule库或APScheduler在脚本内部循环调度。区别是:cron 由系统托管,脚本崩了下次照样触发;内部调度需要脚本常驻,进程挂了就全停了。个人项目推荐 cron,简单可靠。
3. 动手复现:从测试号配置到推送脚本落地
3.1 拿到 appID、appsecret 和用户 openid
动手第一步是把微信侧的三个关键凭证拿到手:appID、appsecret、用户 openid。用测试号的话,登录测试号管理页面就能看到 appID 和 appsecret。openid 是每个用户对每个公众号唯一的标识,你需要让女朋友先关注这个测试号,然后通过“接收消息”或“获取用户列表”接口拿到她的 openid。
获取 access_token 是所有微信接口调用的前置步骤,它相当于调用凭证,有效期通常 7200 秒,过期要重新获取。所以脚本里不能写死,每次运行前先换一个新的:
import requests APPID = "你的appID" APPSECRET = "你的appsecret" def get_access_token(): """获取微信接口调用凭证,有效期约2小时""" url = "https://api.weixin.qq.com/cgi-bin/token" params = { "grant_type": "client_credential", "appid": APPID, "secret": APPSECRET } resp = requests.get(url, params=params, timeout=10) data = resp.json() # 返回码 0 表示成功,其他值要打印出来排查 if data.get("errcode"): raise RuntimeError(f"获取token失败: {data}") return data["access_token"]逻辑说明:grant_type固定填client_credential;appid和secret从配置读取。参数说明:timeout=10防止请求卡死;判断errcode是因为微信失败时不一定返回 HTTP 错误码,而是返回一个带errcode的 JSON,不判断就会拿到一个空 token 继续往下跑,最后报一个看不懂的错。
注意:appsecret 泄露等于别人可以冒充你的公众号发消息。别把它硬编码进要上传到公开仓库的代码里,用环境变量或单独的配置文件,并且把配置文件加进
.gitignore。
3.2 拉取天气并组装模板消息
拿到 token 后,先请求天气接口,再把数据填进模板。模板消息的data字段是一个键值对结构,每个键对应模板里定义的一个占位符。假设模板里定义了city、date、weather、temp、tip五个字段:
import requests from datetime import datetime def get_weather(city_code): """请求天气接口,返回解析后的字典""" url = "https://api.example-weather.com/v1/current" params = {"city": city_code, "key": "你的天气接口key"} resp = requests.get(url, params=params, timeout=10) raw = resp.json() # 按真实返回结构取值,字段名以实际请求结果为准 return { "city": raw["data"]["city"], "weather": raw["data"]["text"], "temp": raw["data"]["temp"], "humidity": raw["data"]["humidity"] } def build_message(openid, weather): """组装模板消息体""" today = datetime.now().strftime("%Y年%m月%d日") tip = "今天有雨,记得带伞" if "雨" in weather["weather"] else "今天天气不错,放心出门" return { "touser": openid, "template_id": "你的模板ID", "data": { "city": {"value": weather["city"]}, "date": {"value": today}, "weather": {"value": weather["weather"]}, "temp": {"value": f'{weather["temp"]}℃'}, "tip": {"value": tip} } }逻辑说明:get_weather负责把接口原始返回整理成干净字典,隔离字段名变化;build_message负责拼装,tip用简单的关键词判断生成一句人话。参数说明:touser是接收者的 openid;template_id是你在公众号后台选定的模板 ID;data里每个字段的value必须是字符串,传数字有时会报格式错误。
3.3 调用下发接口并处理返回码
最后一步是把组装好的消息 POST 给微信的模板消息接口:
def send_message(access_token, message): """下发模板消息,返回微信的响应""" url = f"https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={access_token}" resp = requests.post(url, json=message, timeout=10) result = resp.json() # 40001 通常是 token 失效,40037 是模板ID错误 if result.get("errcode") != 0: print(f"下发失败: {result}") else: print("下发成功") return result if __name__ == "__main__": token = get_access_token() weather = get_weather("101010100") msg = build_message("女朋友的openid", weather) send_message(token, msg)逻辑说明:token 拼在 URL 上,消息体用json=传。参数说明:errcode为 0 才算成功;常见错误码里,40001表示 token 无效(多半是过期或 appsecret 错),40037表示 template_id 不对,43004表示用户没关注或 openid 不对。把返回码打印出来,排查效率会高很多。
4. 避坑与排查:那些让推送“玄学失败”的细节
4.1 现象:本地跑得好好的,服务器上不推送
原因:cron 执行时的环境和你手动执行时不同,最常见的是 Python 路径不对、依赖库没装在系统 Python 里、或者脚本里用了相对路径读配置文件。
解决:cron 里用绝对路径调用 Python 和脚本;在脚本开头把工作目录切到脚本所在目录,或者所有文件路径都写绝对路径;用which python3确认解释器路径。日志里如果出现ModuleNotFoundError,说明依赖装在了虚拟环境而 cron 用的是系统 Python,改成虚拟环境里的解释器路径即可。
4.2 现象:接口返回 40001,token 明明刚获取
原因:access_token 是全局唯一的,如果你在别的地方(比如另一个脚本或调试工具)也调用了获取 token 的接口,新 token 会把旧的顶掉,导致你手里的 token 瞬间失效。
解决:全项目只保留一个获取 token 的入口,拿到后缓存起来(比如写进文件或内存),在有效期内复用。调试时别反复手动调 token 接口。
4.3 现象:模板消息报 40037 或提示字段不匹配
原因:template_id 填错,或者data里的键名和模板定义的占位符对不上。模板里叫weather,你传了weatherText,就会报错。
解决:去公众号后台把模板内容复制出来,逐个核对占位符名称,大小写敏感。改完模板后 template_id 可能变化,要同步更新配置。
4.4 现象:推送成功但内容全是空白或乱码
原因:天气接口返回的字段名和代码里取的不一致,取到了空值;或者编码没处理好。
解决:在get_weather里先把raw完整打印一次,对照真实字段名改取值路径。中文乱码一般是响应头编码问题,requests通常能自动处理,如果不行手动指定resp.encoding = "utf-8"。
4.5 现象:某天突然不推了,日志也没报错
原因:免费天气接口额度用尽、key 过期,或者微信侧模板被下架。这类问题不会抛异常,只是返回空数据或错误码。
解决:在脚本里对关键返回值做校验,天气数据为空时主动打印告警;给 cron 加一个“心跳”机制,比如每天推送成功后往日志写一行带日期的记录,隔天扫一眼就知道有没有断。
5. 进阶玩法:让推送更“像人发的”几个技巧
跑通基础版之后,真正拉开体验差距的是内容组织。我一般会做三件事。第一,加一句基于天气的个性化提醒:温度低于 5℃ 提醒穿厚外套,降雨概率高于 60% 提醒带伞,温差超过 8℃ 提醒添衣。判断逻辑就是几个 if,但读起来完全不一样。第二,把未来一天的预报也带上,比如“明天降温 6℃,记得加衣”,这需要天气接口支持 forecast 字段,取forecast[1]的数据和今天对比即可。第三,控制推送时间,别设太早,7 点到 7 点半之间比较合适,太早打扰休息,太晚出门前看不到。
验证推送是否正常,我习惯用一个“干跑”模式:在脚本里加一个DRY_RUN开关,打开时只打印组装好的消息内容,不真正调用下发接口。这样调试模板格式时不会因为频繁调用触发频率限制,也不会给女朋友发一堆测试消息。
DRY_RUN = True # 调试时打开,只打印不发送 if __name__ == "__main__": token = get_access_token() weather = get_weather("101010100") msg = build_message("女朋友的openid", weather) if DRY_RUN: print("预览消息:", msg) else: send_message(token, msg)这个开关看着简单,但能省掉很多“手滑发出去”的后悔药。从那以后我每次改模板格式,都强制先走一遍DRY_RUN,确认文案和字段没问题再关掉。希望帮到你。
本文还有配套的精品资源,点击获取