用Python构建旅游城市天气数据库从爬取到存储的完整实战指南旅行规划中最让人头疼的莫过于天气的不确定性。去年夏天我计划去青岛度假结果因为没查清楚当地雨季规律整整一周都在酒店看雨。这个教训让我意识到精准的历史天气数据对旅行者有多重要。本文将手把手教你用Python打造一个旅游城市天气数据库涵盖从网页抓取到MySQL存储的全流程特别适合需要为旅行网站或数据分析项目准备基础数据的开发者。1. 环境准备与目标拆解工欲善其事必先利其器。在开始编码前我们需要配置好开发环境并明确技术路线。不同于简单的Demo项目实际天气数据采集需要考虑反爬策略、数据一致性和存储效率等问题。基础环境配置pip install requests beautifulsoup4 pymysql pandas核心工具选型建议请求库requests基础但高效若遇到复杂反爬可升级到selenium解析库BeautifulSoup适合HTML结构规整的页面XPath更灵活但学习曲线略陡数据库MySQL 8.0支持JSON字段和窗口函数等现代特性目标网站分析要点以典型天气网站为例首页入口是否包含城市列表历史数据页面的URL规律通常包含城市ID和日期参数页面渲染方式服务端渲染还是AJAX加载反爬机制验证码、请求频率限制等2. 智能爬虫设计与反爬策略2.1 请求头伪装与IP轮换现代网站基本都会检测异常的User-Agent和请求频率。这是我整理的真实浏览器头集合headers_pool [ {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...}, {User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...}, {User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X)...} ] def get_random_header(): return random.choice(headers_pool)IP代理实战技巧免费代理可用性差建议使用按量付费的商业服务设置超时和重试机制proxies { http: http://proxy_ip:port, https: https://proxy_ip:port } try: response requests.get(url, headersheaders, proxiesproxies, timeout10) except requests.exceptions.Timeout: print(fTimeout occurred for {url})2.2 页面解析的鲁棒性设计不同城市的页面结构可能有细微差异解析代码需要足够健壮。以BeautifulSoup为例def parse_weather(html): soup BeautifulSoup(html, lxml) # 使用CSS选择器配合异常处理 try: date soup.select(.history-day)[0].get(data-date) high_temp soup.select(.high-temp)[0].text.replace(°C, ) weather soup.select(.weather-icon)[0].get(title) except IndexError: print(Element not found, check selector) return None return { date: date, high_temp: high_temp, weather: weather }XPath的灵活应用# 当class名称动态变化时特别有用 tree etree.HTML(html) wind_direction tree.xpath(//div[contains(class, wind)]/text())[0]3. 数据存储优化方案3.1 MySQL表结构设计考虑到天气数据的时序特性推荐以下表结构CREATE TABLE city_weather ( id INT AUTO_INCREMENT PRIMARY KEY, city_name VARCHAR(20) NOT NULL, record_date DATE NOT NULL, max_temp DECIMAL(3,1), min_temp DECIMAL(3,1), weather_condition VARCHAR(20), wind_direction VARCHAR(10), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY idx_city_date (city_name, record_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;批量插入的优化技巧def batch_insert(data_list): sql INSERT IGNORE INTO city_weather (city_name, record_date, max_temp, min_temp, weather_condition) VALUES (%s, %s, %s, %s, %s) with connection.cursor() as cursor: cursor.executemany(sql, data_list) connection.commit()3.2 异常处理与数据校验建立数据质量检查机制def validate_weather(data): rules { max_temp: lambda x: -30 float(x) 50, min_temp: lambda x: -30 float(x) 50, date: lambda x: bool(re.match(r\d{4}-\d{2}-\d{2}, x)) } for field, validator in rules.items(): if not validator(data.get(field, )): raise ValueError(fInvalid {field}: {data[field]})4. 完整项目架构与调度4.1 模块化设计建议的项目结构weather_project/ ├── crawler/ │ ├── __init__.py │ ├── downloader.py # 下载逻辑 │ └── parser.py # 解析逻辑 ├── storage/ │ ├── mysql_handler.py │ └── data_validator.py └── scheduler.py # 任务调度4.2 定时任务配置使用APScheduler实现自动化采集from apscheduler.schedulers.blocking import BlockingScheduler def job(): cities [青岛, 开封, 苏州, 扬州, 烟台] for city in cities: crawl_city_weather(city) scheduler BlockingScheduler() scheduler.add_job(job, cron, hour2) # 每天凌晨2点执行 scheduler.start()速率限制建议每个请求间隔2-5秒单个城市每天采集不超过24次使用time.sleep(random.uniform(1, 3))模拟人工操作5. 数据应用与扩展采集到的数据可以用于旅行最佳时间推荐算法城市气候特征分析异常天气预警模型Pandas分析示例import pandas as pd df pd.read_sql(SELECT * FROM city_weather, conconnection) qingdao_summer df[(df[city_name]青岛) (df[record_date].dt.month.isin([6,7,8]))] print(qingdao_summer.groupby(weather_condition).size().sort_values())对于需要更高性能的场景可以考虑使用PostgreSQL的TimescaleDB扩展处理时序数据添加Redis缓存热门城市查询采用异步IOaiohttpasyncio提升采集效率