
1. 项目概述与核心价值天气预报数据采集是Python爬虫学习的经典实战案例。2345天气网作为国内主流气象服务平台其数据更新及时、覆盖城市全面非常适合作为爬虫教学的目标网站。这个项目不仅能帮助初学者掌握基础的网页数据抓取技术更能学习到如何处理动态加载内容、解析复杂页面结构等实用技能。相比简单的静态页面抓取天气预报数据采集涉及以下几个技术难点动态参数生成、AJAX请求分析、数据包捕获与解析。通过这个案例你将系统性地掌握从网页分析到数据存储的完整爬虫开发流程。我在实际工作中发现天气预报类爬虫项目的技术栈可以复用到电商价格监控、舆情监测等更多实际场景中。2. 环境准备与工具选型2.1 基础环境配置推荐使用Python 3.8版本进行开发主要依赖库包括requests2.26用于发送HTTP请求BeautifulSoup44.10HTML解析库pandas1.3数据清洗与存储selenium4.1处理动态加载内容安装命令pip install requests beautifulsoup4 pandas selenium对于浏览器自动化工具建议搭配ChromeDriver使用。需要注意ChromeDriver版本必须与本地安装的Chrome浏览器版本严格匹配这是新手最容易出错的环节之一。2.2 开发工具推荐Chrome开发者工具F12用于分析网络请求、查看页面元素Postman接口测试工具验证API请求参数Fiddler/Charles抓包工具辅助分析复杂请求PyCharm/VSCode代码编写与调试提示Windows用户建议将chromedriver.exe放在Python安装目录下的Scripts文件夹中可以避免路径配置问题。3. 网站分析与请求构造3.1 页面结构解析打开2345天气网www.weather.com.cn通过检查元素可以发现城市选择采用下拉菜单动态加载天气数据通过AJAX异步请求获取每个城市有唯一的citycode标识主要数据接口格式为/weather/[citycode].shtml关键发现直接访问网页源代码并不能获取完整的天气数据必须分析XHR请求才能找到真实的数据接口。3.2 抓包分析与接口定位使用Chrome开发者工具的Network面板筛选XHR请求后可以发现城市列表接口/data/city3jdata/china.html天气数据接口/data/sk/[citycode].html请求头需要包含Referer字段部分接口有签名验证sign参数通过多次请求分析我们发现citycode的生成规则是省份编号城市编号区县编号。例如北京朝阳区的citycode是101010300。3.3 请求参数逆向对于有反爬机制的接口需要特别关注以下参数timestamp当前时间戳signMD5加密字符串token会话标识通过JavaScript调试可以发现sign参数是通过拼接固定字符串时间戳后取MD5生成的。Python实现代码如下import hashlib import time def generate_sign(): timestamp str(int(time.time())) raw_str fsecret_key_{timestamp} return hashlib.md5(raw_str.encode()).hexdigest()4. 爬虫核心代码实现4.1 城市列表获取首先需要获取完整的城市编码列表def get_city_codes(): base_url http://www.weather.com.cn/data/city3jdata/china.html headers { Referer: http://www.weather.com.cn/, User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) } response requests.get(base_url, headersheaders) provinces response.json() city_codes [] for prov_code in provinces: city_url fhttp://www.weather.com.cn/data/city3jdata/provshi/{prov_code}.html cities requests.get(city_url, headersheaders).json() for city_code in cities: area_url fhttp://www.weather.com.cn/data/city3jdata/station/{prov_code}{city_code}.html areas requests.get(area_url, headersheaders).json() for area_code in areas: full_code f{prov_code}{city_code}{area_code} city_codes.append(full_code) return city_codes4.2 天气数据抓取获取到城市编码后就可以抓取详细的天气数据def get_weather_data(city_code): api_url fhttp://www.weather.com.cn/data/sk/{city_code}.html headers { Referer: fhttp://www.weather.com.cn/weather1d/{city_code}.shtml, User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) } try: response requests.get(api_url, headersheaders) data response.json()[weatherinfo] return { city: data[city], temp: data[temp], wind: data[WD] data[WS], humidity: data[SD], update_time: data[time] } except Exception as e: print(fError fetching data for {city_code}: {str(e)}) return None4.3 反反爬策略实现针对网站可能的反爬机制我们需要实现以下防护措施请求头完善headers { Accept: application/json, text/javascript, */*; q0.01, Accept-Encoding: gzip, deflate, Accept-Language: zh-CN,zh;q0.9, Connection: keep-alive, Host: www.weather.com.cn, Referer: http://www.weather.com.cn/, X-Requested-With: XMLHttpRequest, User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36 }IP代理池集成def get_proxy(): # 这里替换为你的代理获取接口 proxy_url http://your-proxy-pool.com/get try: return requests.get(proxy_url).text except: return None def make_request(url, headers): proxy get_proxy() if proxy: proxies { http: fhttp://{proxy}, https: fhttp://{proxy} } return requests.get(url, headersheaders, proxiesproxies, timeout10) else: return requests.get(url, headersheaders, timeout10)请求频率控制import random import time def random_delay(): time.sleep(random.uniform(1, 3))5. 数据存储与处理5.1 数据清洗获取到的原始数据需要进行清洗def clean_data(raw_data): # 温度处理 temp raw_data.get(temp, N/A) if temp ! N/A: temp float(temp.replace(℃, )) # 风力等级转换 wind_map { 微风: 1-2级, 和风: 3-4级, 劲风: 5-6级 } wind raw_data.get(wind, N/A) wind wind_map.get(wind, wind) return { city: raw_data.get(city, N/A), temperature: temp, wind_level: wind, humidity: raw_data.get(humidity, N/A), update_time: raw_data.get(update_time, N/A) }5.2 存储方案选择根据数据量大小可以选择不同的存储方式小规模数据1万条# CSV存储 import pandas as pd def save_to_csv(data_list, filename): df pd.DataFrame(data_list) df.to_csv(filename, indexFalse, encodingutf_8_sig)中等规模数据1万-100万条# MySQL存储 import pymysql def save_to_mysql(data_list): conn pymysql.connect( hostlocalhost, userroot, passwordyourpassword, databaseweather ) with conn.cursor() as cursor: sql INSERT INTO weather_data (city, temperature, wind_level, humidity, update_time) VALUES (%s, %s, %s, %s, %s) cursor.executemany(sql, data_list) conn.commit() conn.close()大规模数据100万条# MongoDB存储 from pymongo import MongoClient def save_to_mongodb(data_list): client MongoClient(mongodb://localhost:27017/) db client[weather_db] collection db[weather_data] collection.insert_many(data_list) client.close()6. 常见问题与解决方案6.1 请求返回403错误可能原因及解决方案请求头不完整补全Referer、User-Agent等字段IP被封禁使用代理IP或降低请求频率签名验证失败检查sign参数生成逻辑6.2 数据解析异常典型错误处理try: data response.json() weather_info data[weatherinfo] except ValueError: print(Invalid JSON response) except KeyError: print(Missing weatherinfo field in response)6.3 动态加载内容抓取失败对于无法通过API获取的数据可以使用Seleniumfrom selenium import webdriver from selenium.webdriver.chrome.options import Options def get_dynamic_content(url): chrome_options Options() chrome_options.add_argument(--headless) driver webdriver.Chrome(optionschrome_options) driver.get(url) # 等待动态内容加载 time.sleep(2) # 获取页面源码 html driver.page_source driver.quit() return html6.4 数据更新不及时解决方案设置定时任务如每天6点、12点、18点各采集一次监控数据变化当发现更新时立即抓取实现增量采集只获取变化的数据7. 项目优化与扩展7.1 性能优化方案多线程采集from concurrent.futures import ThreadPoolExecutor def multi_thread_crawl(city_codes, workers5): with ThreadPoolExecutor(max_workersworkers) as executor: results list(executor.map(get_weather_data, city_codes)) return [r for r in results if r is not None]异步IO实现import aiohttp import asyncio async def async_fetch(session, url): async with session.get(url) as response: return await response.json() async def main(city_codes): async with aiohttp.ClientSession() as session: tasks [] for code in city_codes: url fhttp://www.weather.com.cn/data/sk/{code}.html tasks.append(async_fetch(session, url)) return await asyncio.gather(*tasks)7.2 功能扩展方向历史数据归档定期存储天气数据建立历史数据库天气预警监控实时监测极端天气预警信息可视化展示使用Pyecharts等库生成天气数据图表天气预报推送通过邮件/微信定时发送天气预报7.3 企业级应用建议对于商业用途的天气数据采集建议使用分布式爬虫框架Scrapy、Scrapy-Redis搭建稳定的代理IP池实现自动化监控与告警系统设计合理的数据存储架构遵守《网络安全法》相关规定8. 完整代码示例以下是整合了所有功能的完整实现import requests import json import time import random import pandas as pd from bs4 import BeautifulSoup from concurrent.futures import ThreadPoolExecutor class WeatherSpider: def __init__(self): self.base_url http://www.weather.com.cn self.headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), Referer: self.base_url } def get_city_codes(self): 获取所有城市编码 prov_url f{self.base_url}/data/city3jdata/china.html provinces requests.get(prov_url, headersself.headers).json() city_codes [] for prov_code in provinces: city_url f{self.base_url}/data/city3jdata/provshi/{prov_code}.html cities requests.get(city_url, headersself.headers).json() for city_code in cities: area_url f{self.base_url}/data/city3jdata/station/{prov_code}{city_code}.html areas requests.get(area_url, headersself.headers).json() for area_code in areas: full_code f{prov_code}{city_code}{area_code} city_codes.append(full_code) return city_codes def get_weather_data(self, city_code): 获取单个城市天气数据 api_url f{self.base_url}/data/sk/{city_code}.html try: response requests.get(api_url, headersself.headers) data response.json()[weatherinfo] return { city: data[city], temp: data[temp], wind: data[WD] data[WS], humidity: data[SD], update_time: data[time] } except Exception as e: print(fError fetching {city_code}: {str(e)}) return None def run(self, output_fileweather_data.csv): 主运行方法 print(开始获取城市列表...) city_codes self.get_city_codes() print(f共获取到{len(city_codes)}个城市编码) print(开始采集天气数据...) with ThreadPoolExecutor(max_workers5) as executor: results list(executor.map(self.get_weather_data, city_codes)) valid_data [r for r in results if r is not None] print(f成功获取{len(valid_data)}个城市的数据) df pd.DataFrame(valid_data) df.to_csv(output_file, indexFalse, encodingutf_8_sig) print(f数据已保存至{output_file}) if __name__ __main__: spider WeatherSpider() spider.run()重要提示实际运行时建议添加适当的延迟如time.sleep(random.uniform(1,3))以避免给目标服务器造成过大压力。商业用途请务必遵守网站的使用条款。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。