别再零散找号码︱078大乐透完整数据框架,主页合集,已整理往期复盘
最近在开发数据分析项目时,经常遇到需要处理彩票数据的场景。传统的手工收集方式效率低下且容易出错,特别是大乐透这种开奖频率高、数据量大的彩种。本文基于实际项目经验,整理了一套完整的大乐透数据分析框架,包含数据采集、存储、分析和可视化全流程,适合数据分析师、Python开发者和彩票研究者直接复用。
1. 大乐透数据分析的价值与应用场景
大乐透数据分析不仅限于预测号码,更多应用于概率统计教学、随机算法验证和数据分析实践。通过系统化分析历史开奖数据,我们可以:
- 验证随机性理论:检验开奖号码是否符合均匀分布规律
- 发现统计规律:分析冷热号、连号、奇偶比等指标的变化趋势
- 构建分析模型:为机器学习预测提供高质量的数据基础
- 教学演示:作为概率统计和数据分析的实战案例
在实际业务中,金融机构的风控模型测试、游戏公司的随机算法验证都可以借鉴这种数据分析方法。接下来我们将从数据源获取开始,逐步构建完整分析框架。
2. 环境准备与工具版本说明
本框架基于Python生态构建,以下是核心环境要求:
操作系统:Windows 10/11, macOS 10.15+, Ubuntu 18.04+Python版本:3.8+(推荐3.9+以获得更好的性能)核心依赖库:
- pandas 1.3+(数据处理与分析)
- requests 2.25+(网络请求)
- beautifulsoup4 4.9+(HTML解析)
- matplotlib 3.5+(数据可视化)
- jupyter 1.0+(交互式分析环境)
项目结构规划:
lottery-analysis/ ├── data/ # 数据存储目录 │ ├── raw/ # 原始数据 │ ├── processed/ # 处理后的数据 │ └── results/ # 分析结果 ├── src/ # 源代码 │ ├── crawler/ # 数据采集模块 │ ├── analysis/ # 分析模块 │ └── visualization/ # 可视化模块 ├── config/ # 配置文件 └── notebooks/ # Jupyter分析笔记3. 数据采集框架设计与实现
3.1 数据源选择与可靠性评估
大乐透数据采集需要选择官方或权威数据源,确保数据的准确性和完整性。推荐的数据源包括:
- 中国体育彩票官方网站:最权威的数据来源,更新及时
- 第三方彩票数据平台:提供API接口,获取更方便
- 历史数据归档:用于回溯测试和模型训练
在实际项目中,我们采用多源验证机制,确保数据的准确性。以下是基础采集类的实现:
# src/crawler/base_crawler.py import requests import pandas as pd from abc import ABC, abstractmethod import time from typing import List, Dict, Optional class BaseLotteryCrawler(ABC): """彩票数据采集基类""" def __init__(self, base_url: str, timeout: int = 10): self.base_url = base_url self.timeout = timeout self.session = requests.Session() self.session.headers.update({ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' }) @abstractmethod def get_latest_draw(self) -> Dict: """获取最新一期开奖数据""" pass @abstractmethod def get_history_data(self, start_period: str, end_period: str) -> pd.DataFrame: """获取历史期号数据""" pass def safe_request(self, url: str, params: Optional[Dict] = None) -> requests.Response: """安全的网络请求方法,包含异常处理""" try: response = self.session.get(url, params=params, timeout=self.timeout) response.raise_for_status() return response except requests.exceptions.RequestException as e: print(f"请求失败: {e}") raise3.2 大乐透专用采集器实现
基于基类,我们实现大乐透数据采集器,包含完整的异常处理和数据验证:
# src/crawler/dlt_crawler.py import re from bs4 import BeautifulSoup from .base_crawler import BaseLotteryCrawler class DLTCrawler(BaseLotteryCrawler): """大乐透数据采集器""" def __init__(self): super().__init__('http://www.lottery.gov.cn') self.lottery_type = 'dlt' def get_latest_draw(self) -> Dict: """获取最新一期大乐透开奖数据""" url = f"{self.base_url}/kjxx/dlt/" response = self.safe_request(url) soup = BeautifulSoup(response.content, 'html.parser') # 解析最新一期数据 latest_draw = self._parse_draw_data(soup) return latest_draw def get_history_data(self, start_period: str, end_period: str) -> pd.DataFrame: """获取历史数据(模拟实现,实际需要根据数据源调整)""" all_data = [] current_period = start_period while current_period <= end_period: try: draw_data = self._get_single_period(current_period) if draw_data: all_data.append(draw_data) time.sleep(1) # 礼貌爬取,避免给服务器造成压力 except Exception as e: print(f"获取期号 {current_period} 数据失败: {e}") # 期号递增逻辑(根据实际期号规则调整) current_period = self._next_period(current_period) return pd.DataFrame(all_data) def _parse_draw_data(self, soup: BeautifulSoup) -> Dict: """解析单期开奖数据的具体实现""" # 实际解析逻辑需要根据网页结构调整 draw_info = { 'period': '24078', # 示例期号 'draw_date': '2024-07-10', 'red_balls': ['01', '05', '15', '20', '25'], 'blue_balls': ['03', '08'], 'prize_pool': '8.5亿元', 'sales_amount': '3.2亿元' } return draw_info4. 数据存储与管理系统设计
4.1 数据库表结构设计
为了高效存储和查询大乐透数据,我们设计优化的数据库结构:
-- 创建大乐透基础数据表 CREATE TABLE dlt_draw_history ( id BIGINT AUTO_INCREMENT PRIMARY KEY, period VARCHAR(10) NOT NULL UNIQUE COMMENT '期号', draw_date DATE NOT NULL COMMENT '开奖日期', red_ball_1 TINYINT NOT NULL COMMENT '红球1', red_ball_2 TINYINT NOT NULL COMMENT '红球2', red_ball_3 TINYINT NOT NULL COMMENT '红球3', red_ball_4 TINYINT NOT NULL COMMENT '红球4', red_ball_5 TINYINT NOT NULL COMMENT '红球5', blue_ball_1 TINYINT NOT NULL COMMENT '蓝球1', blue_ball_2 TINYINT NOT NULL COMMENT '蓝球2', prize_pool DECIMAL(15,2) COMMENT '奖池金额(亿元)', sales_amount DECIMAL(15,2) COMMENT '销售额(亿元)', created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_period (period), INDEX idx_draw_date (draw_date), INDEX idx_red_balls (red_ball_1, red_ball_2, red_ball_3, red_ball_4, red_ball_5), INDEX idx_blue_balls (blue_ball_1, blue_ball_2) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='大乐透开奖历史数据'; -- 创建号码统计表 CREATE TABLE dlt_number_stats ( number TINYINT PRIMARY KEY COMMENT '号码(1-35为红球, 101-112为蓝球)', number_type ENUM('red', 'blue') NOT NULL COMMENT '号码类型', appear_count INT DEFAULT 0 COMMENT '出现次数', last_appear_period VARCHAR(10) COMMENT '最后出现期号', cold_hot_level TINYINT COMMENT '冷热程度(1-5星)', update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) COMMENT='大乐透号码出现统计';4.2 数据管理类实现
# src/data/database_manager.py import sqlite3 import pandas as pd from typing import List, Dict, Optional import logging class DLTDatabaseManager: """大乐透数据库管理类""" def __init__(self, db_path: str = 'data/lottery.db'): self.db_path = db_path self._init_database() def _init_database(self): """初始化数据库和表结构""" with sqlite3.connect(self.db_path) as conn: # 创建大乐透数据表(SQLite版本) conn.execute(''' CREATE TABLE IF NOT EXISTS dlt_draw_history ( id INTEGER PRIMARY KEY AUTOINCREMENT, period TEXT UNIQUE NOT NULL, draw_date TEXT NOT NULL, red_ball_1 INTEGER NOT NULL, red_ball_2 INTEGER NOT NULL, red_ball_3 INTEGER NOT NULL, red_ball_4 INTEGER NOT NULL, red_ball_5 INTEGER NOT NULL, blue_ball_1 INTEGER NOT NULL, blue_ball_2 INTEGER NOT NULL, prize_pool REAL, sales_amount REAL, created_time TEXT DEFAULT CURRENT_TIMESTAMP ) ''') # 创建索引提升查询性能 conn.execute('CREATE INDEX IF NOT EXISTS idx_dlt_period ON dlt_draw_history(period)') conn.execute('CREATE INDEX IF NOT EXISTS idx_dlt_date ON dlt_draw_history(draw_date)') def save_draw_data(self, draw_data: Dict) -> bool: """保存单期开奖数据""" try: with sqlite3.connect(self.db_path) as conn: conn.execute(''' INSERT OR REPLACE INTO dlt_draw_history (period, draw_date, red_ball_1, red_ball_2, red_ball_3, red_ball_4, red_ball_5, blue_ball_1, blue_ball_2, prize_pool, sales_amount) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ( draw_data['period'], draw_data['draw_date'], draw_data['red_balls'][0], draw_data['red_balls'][1], draw_data['red_balls'][2], draw_data['red_balls'][3], draw_data['red_balls'][4], draw_data['blue_balls'][0], draw_data['blue_balls'][1], draw_data.get('prize_pool'), draw_data.get('sales_amount') )) return True except Exception as e: logging.error(f"保存数据失败: {e}") return False def get_history_data(self, limit: int = 1000) -> pd.DataFrame: """获取历史数据""" with sqlite3.connect(self.db_path) as conn: df = pd.read_sql_query( f'SELECT * FROM dlt_draw_history ORDER BY period DESC LIMIT {limit}', conn ) return df5. 数据分析核心算法实现
5.1 基础统计分析方法
# src/analysis/basic_analysis.py import pandas as pd import numpy as np from typing import Dict, List, Tuple from collections import Counter class BasicDLTAnalysis: """大乐透基础统计分析""" def __init__(self, data: pd.DataFrame): self.data = data self.red_balls_columns = ['red_ball_1', 'red_ball_2', 'red_ball_3', 'red_ball_4', 'red_ball_5'] self.blue_balls_columns = ['blue_ball_1', 'blue_ball_2'] def get_number_frequency(self) -> Dict[str, pd.DataFrame]: """计算号码出现频率""" # 红球频率统计 red_balls = self.data[self.red_balls_columns].values.flatten() red_freq = pd.Series(red_balls).value_counts().sort_index() # 蓝球频率统计 blue_balls = self.data[self.blue_balls_columns].values.flatten() blue_freq = pd.Series(blue_balls).value_counts().sort_index() return { 'red_balls': red_freq, 'blue_balls': blue_freq } def get_cold_hot_numbers(self, window_size: int = 50) -> Dict: """分析冷热号(基于最近N期)""" recent_data = self.data.head(window_size) red_balls_recent = recent_data[self.red_balls_columns].values.flatten() blue_balls_recent = recent_data[self.blue_balls_columns].values.flatten() red_hot = pd.Series(red_balls_recent).value_counts().head(10) red_cold = pd.Series(red_balls_recent).value_counts().tail(10) return { 'hot_red': red_hot, 'cold_red': red_cold, 'hot_blue': pd.Series(blue_balls_recent).value_counts().head(5), 'cold_blue': pd.Series(blue_balls_recent).value_counts().tail(5) } def analyze_number_patterns(self) -> Dict: """分析号码模式:奇偶比、大小比、连号等""" results = {} # 奇偶比分析 red_balls_all = self.data[self.red_balls_columns].values odd_even_ratio = [] for draw in red_balls_all: odd_count = np.sum(draw % 2 == 1) even_count = 5 - odd_count odd_even_ratio.append(f"{odd_count}:{even_count}") results['odd_even_pattern'] = pd.Series(odd_even_ratio).value_counts() # 大小比分析(以17为界) size_ratio = [] for draw in red_balls_all: big_count = np.sum(draw > 17) small_count = 5 - big_count size_ratio.append(f"{big_count}:{small_count}") results['size_pattern'] = pd.Series(size_ratio).value_counts() # 连号分析 consecutive_counts = [] for draw in red_balls_all: sorted_draw = np.sort(draw) consecutive = np.sum(np.diff(sorted_draw) == 1) consecutive_counts.append(consecutive) results['consecutive_stats'] = pd.Series(consecutive_counts).value_counts() return results5.2 高级趋势分析方法
# src/analysis/trend_analysis.py import pandas as pd import numpy as np from sklearn.linear_model import LinearRegression from typing import Dict, List class TrendAnalysis: """大乐透趋势分析""" def __init__(self, data: pd.DataFrame): self.data = data.sort_values('period') def analyze_moving_average(self, number: int, window: int = 10) -> Dict: """分析单个号码的移动平均出现间隔""" appearances = [] for idx, row in self.data.iterrows(): red_balls = [row[f'red_ball_{i}'] for i in range(1, 6)] blue_balls = [row[f'blue_ball_{i}'] for i in range(1, 3)] if number in red_balls or number in blue_balls: appearances.append(row['period']) if len(appearances) < 2: return {'error': '数据不足进行移动平均分析'} # 计算出现间隔 intervals = [] for i in range(1, len(appearances)): interval = int(appearances[i-1]) - int(appearances[i]) intervals.append(interval) # 计算移动平均 intervals_series = pd.Series(intervals) moving_avg = intervals_series.rolling(window=min(window, len(intervals))).mean() return { 'appearances': appearances, 'intervals': intervals, 'moving_average': moving_avg.tolist(), 'current_interval': intervals[-1] if intervals else 0 } def predict_trend(self, numbers: List[int]) -> pd.DataFrame: """基于线性回归预测号码趋势""" results = [] for number in numbers: trend_data = self.analyze_moving_average(number) if 'error' in trend_data: continue # 使用简单线性回归预测 X = np.arange(len(trend_data['intervals'])).reshape(-1, 1) y = np.array(trend_data['intervals']) if len(y) > 1: model = LinearRegression() model.fit(X, y) next_interval = model.predict([[len(y)]])[0] results.append({ 'number': number, 'next_interval_pred': max(1, round(next_interval)), 'confidence': model.score(X, y), 'last_appearance': trend_data['appearances'][-1] }) return pd.DataFrame(results)6. 数据可视化与报表生成
6.1 基础可视化图表
# src/visualization/chart_generator.py import matplotlib.pyplot as plt import seaborn as sns import pandas as pd from typing import Dict, List import numpy as np class DLTChartGenerator: """大乐透图表生成器""" def __init__(self, style: str = 'seaborn'): plt.style.use(style) self.fig_size = (12, 8) def plot_number_frequency(self, freq_data: Dict, title: str = "大乐透号码出现频率") -> plt.Figure: """绘制号码频率分布图""" fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6)) # 红球频率图 red_freq = freq_data['red_balls'] ax1.bar(red_freq.index, red_freq.values, color='red', alpha=0.7) ax1.set_title('红球出现频率') ax1.set_xlabel('号码') ax1.set_ylabel('出现次数') # 蓝球频率图 blue_freq = freq_data['blue_balls'] ax2.bar(blue_freq.index, blue_freq.values, color='blue', alpha=0.7) ax2.set_title('蓝球出现频率') ax2.set_xlabel('号码') ax2.set_ylabel('出现次数') plt.tight_layout() return fig def plot_trend_analysis(self, trend_data: pd.DataFrame, numbers: List[int]) -> plt.Figure: """绘制号码趋势分析图""" fig, axes = plt.subplots(2, 2, figsize=(15, 10)) axes = axes.flatten() for i, number in enumerate(numbers[:4]): if i >= len(axes): break number_data = trend_data[trend_data['number'] == number] if not number_data.empty: ax = axes[i] # 简化趋势展示 ax.plot([1, 2, 3], [10, 15, 12], marker='o') # 示例数据 ax.set_title(f'号码 {number} 趋势分析') ax.set_xlabel('期数窗口') ax.set_ylabel('出现间隔') plt.tight_layout() return fig def generate_comprehensive_report(self, analysis_results: Dict) -> plt.Figure: """生成综合分析报告""" fig = plt.figure(figsize=(16, 12)) # 布局多个子图展示不同维度的分析结果 gs = fig.add_gridspec(3, 3) # 号码频率热力图 ax1 = fig.add_subplot(gs[0, :2]) self._plot_frequency_heatmap(ax1, analysis_results) # 奇偶比分布 ax2 = fig.add_subplot(gs[0, 2]) self._plot_odd_even_distribution(ax2, analysis_results) # 大小比分布 ax3 = fig.add_subplot(gs[1, 0]) self._plot_size_distribution(ax3, analysis_results) # 连号分析 ax4 = fig.add_subplot(gs[1, 1]) self._plot_consecutive_analysis(ax4, analysis_results) # 冷热号分析 ax5 = fig.add_subplot(gs[1, 2]) self._plot_cold_hot_analysis(ax5, analysis_results) # 趋势预测 ax6 = fig.add_subplot(gs[2, :]) self._plot_trend_prediction(ax6, analysis_results) plt.tight_layout() return fig def _plot_frequency_heatmap(self, ax, results): """绘制频率热力图(简化实现)""" # 实际实现需要具体数据 data = np.random.rand(35, 10) im = ax.imshow(data, cmap='YlOrRd', aspect='auto') ax.set_title('号码出现频率热力图') plt.colorbar(im, ax=ax)7. 完整实战案例:078期数据分析
7.1 数据准备与预处理
# notebooks/078期大乐透分析.ipynb import sys sys.path.append('../src') from crawler.dlt_crawler import DLTCrawler from data.database_manager import DLTDatabaseManager from analysis.basic_analysis import BasicDLTAnalysis from analysis.trend_analysis import TrendAnalysis from visualization.chart_generator import DLTChartGenerator # 初始化组件 crawler = DLTCrawler() db_manager = DLTDatabaseManager() chart_gen = DLTChartGenerator() # 获取最新数据 try: latest_data = crawler.get_latest_draw() print(f"最新期号: {latest_data['period']}") print(f"开奖号码: 红球{latest_data['red_balls']} 蓝球{latest_data['blue_balls']}") # 保存到数据库 db_manager.save_draw_data(latest_data) except Exception as e: print(f"数据获取失败: {e}") # 使用模拟数据进行演示 latest_data = { 'period': '24078', 'draw_date': '2024-07-10', 'red_balls': [5, 12, 18, 23, 30], 'blue_balls': [3, 8] } # 加载历史数据 history_data = db_manager.get_history_data(limit=200) print(f"加载历史数据 {len(history_data)} 期")7.2 078期专项分析
# 078期号码特征分析 def analyze_078_special_features(draw_data: Dict, history_data: pd.DataFrame) -> Dict: """分析078期特殊特征""" analysis = BasicDLTAnalysis(history_data) features = {} # 基础特征 red_balls = draw_data['red_balls'] blue_balls = draw_data['blue_balls'] features['period'] = draw_data['period'] features['red_balls'] = red_balls features['blue_balls'] = blue_balls # 奇偶比 odd_count = sum(1 for ball in red_balls if ball % 2 == 1) features['odd_even_ratio'] = f"{odd_count}:{5-odd_count}" # 大小比(以17为界) big_count = sum(1 for ball in red_balls if ball > 17) features['size_ratio'] = f"{big_count}:{5-big_count}" # 和值分析 features['sum_value'] = sum(red_balls) # 连号分析 sorted_red = sorted(red_balls) consecutive_pairs = [] for i in range(len(sorted_red)-1): if sorted_red[i+1] - sorted_red[i] == 1: consecutive_pairs.append((sorted_red[i], sorted_red[i+1])) features['consecutive_pairs'] = consecutive_pairs # 与历史模式对比 patterns = analysis.analyze_number_patterns() features['common_patterns'] = { 'odd_even': patterns['odd_even_pattern'].head(3).to_dict(), 'size_ratio': patterns['size_pattern'].head(3).to_dict() } return features # 执行分析 special_features = analyze_078_special_features(latest_data, history_data) print("078期特征分析结果:") for key, value in special_features.items(): print(f"{key}: {value}")7.3 生成分析报告
# 生成完整的078期分析报告 def generate_078_report(): """生成078期完整分析报告""" # 基础统计分析 basic_analysis = BasicDLTAnalysis(history_data) freq_analysis = basic_analysis.get_number_frequency() pattern_analysis = basic_analysis.analyze_number_patterns() cold_hot_analysis = basic_analysis.get_cold_hot_numbers(window_size=50) # 趋势分析 trend_analysis = TrendAnalysis(history_data) important_numbers = [5, 12, 18, 23, 30, 3, 8] # 078期号码 trend_prediction = trend_analysis.predict_trend(important_numbers) # 可视化报告 fig1 = chart_gen.plot_number_frequency(freq_analysis, "078期前后号码频率分布") fig1.savefig('../results/078期号码频率分析.png', dpi=300, bbox_inches='tight') # 综合报告 comprehensive_results = { 'frequency': freq_analysis, 'patterns': pattern_analysis, 'cold_hot': cold_hot_analysis, 'trends': trend_prediction, 'special_features': special_features } fig2 = chart_gen.generate_comprehensive_report(comprehensive_results) fig2.savefig('../results/078期综合分析报告.png', dpi=300, bbox_inches='tight') return comprehensive_results # 执行报告生成 report_results = generate_078_report() print("078期分析报告已生成,保存在 results/ 目录")8. 常见问题与解决方案
8.1 数据采集常见问题
问题1:网络请求失败或超时
- 现象:
requests.exceptions.ConnectionError或超时错误 - 原因:网络不稳定、目标网站反爬虫、请求频率过高
- 解决方案:
- 增加超时时间设置
- 添加重试机制
- 使用代理IP轮换
- 遵守robots.txt,控制请求频率
# 增强的网络请求实现 def robust_request(self, url: str, max_retries: int = 3) -> requests.Response: """带重试机制的稳健请求""" for attempt in range(max_retries): try: response = self.safe_request(url) return response except requests.exceptions.RequestException as e: if attempt == max_retries - 1: raise wait_time = 2 ** attempt # 指数退避 time.sleep(wait_time)问题2:网页结构变化导致解析失败
- 现象:
AttributeError或解析出的数据为空 - 原因:目标网站改版,HTML结构发生变化
- 解决方案:
- 定期检查解析逻辑
- 使用更稳定的CSS选择器
- 添加解析验证机制
- 维护多套解析方案
8.2 数据分析常见问题
问题3:数据质量不一致
- 现象:统计结果异常或计算错误
- 原因:数据源格式不统一、缺失值处理不当
- 解决方案:
- 实现数据验证清洗流程
- 添加数据质量检查点
- 使用Pandas数据清洗功能
def validate_draw_data(self, data: Dict) -> bool: """验证开奖数据完整性""" required_fields = ['period', 'draw_date', 'red_balls', 'blue_balls'] # 检查必需字段 for field in required_fields: if field not in data: return False # 验证号码范围 red_balls = data['red_balls'] blue_balls = data['blue_balls'] if len(red_balls) != 5 or any(not (1 <= ball <= 35) for ball in red_balls): return False if len(blue_balls) != 2 or any(not (1 <= ball <= 12) for ball in blue_balls): return False return True问题4:分析结果难以解释
- 现象:统计指标与实际情况不符
- 原因:分析方法不当、数据量不足、理解偏差
- 解决方案:
- 增加数据样本量
- 使用多种分析方法交叉验证
- 结合业务背景理解结果
9. 最佳实践与工程建议
9.1 代码质量与维护性
模块化设计:将数据采集、存储、分析、可视化分离为独立模块,便于维护和测试。
# 使用配置管理 # config/settings.py import os from dataclasses import dataclass @dataclass class DLTConfig: """大乐透分析配置""" db_path: str = os.getenv('DLT_DB_PATH', 'data/lottery.db') request_timeout: int = 10 max_retries: int = 3 analysis_window: int = 100 # 分析窗口期数 # 使用单例模式管理配置 class ConfigManager: _instance = None def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) cls._instance.config = DLTConfig() return cls._instance日志记录:完善的日志系统便于问题排查和监控。
# utils/logger.py import logging import os def setup_logger(name: str, log_file: str = None) -> logging.Logger: """设置日志记录器""" logger = logging.getLogger(name) logger.setLevel(logging.INFO) # 避免重复添加handler if not logger.handlers: formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) # 控制台输出 console_handler = logging.StreamHandler() console_handler.setFormatter(formatter) logger.addHandler(console_handler) # 文件输出 if log_file: os.makedirs(os.path.dirname(log_file), exist_ok=True) file_handler = logging.FileHandler(log_file) file_handler.setFormatter(formatter) logger.addHandler(file_handler) return logger9.2 性能优化建议
数据库优化:
- 为常用查询字段创建索引
- 定期清理和优化数据库
- 使用连接池管理数据库连接
计算优化:
- 对大规模数据使用Pandas向量化操作
- 缓存频繁使用的分析结果
- 使用生成器处理大数据集
# 使用缓存提升性能 from functools import lru_cache import hashlib @lru_cache(maxsize=100) def cached_analysis(data_hash: str, analysis_type: str): """带缓存的分析函数""" # 基于数据哈希和分析类型缓存结果 pass def get_data_hash(data: pd.DataFrame) -> str: """生成数据哈希用于缓存键""" return hashlib.md5(pd.util.hash_pandas_object(data).values).hexdigest()9.3 安全与合规性
数据使用合规:
- 仅使用公开可获取的数据
- 遵守网站的使用条款
- 不用于商业赌博用途
访问控制:
- 控制数据采集频率,避免对目标网站造成压力
- 实现友好的爬虫策略
- 尊重robots.txt规定
10. 扩展功能与进阶应用
10.1 机器学习预测模型
# src/ml/prediction_model.py from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split import pandas as pd import numpy as np class DLTPredictionModel: """大乐透预测模型(仅供学习研究)""" def __init__(self): self.models = {} def prepare_features(self, history_data: pd.DataFrame) -> pd.DataFrame: """准备机器学习特征""" features = [] for i in range(len(history_data) - 1): current = history_data.iloc[i] previous = history_data.iloc[i + 1] if i + 1 < len(history_data) else None feature_row = { 'period': current['period'], # 添加各种统计特征 'red_sum': sum([current[f'red_ball_{j}'] for j in range(1, 6)]), 'blue_sum': sum([current[f'blue_ball_{j}'] for j in range(1, 3)]), # 可以添加更多衍生特征 } features.append(feature_row) return pd.DataFrame(features) def train_models(self, features: pd.DataFrame, target_columns: List[str]): """训练预测模型""" for target in target_columns: X = features.drop(columns=['period']) y = features[target] # 需要定义合适的目标变量 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) model = RandomForestClassifier(n_estimators=100, random_state=42) model.fit(X_train, y_train) self.models[target] = model print(f"模型 {target} 训练完成,准确率: {model.score(X_test, y_test):.3f}")10.2 Web应用集成
# web_app/app.py (Flask示例) from flask import Flask, render_template, jsonify import pandas as pd from src.data.database_manager import DLTDatabaseManager from src.