# WorkBuddy 一个数据人的省钱日记:用 AI 搞定全国343个城市天气数据,省下几百块

📅 2026/7/11 3:25:11
# WorkBuddy 一个数据人的省钱日记:用 AI 搞定全国343个城市天气数据,省下几百块
一个数据人的省钱日记让 AI 帮我写代码零成本搞定全国343城天气数据背景工作需要全国地级市天气数据国内付费API动辄几百元。我让 WorkBuddyAI智能助手帮我写了一套Python脚本全程对话式搞定零成本拿到343城数据省下几百块。一、需求背景天气数据真不便宜最近工作需要用到全国地级市的天气数据主要关注温度、是否下雨这些维度时间范围 2024 年至今按日统计就行。听起来不复杂上手才发现天气数据在国内是真·付费玩家专属方案价格备注和风天气 API商用需付费按量计费343城 × 500天估算几百元起心知天气 API商用套餐起步价不低历史数据更贵聚合数据等按次收费历史数据贵批量拉不划算某宝代下50-200元不等质量参差不齐不一定靠谱对于个人或小团队来说花几百块买一次性数据确实肉疼。二、转机让 AI 帮我写代码作为一个数据人我日常用WorkBuddy一款AI智能助手既能做分析也能写代码处理各种工作。我把需求直接丢给了它“我需要最近两年全国地级市333个的天气使用 open-meteo主要关注温度、是否下雨等维度帮忙写Python脚本。”接下来就是纯对话式开发我只需要做决策代码全交给 WorkBuddy 生成我的需求WorkBuddy 的响应每个月每个城市一个CSV调整输出格式从2024年1月1日开始改起始日期每个城市一个CSV文件重构输出逻辑帮忙控制并发貌似被封IP了加429限流保护文件已生成的自动跳过加断点续传整个过程中我只需要在几个关键节点做选择其余的脚本编写、调试、限流处理全部由 WorkBuddy 完成。三、WorkBuddy 给出的方案Open-MeteoWorkBuddy 帮我调研后推荐了Open-Meteo—— 一个完全免费、无需 API Key 的天气数据服务基于 ECMWF ERA5 再分析数据。核心特点完全免费无需注册无需 API Key支持历史数据ERA5 再分析数据覆盖全球任意经纬度日级数据最高/最低温、降水、风速、湿度等十几个维度API 地址https://archive-api.open-meteo.com/v1/era5四、WorkBuddy 写的核心代码下面是 WorkBuddy 生成的完整脚本已精简核心逻辑关键设计点都在注释里importrequestsimportpandasaspdimporttimeimportjsonimportosimportrandomfromdatetimeimportdatetime# 配置区 START_DATE2024-01-01END_DATE2026-06-09OUTPUT_DIRweather_dataCITIES_FILEcities.csvREQUEST_DELAY3.0# 请求间隔秒MAX_RETRIES5# 最大重试次数RETRY_BASE_DELAY10.0# 429退避基础延迟秒# classWeatherDataDownloader:天气数据下载器WorkBuddy 生成def__init__(self,output_dirOUTPUT_DIR):self.output_diroutput_dir self.base_urlhttps://archive-api.open-meteo.com/v1/era5self.progress_fileos.path.join(output_dir,progress.json)self.progressself._load_progress()os.makedirs(output_dir,exist_okTrue)# 复用连接会话减少连接开销self.sessionrequests.Session()adapterrequests.adapters.HTTPAdapter(pool_connections1,pool_maxsize1,max_retries0)self.session.mount(http://,adapter)self.session.mount(https://,adapter)deffetch_weather_data(self,lat,lon,start_date,end_date): 获取单个城市天气数据带指数退避重试 params{latitude:lat,longitude:lon,start_date:start_date,end_date:end_date,daily:[temperature_2m_max,temperature_2m_min,temperature_2m_mean,precipitation_sum,rain_sum,snowfall_sum,weathercode,windspeed_10m_max,winddirection_10m_dominant,relative_humidity_2m_mean,pressure_msl_mean,et0_fao_evapotranspiration],timezone:Asia/Shanghai}last_errorNoneforattemptinrange(MAX_RETRIES1):try:responseself.session.get(self.base_url,paramsparams,timeout60)# 429 限流处理指数退避ifresponse.status_code429:waitmin(RETRY_BASE_DELAY*(2**attempt),300)print(f429限流等待{wait:.1f}s 后重试)time.sleep(wait)last_error429 Too Many Requestscontinueresponse.raise_for_status()dataresponse.json()dailydata[daily]dfpd.DataFrame({date:daily[time],temp_max:daily[temperature_2m_max],temp_min:daily[temperature_2m_min],temp_mean:daily[temperature_2m_mean],precipitation:daily[precipitation_sum],rain:daily[rain_sum],snowfall:daily[snowfall_sum],weathercode:daily[weathercode],windspeed_max:daily[windspeed_10m_max],wind_direction:daily[winddirection_10m_dominant],humidity_mean:daily[relative_humidity_2m_mean],pressure_mean:daily[pressure_msl_mean],evapotranspiration:daily[et0_fao_evapotranspiration]})# 衍生字段是否下雨、天气描述df[is_rainy]df[rain].apply(lambdax:x0ifxisnotNoneelseFalse)df[weather_desc]df[weathercode].apply(self._weathercode_to_desc)returndfexceptrequests.exceptions.RequestExceptionase:wait5*(attempt1)print(f网络错误{wait}s后重试:{e})time.sleep(wait)last_errorstr(e)ifattemptMAX_RETRIES:raiseraiseException(f重试{MAX_RETRIES}次后仍失败:{last_error})defdownload_city_data(self,city_info,start_date,end_date):下载单个城市数据已存在则自动跳过city_namecity_info[city]provincecity_info[province]latfloat(city_info[lat])lonfloat(city_info[lon])safe_citycity_name.replace(/,_).replace(\\,_)safe_provinceprovince.replace(/,_).replace(\\,_)output_fileos.path.join(self.output_dir,f{safe_city}_{safe_province}.csv)# 断点续传磁盘文件已存在就跳过ifos.path.exists(output_file):print(f[SKIP] 文件已存在:{os.path.basename(output_file)})returnTruetry:dfself.fetch_weather_data(lat,lon,start_date,end_date)df[city]city_name df[province]province df[lat]lat df[lon]lon df.to_csv(output_file,indexFalse,encodingutf-8-sig)city_keyf{city_name}_{province}self.progress[completed].append(city_key)self._save_progress()returnTrueexceptExceptionase:self.progress[failed].append({city:city_name,error:str(e)})self._save_progress()returnFalsefinally:# 3秒基础 0~2秒随机抖动delayREQUEST_DELAYrandom.uniform(0,2.0)time.sleep(delay)WorkBuddy 设计的三大关键点1. 断点续传磁盘文件优先ifos.path.exists(output_file):returnTrue# 文件在就不重复下载即使progress.json被删只要 CSV 在就不会重复请求。2. 429 限流保护指数退避第1次失败 → 等 10s 第2次失败 → 等 20s 第3次失败 → 等 40s ...最多300s3. 请求节奏控制每个城市间隔 3s 0~2s 随机抖动每 10 个城市额外休息 15s连续失败 3 次自动暂停 60s五、最终成果数据规模指标数值城市数量343 个时间范围2024-01-01 ~ 2026-06-09数据粒度日级字段数量20 个含衍生字段总记录数约 30 万行合并文件大小36 MB字段说明字段说明单位date日期YYYY-MM-DDtemp_max日最高温度°Ctemp_min日最低温度°Ctemp_mean日平均温度°Cprecipitation总降水量mmrain降雨量mmsnowfall降雪量cmweathercodeWMO天气代码-windspeed_max最大风速m/swind_direction主导风向°humidity_mean平均相对湿度%pressure_mean平均海平面气压hPaevapotranspiration参考蒸散发mmis_rainy是否下雨True/Falseweather_desc天气描述中文wind_direction_desc风向描述中文city城市名-province省份-lat纬度-lon经度-输出文件结构weather_data/ ├── 七台河_黑龙江省.csv # 每个城市一个文件 ├── 三亚_海南省.csv ├── 上海_上海市.csv ├── 北京_北京市.csv ├── ... ├── all_cities.csv # 所有城市合并30万行36MB ├── progress.json # 断点续传进度 └── weather_download.log # 运行日志数据示例date,temp_max,temp_min,temp_mean,precipitation,rain,is_rainy,weather_desc,city,province 2024-01-01,-8.6,-24.5,-15.4,0.0,0.0,False,阴天,七台河,黑龙江省 2024-01-02,-9.1,-19.4,-13.2,0.0,0.0,False,阴天,七台河,黑龙江省 2024-01-04,-4.7,-13.7,-9.0,0.3,0.0,False,小雪,七台河,黑龙江省六、成本对比方案成本数据维度历史数据和风天气商业API几百元起丰富付费心知天气商业版几百元起丰富付费某宝代下50-200元看卖家不稳定WorkBuddy Open-Meteo0 元13维度支持实际节省几百元。而且数据完全自主可控需要更新时重跑脚本即可没有任何边际成本。七、踩坑记录都是 WorkBuddy 帮我填的坑1429 Too Many Requests一开始设的 1 秒间隔跑了约 50 个城市就被 Open-Meteo 限流了。WorkBuddy 的修复间隔从 1s 提到 3s加随机抖动429 时指数退避重试。坑2断点续传不可靠脚本异常退出时progress.json可能没及时写入。WorkBuddy 的修复优先检查磁盘文件是否存在不依赖progress.json。坑3Windows CMD 编码问题bat 文件里写了中文提示CMD 报不是内部或外部命令。WorkBuddy 的修复bat 文件全用英文中文交给 Python 的 logging。八、总结维度评价WorkBuddy对话式开发我只做决策代码全包Open-Meteo API免费、稳定、数据质量好ERA5再分析数据数据覆盖全国343城日级20个字段够用开发成本0 行手写代码全程对话经济成本0 元适用场景个人分析、小团队、非商用一句话总结能用 AI 搞定的事绝不花冤枉钱。数据来源Open-Meteohttps://open-meteo.com/基于 ECMWF ERA5 再分析数据仅供个人学习和非商业用途。代码由 WorkBuddy AI 智能助手生成经实际运行验证。