资讯动态

Python命令行天气查询工具实战:requests+argparse构建可复用工程骨架

发布时间:2026/10/23 1:41:24 来源:尧图企业网站定制
简介这份资源是一个用 Python 编写的天气查询小程序面向刚接触网络请求与 API 交互的编程初学者以及想通过小项目巩固 Python 基础的开发者可用于查询指定地区的实时天气信息。压缩包共 2 个文件以 1 个 py 脚本和 1 个 db 数据库文件为主脚本承载天气查询的核心逻辑数据库文件用于存放城市索引等基础数据整体约 31KB体量轻便、便于直接运行与阅读。目前已有 463 人学习下载具备一定的参考热度。程序围绕 requests 与 json 两个核心库展开演示了如何向天气 API 发送 GET 请求、解析返回的 JSON 数据并提取温度、湿度、风速等信息同时涉及 sys.argv 命令行参数处理、API 密钥传递以及输入校验与请求异常处理等细节读者可据此理解 Python 与在线服务集成的完整链路并在此基础上扩展天气图标显示等功能。1. 一个命令行天气查询小工具为什么值得拆上周同事在群里问「明天要不要带伞」我顺手在终端敲了条命令三秒后气温、湿度、未来两天降水概率全出来了。他以为我装了什么插件其实就是一个用 Python 写的天气查询小程序——输入城市名调公开天气接口把 JSON 解析成人类能读的几行字。这类项目看着简单但麻雀虽小五脏俱全HTTP 请求、异常处理、数据解析、命令行参数、缓存策略一个完整的小型工程该有的它都有。适合刚学完 Python 语法想找个练手项目的新手也适合想快速搭个内部小工具的老手。下面我按「能干什么 → 怎么跑 → 怎么改」的顺序把这个项目的骨架和关键实现拆开讲。2. 项目定位与技术选型为什么是 requests argparse 这套组合2.1 这个项目到底解决什么问题天气查询是个典型的「输入极简、输出结构化」场景。用户只需要给一个城市名程序负责拼请求、发 HTTP、解析响应、格式化输出。它不涉及数据库、不涉及前端渲染、不涉及用户系统所以技术栈可以压到最薄。项目正文没给具体依赖清单但按这个场景的常见做法核心依赖就两个requests负责 HTTP 通信argparse负责命令行参数解析。前者比urllib少写一半样板代码后者是标准库自带、零安装成本。如果你想让输出带颜色可以加colorama想缓存查询结果标准库的json 文件读写就够了不必上 Redis。这个项目的价值不在于「查天气」本身——手机上一划就有——而在于它是一个可复现的最小工程模板。你把它跑通之后换掉接口地址和解析逻辑就能变成汇率查询、快递追踪、股票行情骨架完全复用。2.2 技术栈拆解与选型理由层次选型理由网络请求requests同步阻塞模型足够API 简单异常类型清晰参数解析argparse标准库支持位置参数和可选参数自带 help数据解析json标准库天气接口返回 JSON无需额外依赖输出格式化字符串格式化 可选 colorama终端可读性优先缓存本地 JSON 文件 时间戳避免频繁请求触发接口限流选requests而不是aiohttp是因为天气查询是低频操作一个人一天查不了几次异步带来的复杂度远大于收益。选argparse而不是click是因为这个项目的参数就一个城市名加几个开关标准库完全够用少一个第三方依赖就少一个安装失败的坑。这种「够用就好」的选型思路在小工具类项目里比堆技术栈更值得学。2.3 目录结构长什么样项目正文没给目录树但按这类小程序的常见组织方式我一般会拆成下面这样既不过度设计也不把所有逻辑塞一个文件weather-cli/ ├── main.py # 入口参数解析与流程编排 ├── weather/ │ ├── __init__.py │ ├── api.py # 接口请求与响应处理 │ ├── parser.py # JSON 字段提取与格式化 │ └── cache.py # 本地缓存读写 ├── config.py # 接口地址、超时、缓存过期时间 └── requirements.txtmain.py只做三件事解析参数、调api拿数据、调parser输出。业务逻辑全在weather/包里这样你以后想换接口只动api.py想改输出样式只动parser.py。新手容易把所有代码写进一个main.py跑是能跑但改起来就是牵一发动全身。3. 跑起来从零到第一条天气输出的完整步骤3.1 环境准备与依赖安装先确认 Python 版本。这个项目用到的语法在 3.7 以上都没问题建议直接用 3.8。虚拟环境是血泪经验——我见过太多人把依赖装进系统 Python后来另一个项目要不同版本的requests直接翻车。所以第一步永远是建虚拟环境# 创建并激活虚拟环境Windows 用 venv\Scripts\activate python -m venv venv source venv/bin/activate # 安装依赖 pip install requests如果你要用颜色输出再加一句pip install colorama。requirements.txt里就写这两行版本号可以不锁死这类小工具对版本不敏感。装完之后用pip list确认requests在列表里不在的话检查是不是虚拟环境没激活——这是新手最高频的翻车点终端前面没有(venv)前缀就说明没进去。3.2 接口配置与第一个请求天气数据来自公开接口项目里通常把地址和密钥放在config.py。不同接口的字段名不一样下面用常见的「当前天气」结构举例你拿到自己的接口文档后对照改字段名即可# config.py API_URL https://api.example-weather.com/v1/current API_KEY your_api_key_here # 替换成自己申请的 key TIMEOUT 8 # 请求超时秒数别设太长 CACHE_TTL 600 # 缓存有效期单位秒# weather/api.py import requests from config import API_URL, API_KEY, TIMEOUT def fetch_weather(city: str) - dict: 请求天气接口返回原始 JSON 字典。 params { city: city, key: API_KEY, lang: zh, # 中文返回 } try: resp requests.get(API_URL, paramsparams, timeoutTIMEOUT) resp.raise_for_status() # 4xx/5xx 直接抛异常 return resp.json() except requests.Timeout: raise RuntimeError(f请求超时{TIMEOUT}s检查网络或调大 TIMEOUT) except requests.HTTPError as e: raise RuntimeError(f接口返回错误{e.response.status_code})这段代码有三个关键点。第一params字典交给requests拼查询串不要自己手拼 URL中文城市名手拼容易漏编码。第二timeout必须显式设置不设的话请求可能挂几十秒用户体验极差。第三raise_for_status()把 HTTP 错误转成异常配合try/except给出人话提示而不是让用户看到一坨 traceback。参数说明city是位置参数传中文或拼音看接口支持lang控制返回语言有些接口用language字段名以文档为准。3.3 解析响应并格式化输出拿到 JSON 之后不能直接print那出来的是一坨嵌套字典。parser.py的职责是把字段提出来拼成终端里一眼能读的几行# weather/parser.py def format_weather(raw: dict) - str: 把接口原始 JSON 转成可读文本。字段名按实际接口调整。 try: cur raw[data][current] city raw[data][city] temp cur[temp] humidity cur[humidity] desc cur[weather] wind cur.get(wind_speed, 未知) except KeyError as e: raise RuntimeError(f接口字段缺失{e}检查接口版本是否变更) lines [ f城市{city}, f天气{desc}, f气温{temp}°C, f湿度{humidity}%, f风速{wind} m/s, ] return \n.join(lines)try/except KeyError是这里的保险丝。天气接口偶尔会改字段名或某些城市缺字段没有这层保护用户看到的就是KeyError: humidity这种黑匣子报错。cur.get(wind_speed, 未知)用get给可选字段兜底必填字段才用[]直接取。输出用\n.join()而不是多次print方便以后加颜色或写日志时整体处理。3.4 入口编排与命令行参数main.py把上面几块串起来argparse负责接住用户输入# main.py import argparse from weather.api import fetch_weather from weather.parser import format_weather from weather.cache import load_cache, save_cache def main(): parser argparse.ArgumentParser(description命令行天气查询) parser.add_argument(city, help城市名如 北京) parser.add_argument(--no-cache, actionstore_true, help跳过缓存强制请求) args parser.parse_args() if not args.no_cache: cached load_cache(args.city) if cached: print(format_weather(cached)) return raw fetch_weather(args.city) save_cache(args.city, raw) print(format_weather(raw)) if __name__ __main__: main()跑起来就是python main.py 北京加--no-cache强制走网络。argparse的actionstore_true表示这个开关出现即为真不出现为假适合布尔型选项。if __name__ __main__这层判断别省否则这个文件被别的模块 import 时会直接执行查询逻辑。到这里一个能跑的最小闭环就完成了参数进、请求发、JSON 解析、文本出。4. 核心模块拆解缓存、异常与请求链路的关键实现4.1 缓存模块省的不只是流量天气数据十分钟内基本不变每次查询都打接口既慢又容易触发限流。缓存模块用本地 JSON 文件存「城市 → 数据 时间戳」读的时候比对时间差# weather/cache.py import json, os, time from config import CACHE_TTL CACHE_DIR os.path.join(os.path.expanduser(~), .weather_cache) def _path(city: str) - str: os.makedirs(CACHE_DIR, exist_okTrue) return os.path.join(CACHE_DIR, f{city}.json) def load_cache(city: str): 命中且未过期返回数据否则返回 None。 p _path(city) if not os.path.exists(p): return None try: with open(p, r, encodingutf-8) as f: obj json.load(f) if time.time() - obj[ts] CACHE_TTL: return obj[data] except (json.JSONDecodeError, KeyError): pass # 缓存损坏就当没有不阻断主流程 return None def save_cache(city: str, data: dict): with open(_path(city), w, encodingutf-8) as f: json.dump({ts: time.time(), data: data}, f, ensure_asciiFalse)几个细节值得说。缓存目录放在用户主目录下的隐藏文件夹而不是项目目录这样换工作目录也能命中。ensure_asciiFalse让中文城市名在文件里正常显示方便调试时直接打开看。load_cache里捕获JSONDecodeError和KeyError后静默返回None——缓存是加速手段坏了就退化成直接请求绝不能因为缓存文件损坏让整个程序崩掉。CACHE_TTL设 600 秒是个折中太短等于没缓存太长天气变了你还看旧数据。4.2 异常分层让报错说人话这个项目最容易写得糙的地方就是异常处理。接口超时、城市不存在、密钥失效、返回字段变更四种情况用户看到的提示应该完全不同。我的做法是在api.py里把底层异常统一转成RuntimeError并带上人话main.py只兜一层# main.py 中的调用段 try: raw fetch_weather(args.city) except RuntimeError as e: print(f查询失败{e}) return这样终端里出现的是「查询失败请求超时8s检查网络或调大 TIMEOUT」而不是二十行 traceback。注意不要用裸except:把所有异常吞掉那样连代码 bug 都被掩盖了调试时你会怀疑人生。分层原则是底层知道「发生了什么」上层决定「怎么告诉用户」中间用自定义异常或RuntimeError传递。4.3 请求链路全景一次查询经过哪些环节把一次python main.py 上海的完整链路串起来看argparse解析出city上海和no_cacheFalse→load_cache查本地文件未命中 →fetch_weather拼参数、发 GET、8 秒超时、raise_for_status校验 →resp.json()转字典 →save_cache写文件带时间戳 →format_weather提取字段拼文本 →print输出。整条链路里任何一个环节失败都有对应的异常出口不会让程序卡死或输出乱码。这个「入口薄、模块清、异常有出口」的结构就是这类小工具能长期维护的关键。5. 避坑与排查五个真实会遇到的坑5.1 中文城市名请求返回空结果现象传「北京」接口返回空数组或 400。原因部分接口要求 URL 编码或拼音requests的params会自动编码但如果接口本身只认拼音中文就查不到。解决先看接口文档确认参数格式需要拼音就加一层映射字典或者用urllib.parse.quote手动处理后再拼。排查时把resp.url打出来看实际请求地址长什么样比猜快得多。5.2 密钥写死在代码里被提交现象代码推到公开仓库后密钥泄露接口被刷爆。原因API_KEY直接写在config.py里跟着代码走。解决改成读环境变量os.environ.get(WEATHER_KEY)本地用.env文件加载.env加进.gitignore。这个坑我踩过一次从那以后凡是带 key 的项目第一步就是先写.gitignore。5.3 缓存导致「天气不更新」现象明明外面下雨程序还显示晴。原因CACHE_TTL设太大或者save_cache的时间戳用了错误的时间源。解决先手动删掉~/.weather_cache下的文件验证是不是缓存问题是的话调小CACHE_TTL并确认time.time()和文件里的ts是同一时区同一单位都是秒级 Unix 时间戳。加--no-cache开关就是给这种情况留的后悔药。5.4 接口字段变更导致 KeyError现象昨天还能跑今天报KeyError: temp。原因接口升级换了字段名或者某些城市返回结构不一致。解决parser.py里对必填字段的KeyError捕获后打印原始 JSON 的前几百字符对照接口文档改映射。长期做法是把字段映射抽成一个常量字典改的时候只动一处。5.5 虚拟环境没激活导致模块找不到现象ModuleNotFoundError: No module named requests但明明pip install过。原因装进了系统 Python 或另一个虚拟环境。解决看终端提示符有没有(venv)没有就重新source venv/bin/activate再用which python和which pip确认两者指向同一环境。这个坑新手一天能踩三次养成「先看前缀再敲命令」的习惯就好。6. 二次开发换接口、加功能与验证方法拿到这个骨架之后最值钱的改造方向是「换数据源」。把config.py的API_URL和api.py的params换掉再把parser.py的字段映射对着新接口文档改一遍一个汇率查询或快递追踪工具就出来了main.py和cache.py一行不用动。这就是前面强调「入口薄、模块清」的回报。第二个方向是加「多城市对比」。argparse的city改成nargs接收多个城市main.py里循环调用fetch_weather输出用表格对齐。注意循环里每个城市单独 try一个失败不能拖垮整批parser.add_argument(cities, nargs, help一个或多个城市名) # 调用侧 for city in args.cities: try: print(format_weather(fetch_weather(city))) except RuntimeError as e: print(f{city} 查询失败{e})第三个方向是输出结构化。加一个--json开关命中时直接json.dumps(raw, ensure_asciiFalse, indent2)方便管道给其他脚本消费。验证方法很简单改完先跑python main.py 北京 --no-cache确认网络链路通再跑python main.py 北京确认缓存命中最后故意传个不存在的城市看报错是否友好。三步都过改造就算稳了。我自己的习惯是每次改完parser.py都拿一份保存下来的真实响应 JSON 做离线测试不依赖网络这样字段映射改错了立刻能发现不用等接口抽风。从那以后我每次动解析逻辑都强制走一遍离线样本省了大量「线上才发现字段对不上」的时间。希望这个骨架能帮你少走几步弯路把它改成自己顺手的小工具。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑