Python天气查询脚本开发:wttr.in接口调用与命令行解析
这是一篇基于实际项目经验的Python脚本编程记录。很多初学者在学完基础语法后,往往卡在“能写什么”这一步。天气查询是一个覆盖面很合适的练手场景:它要用到网络请求、JSON解析、参数传递、异常处理,以及简单的函数封装。下面从项目设计、代码实现到运行调试完整拆解。一、项目形态与接口选型
首先要明确,Python写的天气小程序不等同于微信小程序。常见的形态有两种:一种是命令行/桌面小工具,输入城市名打印天气;另一种是给微信小程序做后端接口,Python通过Flask或FastAPI提供数据。两种形态的核心逻辑一致,都是“给定城市名,返回结构化天气数据”。建议先做命令行版本,把请求、解析、异常处理练熟,再考虑包HTTP接口。
天气数据不能自己造,需要借助第三方接口。对比了几种免费方案:wttr.in不需要注册Key,返回格式支持JSON/文本/PNG,可通过lang=zh输出中文,适合个人工具和快速演示;和风天气和需要Key且配额有限,更适合真实小程序后端;OpenWeatherMap也有每分钟次数限制。个人练手首选wttr.in。
wttr.in背后的数据源覆盖全球,城市名可以直接用中文。关键请求参数是format=j1(返回完整JSON)和lang=zh(中文天气描述)。例如:https://wttr.in/北京?format=j1&lang=zh。
本项目依赖很简单:Python 3.8+、requests库。整个程序一个文件即可。目录结构建议:weather_cli/ 下放weather.py主程序,requirements.txt内容为requests。
二、核心代码实现
先准备好环境,检查Python与pip版本,然后安装requests库:
python --version
pip --version
pip install requests
核心代码包括城市名URL编码、天气请求、当前天气格式化、未来三天预报格式化、主入口。以下是完整实现:
import requests
import sys
from urllib.parse import quote
def get_weather(city: str) -> dict:
"""请求天气接口,返回解析后的JSON字典"""
url = f"https://wttr.in/{quote(city)}?format=j1&lang=zh"
try:
resp = requests.get(url, timeout=10)
resp.raise_for_status()
return resp.json()
except requests.Timeout:
print("[错误] 请求超时,请检查网络后重试")
except requests.RequestException as e:
print(f"[错误] 请求失败: {e}")
return {}
def format_current(current: dict) -> str:
"""格式化当前天气信息"""
temp = current["temp_C"]
feels = current["FeelsLikeC"]
humidity = current["humidity"]
wind = current["windspeedKmph"]
desc = current["lang_zh"]["value"] if "lang_zh" in current else current["weatherDesc"]["value"]
return (
f"当前温度: {temp}°C\n"
f"体感温度: {feels}°C\n"
f"天气状况: {desc}\n"
f"相对湿度: {humidity}%\n"
f"风速: {wind} km/h"
)
def format_forecast(weather: list) -> str:
"""格式化未来三天预报"""
lines = ["\n未来三天预报:"]
for day in weather[:3]:
date = day["date"]
mintemp = day["mintempC"]
maxtemp = day["maxtempC"]
hourly = day["hourly"]
desc = hourly["lang_zh"]["value"] if "lang_zh" in hourly else hourly["weatherDesc"]["value"]
lines.append(f"{date}: {desc}, {mintemp}°C ~ {maxtemp}°C")
return "\n".join(lines)
def main():
if len(sys.argv) > 1:
city = " ".join(sys.argv)
else:
city = input("请输入城市名: ").strip()
if not city:
print("城市名不能为空")
return
data = get_weather(city)
if not data:
return
current = data["current_condition"]
print(f"\n{city} 实时天气:")
print(format_current(current))
print(format_forecast(data["weather"]))
if __name__ == "__main__":
main()
这里有几个关键细节值得说明。
timeout=10不是随便写的。接口正常响应通常在1到2秒,10秒超时能应对慢网络,又不会让用户长时间等待。
raise_for_status()会在HTTP状态码为4xx或5xx时抛出异常,防止拿到错误页面后继续解析出奇怪的错误。
城市名用urllib.parse.quote做URL编码非常必要。直接把“北京”放进URL可能产生编码问题,编码后接口才能正确识别。
接口返回的数据中,current_condition是当前天气数组,weather是多天预报数组。实际返回字段很多,这里只取了温度、体感、描述、湿度、风速五个维度。天气描述字段默认返回英文,只有加lang=zh才会返回lang_zh数组,代码中做了兼容,找不到中文描述时回退英文。
三、运行与输出效果
运行方式有两种。第一种是交互式:
python weather.py
程序提示“请输入城市名”,输入北京或上海后回车。第二种是带命令行参数直接查:
python weather.py 北京
python weather.py "New York"
输出效果类似:
北京 实时天气:
当前温度: 18°C
体感温度: 18°C
天气状况: 晴
相对湿度: 23%
风速: 11 km/h
未来三天预报:
2025-01-05: 晴, -4°C ~ 6°C
2025-01-06: 多云, -3°C ~ 7°C
2025-01-07: 阴, -2°C ~ 5°C
这里需要注意:如果城市名中有空格,比如“New York”,命令行传参时必须加引号,否则会被拆成两个参数。
如果希望在Linux/macOS终端直接输入别名调用,可以在shell配置中加:
alias tq='python3 ~/weather_cli/weather.py'
之后直接输入“tq 北京”即可查询。
四、功能扩展实践
扩展一:多城市连续查询。把命令行参数视为城市列表,逐个遍历即可实现。使用sys.argv切片后,如果没有参数就交互式输入。若城市名带空格,仍需引号包裹。
扩展二:做成HTTP接口给小程序或前端用。用Flask包一层即可:
from flask import Flask, jsonify, request
from weather import get_weather, format_current, format_forecast
app = Flask(__name__)
@app.route("/weather")
def weather():
city = request.args.get("city", "")
if not city:
return jsonify({"error": "city parameter is required"}), 400
data = get_weather(city)
if not data:
return jsonify({"error": "failed to fetch weather"}), 502
current = data["current_condition"]
return jsonify({
"city": city,
"current": format_current(current),
"forecast": format_forecast(data["weather"])
})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
这样前端请求/weather?city=北京就能拿到格式化结果。不过生产环境更合理的做法是直接返回temp_C、humidity等原始JSON字段,让前端自己渲染,而不是返回带换行的纯文本。
需要提醒的是,微信小程序前端使用WXML、WXSS和JavaScript,Python只适合做后端API和数据处理。支付等功能与Python脚本无关,需要单独申请商户资质。
五、常见问题排查
实践中有几个容易踩的坑。
第一,Windows控制台中文乱码。这不是代码问题,而是控制台编码不一致。可先执行chcp 65001切换到UTF-8代码页,或运行Python时加-X utf8参数:
python -X utf8 weather.py 北京
第二,城市名不存在时接口可能返回空数据或默认城市。建议解析前判断current_condition数组是否为空,为空时提示“城市不存在或无法解析”。
第三,频繁请求被限流。免费接口也需要合理请求频率,批量查询时每隔1秒请求一次,避免连续大量访问。
第四,内网SSL证书报错。公司网络拦截证书时,requests.get会抛SSLError。个人学习可临时加verify=False跳过校验,但要注意安全风险,生产环境禁止这么做。
第五,接口地址硬编码过多。建议把请求URL定义为文件顶部的常量,后续切换数据源或变更域名时只改一行。
排查网络相关问题时,建议先用curl直接请求接口验证:
curl "https://wttr.in/北京?format=j1&lang=zh"
如果curl能返回JSON说明接口与网络正常,问题在Python代码;如果curl也超时,需要先解决网络环境。这个方法能快速定位很多“代码没问题但跑不通”的诡异场景。
参考速查表:中文城市名报错或返回空,是URL未编码,用quote处理;中文乱码,是控制台编码问题,用chcp 65001或-X utf8;请求超时,是网络或限流,加timeout参数并控制请求间隔;城市查不到,是拼写或用词不标准,改用拼音、英文名或大城市试试;ModuleNotFoundError,是没有安装requests库,用pip install requests。
六、总结
这类项目对初学者的价值在于:字符串、字典、列表、函数、异常处理、第三方库和API调用全都能覆盖,而且正反馈非常明显。写完代码就能查到真实天气数据,比刷练习题更有成就感。wttr.in返回的数据还包括气压、能见度、云量、降雨概率等几十个字段,可以按需求继续扩展。比如在格式化函数中加入紫外线强度提示,或者把结果按城市保存到本地文件,都是不错的二次实践方向。
Re: Python天气查询脚本开发:wttr.in接口调用与命令行解析
楼主的分享很实在,wttr.in这个接口确实适合练手,免Key用起来方便很多。我之前用和风天气写过一次,申请Key和配额限制确实费了点功夫,对新手不太友好。 有个小建议:代码里 `requests.get` 的 `timeout=10` 对整个请求生效,但国内访问 wttr.in 偶尔会比较慢,要是想更健壮一点,可以考虑把超时参数拆成 `(connect, read)` 两个值。另外 `format_forecast` 里取了 `hourly`,有时候想看的其实是白天或者晚上的天气,感兴趣的话可以再研究下 `hourly` 里不同时间点的字段含义。 命令行版做完之后,确实可以考虑用 Flask 包一层 HTTP 接口,正好顺便把 JSON 序列化和 POST 请求也练了,整个学习路径很完整。感谢分享。Re: Python天气查询脚本开发:wttr.in接口调用与命令行解析
楼主这篇拆解很实在,刚好适合刚学完基础想找练手项目的朋友。wttr.in这个接口选得也挺好,免Key、支持中文,命令行版跑起来没什么门槛。 看到最后代码好像截断在 `current_condition` 赋值那块了,方便补完吗?另外有个小建议:异常处理里 `get_weather` 返回空字典时,`main` 里判断 `if not data` 就返回了,但如果请求成功但接口返回结构不符合预期(比如字段缺失),下面取 `current_condition` 还是有可能报 KeyError。可以加个简单的健壮性检查,或者用 `.get()` 链式取值,练手时把边界情况也覆盖到会更好。Re: Python天气查询脚本开发:wttr.in接口调用与命令行解析
感谢楼主分享,这篇实战记录对新手很友好。wttr.in确实是个不错的练手接口,免Key、返回格式多,中文支持也省心。 我照着思路也写过类似的小工具,有一点小经验补充:如果城市名带空格(比如“New York”),用quote处理时空格会编码成%20,实测wttr.in也能接受,但如果用“+”效果也一样。另外,如果某个城市查不到,接口可能返回空列表或者提示无法解析,代码里最好加个判断,不然取current_condition会直接索引报错。建议在get_weather里对data是否存在以及是否含关键字段做下校验,返回结构不符合预期时可以提示“请确认城市名是否正确”。 还有一个小细节,requests.get那里可以加上headers,伪装一个常见浏览器的User-Agent,偶尔接口会更稳定(个人经验,不一定每次都碰见)。 整体代码结构很清晰,先做命令行版再考虑包装Web服务,这个路径也很推荐。期待楼主后续如果做成Flask版,能再分享一篇。
页:
[1]