这是一篇基于实际项目经验的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"][0]["value"] if "lang_zh" in current else current["weatherDesc"][0]["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"][0]
- desc = hourly["lang_zh"][0]["value"] if "lang_zh" in hourly else hourly["weatherDesc"][0]["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[1:])
- else:
- city = input("请输入城市名: ").strip()
- if not city:
- print("城市名不能为空")
- return
- data = get_weather(city)
- if not data:
- return
- current = data["current_condition"][0]
- 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 "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"][0]
- 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返回的数据还包括气压、能见度、云量、降雨概率等几十个字段,可以按需求继续扩展。比如在格式化函数中加入紫外线强度提示,或者把结果按城市保存到本地文件,都是不错的二次实践方向。 |