Python进阶实践:从命令行工具到数据爬取的完整开发指南

一、天气查询命令行工具开发(基础篇)

1.1 项目目标与架构设计

本项目的核心目标是构建一个可通过命令行交互的天气查询系统,用户输入城市名称即可获取实时温度、湿度等气象数据。系统采用分层架构设计:

  • 数据层:集成第三方气象API
  • 业务层:实现城市坐标转换与天气数据解析
  • 交互层:提供友好的命令行参数解析与错误处理

1.2 核心功能实现

1.2.1 气象数据获取

推荐使用无需API Key的Open-Meteo服务,其RESTful接口设计简洁:

  1. import requests
  2. def fetch_weather_data(lat, lon):
  3. """获取指定坐标的实时天气数据"""
  4. base_url = "https://api.open-meteo.com/v1/forecast"
  5. params = {
  6. "latitude": lat,
  7. "longitude": lon,
  8. "current_weather": True,
  9. "timezone": "auto"
  10. }
  11. try:
  12. response = requests.get(base_url, params=params, timeout=5)
  13. response.raise_for_status()
  14. return response.json()
  15. except requests.exceptions.RequestException as e:
  16. print(f"网络请求失败: {str(e)}")
  17. return None

1.2.2 城市坐标转换

采用三级缓存策略优化性能:

  1. 内存缓存:内置高频查询城市坐标
  2. 文件缓存:持久化存储扩展坐标库
  3. API查询:集成地理编码服务(如Nominatim)
  1. # 基础坐标库
  2. CITY_COORDINATES = {
  3. "Beijing": (39.9042, 116.4074),
  4. "Shanghai": (31.2304, 121.4737),
  5. "Guangzhou": (23.1291, 113.2644)
  6. }
  7. def get_coordinates(city_name):
  8. """获取城市坐标(支持缓存扩展)"""
  9. # 1. 检查内存缓存
  10. if city_name in CITY_COORDINATES:
  11. return CITY_COORDINATES[city_name]
  12. # 2. 实际项目中可添加文件缓存检查
  13. # with open('city_coords.json') as f:
  14. # coords_db = json.load(f)
  15. # 3. 调用地理编码API(示例伪代码)
  16. # geocode_url = "https://nominatim.openstreetmap.org/search"
  17. # ...
  18. raise ValueError(f"未找到城市坐标: {city_name}")

1.2.3 命令行交互设计

使用argparse模块构建专业级CLI:

  1. import argparse
  2. def main():
  3. parser = argparse.ArgumentParser(
  4. description="实时天气查询工具",
  5. epilog="示例: python weather.py Beijing"
  6. )
  7. parser.add_argument("city", help="要查询的城市名称")
  8. parser.add_argument("-v", "--verbose", action="store_true", help="显示详细信息")
  9. args = parser.parse_args()
  10. try:
  11. lat, lon = get_coordinates(args.city)
  12. weather_data = fetch_weather_data(lat, lon)
  13. if weather_data and args.verbose:
  14. print(f"""
  15. 城市: {args.city}
  16. 温度: {weather_data['current_weather']['temperature']}°C
  17. 湿度: {weather_data['current_weather']['humidity']}%
  18. 风速: {weather_data['current_weather']['windspeed']} km/h
  19. """)
  20. elif weather_data:
  21. print(f"{args.city}: {weather_data['current_weather']['temperature']}°C")
  22. except Exception as e:
  23. print(f"查询失败: {str(e)}")
  24. if __name__ == "__main__":
  25. main()

1.3 性能优化建议

  1. 异步请求:使用aiohttp实现并发查询
  2. 数据缓存:添加Redis缓存层(TTL设为10分钟)
  3. 错误重试:实现指数退避重试机制
  4. 日志系统:集成logging模块记录运行状态

二、豆瓣电影数据爬取与分析(进阶篇)

2.1 项目架构设计

本系统采用典型的爬虫-存储-分析架构:

  1. 数据采集层 数据清洗层 存储层 分析层
  2. requests BeautifulSoup pandas matplotlib

2.2 核心模块实现

2.2.1 智能反爬策略

  1. import requests
  2. from fake_useragent import UserAgent
  3. import time
  4. import random
  5. class AntiScrapingClient:
  6. def __init__(self):
  7. self.ua = UserAgent()
  8. self.session = requests.Session()
  9. def get(self, url):
  10. headers = {
  11. "User-Agent": self.ua.random,
  12. "Accept-Language": "zh-CN,zh;q=0.9",
  13. "Referer": "https://www.baidu.com"
  14. }
  15. time.sleep(random.uniform(0.5, 1.5)) # 随机延迟
  16. try:
  17. response = self.session.get(url, headers=headers, timeout=10)
  18. if response.status_code == 429:
  19. raise Exception("请求过于频繁")
  20. return response
  21. except requests.exceptions.RequestException as e:
  22. print(f"请求异常: {str(e)}")
  23. return None

2.2.2 数据解析模块

使用CSS选择器精准定位元素:

  1. from bs4 import BeautifulSoup
  2. def parse_movie_page(html):
  3. soup = BeautifulSoup(html, 'lxml')
  4. movies = []
  5. for item in soup.select('.item'):
  6. try:
  7. title = item.select_one('.title').text.strip()
  8. rating = float(item.select_one('.rating_num').text)
  9. year = item.select_one('.bd p').text.split('\n')[1].strip().split('/')[0].strip()
  10. # 处理导演信息(示例)
  11. director_info = item.select_one('.bd p').text.split('\n')[0].strip()
  12. directors = [d.strip() for d in director_info.split('导演:')[-1].split('主演:')[0].split('/')] if '导演:' in director_info else []
  13. movies.append({
  14. 'title': title,
  15. 'rating': rating,
  16. 'year': year,
  17. 'directors': directors
  18. })
  19. except Exception as e:
  20. print(f"解析错误: {str(e)}")
  21. continue
  22. return movies

2.2.3 数据存储方案

推荐使用pandas的DataFrame结构:

  1. import pandas as pd
  2. def save_to_csv(movies, filename='douban_top250.csv'):
  3. df = pd.DataFrame(movies)
  4. # 数据清洗
  5. df['year'] = pd.to_numeric(df['year'], errors='coerce')
  6. df = df.dropna(subset=['year'])
  7. # 保存文件
  8. df.to_csv(filename, index=False, encoding='utf_8_sig')
  9. print(f"数据已保存至 {filename}")

2.3 数据分析与可视化

2.3.1 基础统计分析

  1. def analyze_movies(filename):
  2. df = pd.read_csv(filename)
  3. # 基础统计
  4. stats = {
  5. '总数量': len(df),
  6. '平均分': round(df['rating'].mean(), 2),
  7. '最高分': df['rating'].max(),
  8. '最低分': df['rating'].min(),
  9. '年代跨度': f"{df['year'].min()}-{df['year'].max()}"
  10. }
  11. # 导演作品统计
  12. director_counts = df['directors'].explode().value_counts().head(10)
  13. return {
  14. 'basic_stats': stats,
  15. 'top_directors': director_counts.to_dict()
  16. }

2.3.2 可视化实现

使用matplotlib生成专业图表:

  1. import matplotlib.pyplot as plt
  2. def visualize_data(df):
  3. plt.figure(figsize=(12, 6))
  4. # 年代分布
  5. plt.subplot(1, 2, 1)
  6. year_counts = df['year'].value_counts().sort_index()
  7. year_counts.plot(kind='bar', color='skyblue')
  8. plt.title('电影年代分布')
  9. plt.xlabel('年份')
  10. plt.ylabel('数量')
  11. # 评分分布
  12. plt.subplot(1, 2, 2)
  13. df['rating'].plot(kind='hist', bins=10, edgecolor='black', color='salmon')
  14. plt.title('评分分布')
  15. plt.xlabel('评分')
  16. plt.ylabel('数量')
  17. plt.tight_layout()
  18. plt.savefig('movie_analysis.png')
  19. plt.close()

2.4 高级扩展建议

  1. 分布式爬取:使用Scrapy-Redis实现分布式任务队列
  2. 增量更新:记录已爬取URL实现增量更新
  3. 异常监控:集成Sentry进行错误监控
  4. 数据持久化:将清洗后的数据存入关系型数据库

三、开发最佳实践总结

3.1 代码组织规范

  1. project/
  2. ├── config/ # 配置文件
  3. └── settings.py
  4. ├── core/ # 核心逻辑
  5. ├── api_client.py
  6. ├── parser.py
  7. └── analyzer.py
  8. ├── utils/ # 工具函数
  9. ├── cache.py
  10. └── logger.py
  11. ├── tests/ # 单元测试
  12. └── test_parser.py
  13. └── main.py # 入口文件

3.2 性能优化策略

  1. 连接池管理:使用requests.Session复用TCP连接
  2. 批量操作:数据库操作尽量使用批量插入
  3. 内存优化:大数据处理使用生成器而非列表
  4. 并行计算:对独立任务使用多进程/多线程

3.3 安全考虑要点

  1. 敏感信息:API密钥等使用环境变量存储
  2. 输入验证:对所有用户输入进行严格校验
  3. 速率限制:遵守目标网站的robots.txt规定
  4. 异常处理:区分预期异常和意外异常

通过这两个项目的完整实现,开发者可以系统掌握Python在数据采集、处理和分析领域的核心技能。建议在实际开发中结合具体业务需求进行模块化改造,逐步构建可复用的技术组件库。