268-基于Django的热门游戏榜单数据分析系统
摘要 本项目开发了一个基于Django的Steam游戏数据分析推荐系统,集成了数据爬取、情感分析和智能推荐功能。系统采用前后端分离架构,后端使用Django REST Framework构建API,前端采用Bootstrap+ECharts实现响应式可视化界面。核心功能包括游戏数据管理、评论情感分析、个性化推荐算法等,通过数据挖掘技术为玩家和开发者提供市场洞察。系统特色包括实时数据更新、AI驱动的
·
基于Django的Steam热门游戏数据可视化分析推荐系统
探索游戏世界的数据奥秘,用技术洞察玩家喜好
📋 目录
🎯 项目概述
本项目是一个基于Django框架开发的Steam游戏数据分析系统,旨在通过数据挖掘和可视化技术,为游戏开发者和玩家提供深度的游戏市场洞察。系统集成了数据爬取、情感分析、推荐算法等多项技术,打造了一个功能完整的游戏数据分析平台。
项目特色
- 🎮 全面数据覆盖:涵盖Steam平台热门游戏的多维度数据
- 📊 智能可视化:基于ECharts的交互式图表展示
- 🤖 AI驱动分析:集成情感分析和智能推荐算法
- 🎨 现代化UI:采用玻璃拟态设计的响应式界面
- 🔄 实时数据:支持数据实时更新和爬取管理
🛠 技术栈
后端技术
- Django 4.x - Python Web框架
- Django REST Framework - API开发
- PostgreSQL/MySQL - 数据库
- Celery - 异步任务处理
- Redis - 缓存和消息队列
前端技术
- HTML5 + CSS3 - 页面结构和样式
- JavaScript ES6+ - 交互逻辑
- Bootstrap 5 - 响应式框架
- ECharts - 数据可视化
- jQuery - DOM操作
数据科学
- Pandas - 数据处理
- NumPy - 数值计算
- Scikit-learn - 机器学习
- jieba - 中文分词
- TextBlob - 情感分析
部署运维
- Docker - 容器化部署
- Nginx - 反向代理
- Gunicorn - WSGI服务器
- Supervisor - 进程管理
🏗 系统架构
项目演示
🚀 核心功能
1. 数据管理模块
游戏数据管理
# models.py
class App(models.Model):
app_id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=255)
developer = models.CharField(max_length=255)
publisher = models.CharField(max_length=255)
categories = models.TextField()
genres = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2)
positive_reviews = models.IntegerField(default=0)
negative_reviews = models.IntegerField(default=0)
achievements_count = models.IntegerField(default=0)
release_date = models.DateField()
create_time = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'app'
verbose_name = '游戏应用'
verbose_name_plural = '游戏应用'
评论数据管理
class Comment(models.Model):
id = models.AutoField(primary_key=True)
app = models.ForeignKey(App, on_delete=models.CASCADE)
content = models.TextField()
sentiment_score = models.FloatField(default=0.0)
sentiment_label = models.CharField(max_length=20, default='neutral')
create_time = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'comment'
verbose_name = '游戏评论'
verbose_name_plural = '游戏评论'
2. 数据分析模块
类别分析
# views.py
def category_analysis(request):
"""游戏类别分析"""
categories = App.objects.values('categories').annotate(
count=Count('id'),
avg_price=Avg('price'),
avg_rating=Avg('positive_reviews')
).order_by('-count')[:10]
# 数据处理和可视化
chart_data = {
'categories': [item['categories'] for item in categories],
'counts': [item['count'] for item in categories],
'prices': [float(item['avg_price']) for item in categories]
}
return render(request, 'category_analysis.html', {
'chart_data': chart_data
})
情感分析
# sentiment_analysis.py
import jieba
from textblob import TextBlob
import re
class SentimentAnalyzer:
def __init__(self):
# 加载情感词典
self.positive_words = self.load_words('positive.txt')
self.negative_words = self.load_words('negative.txt')
def analyze_sentiment(self, text):
"""分析文本情感"""
# 中文分词
words = jieba.cut(text)
# 情感评分计算
positive_score = sum(1 for word in words if word in self.positive_words)
negative_score = sum(1 for word in words if word in self.negative_words)
# 综合评分
total_score = positive_score - negative_score
if total_score > 0:
return 'positive', total_score
elif total_score < 0:
return 'negative', total_score
else:
return 'neutral', 0
3. 推荐算法模块
协同过滤推荐
# recommendation.py
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
class GameRecommendation:
def __init__(self):
self.user_item_matrix = None
self.item_similarity = None
def build_user_item_matrix(self, user_favorites):
"""构建用户-物品矩阵"""
# 实现用户-物品矩阵构建逻辑
pass
def calculate_item_similarity(self):
"""计算物品相似度"""
if self.user_item_matrix is not None:
self.item_similarity = cosine_similarity(self.user_item_matrix.T)
def recommend_games(self, user_id, top_n=10):
"""为用户推荐游戏"""
# 基于协同过滤的推荐算法
user_vector = self.user_item_matrix[user_id]
scores = np.dot(user_vector, self.item_similarity)
# 返回推荐结果
recommended_indices = np.argsort(scores)[::-1][:top_n]
return recommended_indices
🗄 数据库设计
核心表结构
-- 游戏应用表
CREATE TABLE app (
app_id INTEGER PRIMARY KEY,
name VARCHAR(255) NOT NULL,
developer VARCHAR(255),
publisher VARCHAR(255),
categories TEXT,
genres TEXT,
price DECIMAL(10,2),
positive_reviews INTEGER DEFAULT 0,
negative_reviews INTEGER DEFAULT 0,
achievements_count INTEGER DEFAULT 0,
release_date DATE,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 用户表
CREATE TABLE user (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
email VARCHAR(100),
avatar VARCHAR(255),
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 评论表
CREATE TABLE comment (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
app_id INTEGER,
user_id INTEGER,
content TEXT NOT NULL,
sentiment_score FLOAT DEFAULT 0.0,
sentiment_label VARCHAR(20) DEFAULT 'neutral',
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (app_id) REFERENCES app(app_id),
FOREIGN KEY (user_id) REFERENCES user(id)
);
-- 用户收藏表
CREATE TABLE user_favorite (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
user_id INTEGER,
app_id INTEGER,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES user(id),
FOREIGN KEY (app_id) REFERENCES app(app_id),
UNIQUE KEY unique_favorite (user_id, app_id)
);
🎨 前端设计
现代化UI设计
登录注册页面
<!-- templates/login.html -->
<div class="auth-form">
<div class="auth-header">
<h1 class="auth-title">欢迎回来</h1>
<p class="auth-subtitle">登录您的账户以继续使用</p>
</div>
<form method="post" action="{% url 'login' %}" class="needs-validation" novalidate>
{% csrf_token %}
<div class="form-group">
<label for="username" class="form-label">用户名</label>
<div class="input-group">
<i class="bi bi-person input-icon"></i>
<input type="text"
name="username"
id="username"
class="form-control"
placeholder="请输入您的用户名"
required>
</div>
<div class="invalid-feedback">请输入您的用户名</div>
</div>
<div class="form-group">
<label for="password" class="form-label">密码</label>
<div class="input-group">
<i class="bi bi-lock input-icon"></i>
<input type="password"
name="password"
id="password"
class="form-control"
placeholder="请输入您的密码"
required>
<button type="button" class="password-toggle" onclick="togglePassword('password')">
<i class="bi bi-eye" id="password-eye"></i>
</button>
</div>
<div class="invalid-feedback">请输入您的密码</div>
</div>
<div class="form-group">
<button type="submit" class="btn-auth">
<span>登录</span>
</button>
</div>
</form>
<div class="auth-link">
<p>还没有账号? <a href="{% url 'register' %}">立即注册</a></p>
</div>
</div>
现代化CSS样式
/* static/css/auth.css */
:root {
--primary-color: #1b2838;
--secondary-color: #66c0f4;
--accent-color: #c7d5e0;
--text-primary: #ffffff;
--text-secondary: #b9b9b9;
--background-dark: #171a21;
--background-light: #2a475e;
--border-radius: 12px;
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
body.auth-body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, var(--background-dark) 0%, var(--background-light) 100%);
color: var(--text-primary);
min-height: 100vh;
overflow-x: hidden;
position: relative;
}
.auth-form {
background: rgba(255, 255, 255, 0.05);
padding: 40px;
border-radius: var(--border-radius);
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
box-shadow: var(--shadow-medium);
}
.btn-auth {
width: 100%;
padding: 15px;
background: linear-gradient(135deg, var(--secondary-color) 0%, var(--accent-color) 100%);
border: none;
border-radius: 8px;
color: var(--primary-color);
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
transition: var(--transition);
position: relative;
overflow: hidden;
}
.btn-auth:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-medium);
}
响应式设计
/* 响应式断点 */
@media (max-width: 1200px) {
.content-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
.stats-grid {
grid-template-columns: 1fr;
gap: 1rem;
}
.recommend-item {
padding: 0.75rem;
margin-bottom: 0.75rem;
}
.game-thumbnail {
width: 80px;
height: 60px;
margin-right: 0.75rem;
}
}
🔧 后端实现
Django项目结构
steam_analysis_system/
├── manage.py
├── requirements.txt
├── 基于Django系统/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── wsgi.py
│ └── asgi.py
├── app/
│ ├── models.py
│ ├── views.py
│ ├── urls.py
│ ├── admin.py
│ ├── apps.py
│ ├── templates/
│ │ ├── base.html
│ │ ├── home.html
│ │ ├── login.html
│ │ └── register.html
│ ├── static/
│ │ ├── css/
│ │ │ ├── style.css
│ │ │ ├── auth.css
│ │ │ └── modern-system.css
│ │ ├── js/
│ │ │ └── auth.js
│ │ └── img/
│ └── utils/
│ ├── errorResponse.py
│ └── getChangeSelfInfoData.py
├── middleware/
│ └── auth.py
├── spider/
│ ├── 1.py
│ └── steam_topsellers_details.csv
└── templates/
├── base_login.html
├── login.html
└── register.html
核心视图函数
# views.py
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
from .models import App, Comment, User
from .utils.errorResponse import errorResponse
import json
@login_required
def home(request):
"""首页视图"""
# 获取基础统计数据
basic_stats = {
'total_games': App.objects.count(),
'total_comments': Comment.objects.count(),
'free_games': App.objects.filter(price=0).count(),
'paid_games': App.objects.filter(price__gt=0).count(),
}
# 获取热门游戏
top_games = App.objects.order_by('-positive_reviews')[:5]
# 获取最新游戏
recent_games = App.objects.order_by('-create_time')[:5]
return render(request, 'home.html', {
'basic_stats': basic_stats,
'top_games': top_games,
'recent_games': recent_games,
'active_menu': 'home'
})
@require_http_methods(["GET", "POST"])
def login(request):
"""用户登录"""
if request.method == 'GET':
return render(request, 'login.html')
elif request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
try:
user = User.objects.get(username=username, password=password)
request.session['username'] = username
return redirect('/app/home')
except User.DoesNotExist:
return errorResponse.errorResponse(request, '用户名或密码错误')
@require_http_methods(["GET", "POST"])
def register(request):
"""用户注册"""
if request.method == 'GET':
return render(request, 'register.html')
elif request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
confirm_password = request.POST.get('confirmPassword')
# 验证输入
if not username or not password or not confirm_password:
return errorResponse.errorResponse(request, '不允许为空值')
if password != confirm_password:
return errorResponse.errorResponse(request, '两次密码不一致')
try:
User.objects.get(username=username)
return errorResponse.errorResponse(request, '该账号已存在')
except User.DoesNotExist:
User.objects.create(username=username, password=password)
return redirect('/app/login')
API接口设计
# API视图
@csrf_exempt
@require_http_methods(["POST"])
def analyze_sentiment_api(request):
"""情感分析API"""
try:
data = json.loads(request.body)
text = data.get('text', '')
# 调用情感分析服务
analyzer = SentimentAnalyzer()
sentiment, score = analyzer.analyze_sentiment(text)
return JsonResponse({
'success': True,
'sentiment': sentiment,
'score': score
})
except Exception as e:
return JsonResponse({
'success': False,
'error': str(e)
})
@csrf_exempt
@require_http_methods(["POST"])
def toggle_favorite(request):
"""切换收藏状态API"""
try:
data = json.loads(request.body)
app_id = data.get('app_id')
user_id = request.session.get('user_id')
# 实现收藏逻辑
favorite, created = UserFavorite.objects.get_or_create(
user_id=user_id,
app_id=app_id
)
if not created:
favorite.delete()
is_favorited = False
else:
is_favorited = True
return JsonResponse({
'success': True,
'is_favorited': is_favorited
})
except Exception as e:
return JsonResponse({
'success': False,
'error': str(e)
})
📊 数据可视化
ECharts图表集成
// 类别分析图表
function initCategoryChart(data) {
const chart = echarts.init(document.getElementById('categoryChart'));
const option = {
title: {
text: '游戏类别分布',
left: 'center',
textStyle: {
color: '#ffffff'
}
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left',
textStyle: {
color: '#ffffff'
}
},
series: [{
name: '游戏类别',
type: 'pie',
radius: '50%',
data: data.categories.map((item, index) => ({
value: data.counts[index],
name: item
})),
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}]
};
chart.setOption(option);
}
// 价格分析图表
function initPriceChart(data) {
const chart = echarts.init(document.getElementById('priceChart'));
const option = {
title: {
text: '游戏价格分布',
left: 'center',
textStyle: {
color: '#ffffff'
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
xAxis: {
type: 'category',
data: data.price_ranges,
axisLabel: {
color: '#ffffff'
}
},
yAxis: {
type: 'value',
axisLabel: {
color: '#ffffff'
}
},
series: [{
name: '游戏数量',
type: 'bar',
data: data.counts,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#66c0f4' },
{ offset: 1, color: '#c7d5e0' }
])
}
}]
};
chart.setOption(option);
}
词云可视化
// 词云图表
function initWordCloud(data) {
const chart = echarts.init(document.getElementById('wordCloudChart'));
const option = {
title: {
text: '游戏评论词云',
left: 'center',
textStyle: {
color: '#ffffff'
}
},
tooltip: {},
series: [{
type: 'wordCloud',
gridSize: 2,
sizeRange: [12, 50],
rotationRange: [-90, 90],
shape: 'pentagon',
width: '100%',
height: '100%',
textStyle: {
fontFamily: 'sans-serif',
fontWeight: 'bold',
color: function () {
return 'rgb(' + [
Math.round(Math.random() * 255),
Math.round(Math.random() * 255),
Math.round(Math.random() * 255)
].join(',') + ')';
}
},
data: data.words
}]
};
chart.setOption(option);
}
🚀 部署指南
Docker部署
# Dockerfile
FROM python:3.9-slim
WORKDIR /app
# 安装系统依赖
RUN apt-get update && apt-get install -y \
gcc \
postgresql-client \
&& rm -rf /var/lib/apt/lists/*
# 安装Python依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制项目文件
COPY . .
# 收集静态文件
RUN python manage.py collectstatic --noinput
# 暴露端口
EXPOSE 8000
# 启动命令
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "基于Django系统.wsgi:application"]
# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "8000:8000"
volumes:
- .:/app
depends_on:
- db
- redis
environment:
- DEBUG=False
- DATABASE_URL=postgresql://user:password@db:5432/steam_analysis
db:
image: postgres:13
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=steam_analysis
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
redis:
image: redis:6-alpine
ports:
- "6379:6379"
volumes:
postgres_data:
Nginx配置
# nginx.conf
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /static/ {
alias /path/to/your/static/files/;
expires 30d;
add_header Cache-Control "public, immutable";
}
location /media/ {
alias /path/to/your/media/files/;
expires 30d;
add_header Cache-Control "public, immutable";
}
}
✨ 项目亮点
1. 现代化UI设计
- 采用玻璃拟态设计风格
- 响应式布局适配各种设备
- 流畅的动画过渡效果
- 统一的视觉设计语言
2. 智能数据分析
- 多维度数据统计
- 实时情感分析
- 智能推荐算法
- 交互式数据可视化
3. 高性能架构
- 异步任务处理
- 缓存优化
- 数据库查询优化
- 静态文件CDN加速
4. 用户体验优化
- 直观的操作界面
- 快速的数据加载
- 友好的错误提示
- 完善的用户反馈
🔧 技术难点
1. 数据爬取与处理
挑战:Steam平台反爬虫机制严格,需要处理大量数据
解决方案:
# 使用代理池和请求头轮换
import requests
import random
import time
class SteamCrawler:
def __init__(self):
self.session = requests.Session()
self.proxies = self.load_proxies()
self.headers = self.load_headers()
def crawl_with_retry(self, url, max_retries=3):
for attempt in range(max_retries):
try:
proxy = random.choice(self.proxies)
headers = random.choice(self.headers)
response = self.session.get(
url,
proxies=proxy,
headers=headers,
timeout=10
)
if response.status_code == 200:
return response
except Exception as e:
print(f"Attempt {attempt + 1} failed: {e}")
time.sleep(random.uniform(1, 3))
return None
2. 情感分析准确性
挑战:中文情感分析准确率不高,需要优化算法
解决方案:
# 结合多种情感分析方法
class AdvancedSentimentAnalyzer:
def __init__(self):
self.textblob_analyzer = TextBlob
self.jieba_analyzer = jieba
self.custom_lexicon = self.load_custom_lexicon()
def analyze_sentiment_ensemble(self, text):
# 方法1:TextBlob分析
blob = TextBlob(text)
textblob_score = blob.sentiment.polarity
# 方法2:自定义词典分析
custom_score = self.custom_lexicon_analysis(text)
# 方法3:机器学习模型预测
ml_score = self.ml_model_predict(text)
# 集成多种方法的结果
final_score = (textblob_score * 0.3 +
custom_score * 0.4 +
ml_score * 0.3)
return self.score_to_label(final_score)
3. 大数据量可视化
挑战:大量数据点导致图表渲染缓慢
解决方案:
// 数据分页和懒加载
class ChartDataManager {
constructor(chart, dataSource) {
this.chart = chart;
this.dataSource = dataSource;
this.pageSize = 1000;
this.currentPage = 0;
}
async loadData(page = 0) {
const start = page * this.pageSize;
const end = start + this.pageSize;
const data = await this.dataSource.getData(start, end);
if (page === 0) {
this.chart.setOption({
series: [{
data: data
}]
});
} else {
this.chart.appendData({
seriesIndex: 0,
data: data
});
}
}
// 虚拟滚动优化
enableVirtualScroll() {
this.chart.on('scroll', (params) => {
const start = Math.floor(params.start);
const end = Math.floor(params.end);
this.loadDataRange(start, end);
});
}
}
🔮 未来规划
短期目标(1-3个月)
- 增加更多数据源(Epic Games、GOG等)
- 优化推荐算法准确率
- 添加实时数据更新功能
- 完善移动端适配
中期目标(3-6个月)
- 集成机器学习模型
- 添加社交功能
- 开发API接口文档
- 性能优化和监控
长期目标(6-12个月)
- 构建微服务架构
- 支持多语言国际化
- 开发移动端APP
- 商业化运营
📞 联系方式
码界筑梦坊 - 专注于技术分享与创新实践
📝 总结
本项目通过现代化的技术栈和设计理念,构建了一个功能完整的Steam游戏数据分析系统。从数据爬取到可视化展示,从用户管理到智能推荐,每个模块都经过精心设计和优化。
技术收获:
- 深入理解Django框架的高级特性
- 掌握数据可视化的最佳实践
- 学会处理大规模数据的性能优化
- 体验现代化前端开发流程
项目价值:
- 为游戏开发者提供市场洞察
- 为玩家提供个性化推荐
- 为研究人员提供数据支持
- 为技术社区贡献开源代码
希望这个项目能够为您的技术学习和项目开发提供参考和启发。如果您有任何问题或建议,欢迎通过上述联系方式与我交流!
本文由码界筑梦坊原创,转载请注明出处。
更多推荐
所有评论(0)