1. 项目背景与核心价值
作为一个在Web开发领域摸爬滚打多年的老码农,我见过太多毕业设计项目流于表面。但这个基于Django+Vue.js的小说推荐系统确实值得深入探讨——它不仅涵盖了全栈开发的完整技术链,还融合了爬虫、数据可视化等实用技能点。这种项目如果能做深做透,完全可以成为简历上的亮点工程。
这个系统的核心价值在于:
- 技术栈组合合理:Django作为稳健的后端框架,Vue.js作为现代化的前端方案,符合当前企业主流技术选型
- 功能模块完整:从数据采集(爬虫)到处理(推荐算法)再到展示(可视化),形成了数据处理闭环
- 实战性强:涉及高并发访问、大数据量处理等实际工程问题,不是简单的CRUD练习
我去年指导过几个类似项目,发现学生们最容易在三个地方栽跟头:Django ORM的N+1查询问题、Vue的组件状态管理、以及推荐算法的冷启动问题。下面我就结合这些实战经验,详细拆解这个系统的技术实现要点。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构设计
2.1 整体架构方案
推荐采用前后端分离架构:
code复制前端:Vue.js 2.x + Element UI + ECharts
后端:Django 3.x + Django REST framework
数据库:MySQL 8.0(适合结构化数据)+ Redis(缓存和实时推荐)
爬虫:Scrapy + selenium(应对动态渲染的网站)
部署:Nginx + uWSGI(生产环境)/ Django runserver(开发环境)
这个组合的优点是:
- Vue的响应式特性非常适合频繁更新的推荐结果展示
- Django的admin后台能快速搭建内容管理系统
- Scrapy的中间件机制便于实现分布式爬取
- 用Redis的sorted set可以高效实现实时推荐排名
2.2 数据库设计要点
小说类数据的特点是:
- 实体关系复杂(作者-作品-章节-标签等多对多关系)
- 文本内容大(单章就可能超过64KB)
- 访问模式特殊(热门作品集中访问)
建议的模型设计:
python复制class Novel(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
cover = models.ImageField(upload_to='covers/')
intro = models.TextField()
tags = models.ManyToManyField(Tag)
# 优化查询性能
class Meta:
indexes = [
models.Index(fields=['title']),
models.Index(fields=['author']),
]
class Chapter(models.Model):
novel = models.ForeignKey(Novel, on_delete=models.CASCADE, related_name='chapters')
title = models.CharField(max_length=100)
content = models.TextField()
word_count = models.IntegerField()
# 大文本字段单独存储
content = models.OneToOneField(ChapterContent, on_delete=models.CASCADE)
特别注意:Django的FileField在开发环境使用本地存储,生产环境需要配置AWS S3或阿里云OSS等对象存储服务
3. 核心功能实现
3.1 小说爬虫实现
针对不同小说网站的爬取策略:
- 静态页面站点
python复制import scrapy
class NovelSpider(scrapy.Spider):
name = 'qidian'
custom_settings = {
'DOWNLOAD_DELAY': 2,
'CONCURRENT_REQUESTS': 1
}
def parse(self, response):
yield {
'title': response.css('h1::text').get(),
'author': response.xpath('//span[@class="author"]/text()').get(),
'chapters': [{
'title': chap.css('a::text').get(),
'url': chap.css('a::attr(href)').get()
} for chap in response.css('.chapter-list li')]
}
- 动态渲染站点
python复制from selenium import webdriver
from scrapy.http import HtmlResponse
class JjwxcSpider(scrapy.Spider):
name = 'jjwxc'
def start_requests(self):
driver = webdriver.Chrome()
driver.get('http://www.jjwxc.net/')
# 处理登录等交互操作
return [HtmlResponse(
url=driver.current_url,
body=driver.page_source,
encoding='utf-8'
)]
爬虫实战经验:
- 设置合理的下载延迟(DOWNLOAD_DELAY)避免被封
- 使用Rotating User Agent中间件轮换请求头
- 对动态内容优先考虑API接口而非页面解析
3.2 推荐算法实现
基于内容的推荐算法实现示例:
python复制from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import linear_kernel
def content_based_recommend(novel_id, top_n=5):
novels = Novel.objects.all().values('id', 'title', 'intro')
tfidf = TfidfVectorizer(stop_words='english')
tfidf_matrix = tfidf.fit_transform([n['intro'] for n in novels])
cosine_sim = linear_kernel(tfidf_matrix, tfidf_matrix)
idx = next(i for i,n in enumerate(novels) if n['id']==novel_id)
sim_scores = list(enumerate(cosine_sim[idx]))
sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
return [novels[i[0]]['id'] for i in sim_scores[1:top_n+1]]
协同过滤的Django实现:
python复制from collections import defaultdict
def user_based_collaborative_filtering(user_id, top_n=5):
# 获取用户-小说评分矩阵
ratings = Rating.objects.all().values('user_id', 'novel_id', 'score')
# 构建用户相似度矩阵
user_sim = defaultdict(dict)
for u1 in User.objects.all():
for u2 in User.objects.all():
if u1.id != u2.id:
# 计算余弦相似度
user_sim[u1.id][u2.id] = calculate_similarity(u1, u2)
# 生成推荐
seen_novels = set(Rating.objects.filter(user_id=user_id)
.values_list('novel_id', flat=True))
recommendations = []
for similar_user in sorted(user_sim[user_id].items(),
key=lambda x: x[1], reverse=True)[:5]:
for novel in Rating.objects.filter(user_id=similar_user[0])
.exclude(novel_id__in=seen_novels)
.order_by('-score')[:3]:
recommendations.append(novel.id)
return recommendations[:top_n]
3.3 数据可视化实现
使用Vue+ECharts实现阅读趋势图:
vue复制<template>
<div class="chart-container">
<div ref="trendChart" style="width:600px;height:400px;"></div>
</div>
</template>
<script>
import * as echarts from 'echarts'
export default {
mounted() {
this.initChart()
},
methods: {
async initChart() {
const res = await this.$http.get('/api/reading-trend/')
const chart = echarts.init(this.$refs.trendChart)
const option = {
title: { text: '每周阅读趋势' },
tooltip: {},
xAxis: {
type: 'category',
data: res.data.dates
},
yAxis: { type: 'value' },
series: [{
name: '阅读量',
type: 'line',
smooth: true,
data: res.data.counts,
areaStyle: {}
}]
}
chart.setOption(option)
window.addEventListener('resize', chart.resize)
}
}
}
</script>
4. 性能优化方案
4.1 数据库查询优化
- 解决N+1查询问题
python复制# 错误写法:会导致N+1查询
novels = Novel.objects.all()
for novel in novels:
print(novel.author.name) # 每次循环都查询一次author表
# 正确写法:使用select_related
novels = Novel.objects.select_related('author').all()
- 批量操作替代循环
python复制# 低效写法
for novel in Novel.objects.filter(views__gt=1000):
novel.is_hot = True
novel.save()
# 高效写法
Novel.objects.filter(views__gt=1000).update(is_hot=True)
4.2 缓存策略
使用Redis缓存热门推荐:
python复制from django.core.cache import cache
def get_hot_novels():
cache_key = 'hot_novels'
novels = cache.get(cache_key)
if not novels:
novels = list(Novel.objects.filter(is_hot=True)
.order_by('-views')[:10])
cache.set(cache_key, novels, timeout=3600) # 缓存1小时
return novels
4.3 异步任务处理
使用Celery处理耗时操作:
python复制# tasks.py
from celery import shared_task
from django.core.mail import send_mail
@shared_task
def send_recommendation_email(user_id):
user = User.objects.get(id=user_id)
novels = get_recommendations(user.id)
send_mail(
'您的小说推荐',
f'根据您的阅读喜好,推荐以下作品:{", ".join(n.title for n in novels)}',
'noreply@novelsite.com',
[user.email],
fail_silently=False,
)
# views.py
def recommend_view(request):
send_recommendation_email.delay(request.user.id)
return JsonResponse({'status': 'email will be sent'})
5. 部署与监控
5.1 生产环境部署
Nginx配置示例:
nginx复制upstream novel_app {
server unix:///tmp/novel.sock;
}
server {
listen 80;
server_name novel.example.com;
location / {
include uwsgi_params;
uwsgi_pass novel_app;
}
location /static/ {
alias /path/to/static/;
expires 30d;
}
location /media/ {
alias /path/to/media/;
expires 30d;
}
}
uWSGI配置:
ini复制[uwsgi]
chdir = /path/to/project
module = novel.wsgi:application
master = true
processes = 4
socket = /tmp/novel.sock
chmod-socket = 666
vacuum = true
5.2 监控方案
使用Prometheus+Grafana监控关键指标:
- Django应用指标:请求量、响应时间、错误率
- 数据库指标:查询延迟、连接数
- 系统指标:CPU、内存、磁盘使用率
配置示例:
python复制# settings.py
INSTALLED_APPS += ['django_prometheus']
MIDDLEWARE = [
'django_prometheus.middleware.PrometheusBeforeMiddleware',
# ...其他中间件
'django_prometheus.middleware.PrometheusAfterMiddleware'
]
6. 毕业设计答辩要点
6.1 PPT制作建议
- 技术架构图:用分层图展示前后端分离架构
- 数据流程图:从爬取到推荐的全流程
- 核心算法:用伪代码说明推荐逻辑
- 创新点:对比传统推荐系统的改进
- 演示截图:包括后台管理和前端展示
6.2 答辩常见问题
-
技术选型问题
- 为什么选择Django而不是Flask?
- Vue和React的对比选择依据?
-
算法问题
- 冷启动问题如何解决?
- 推荐准确率如何评估?
-
性能问题
- 如何应对热门小说的高并发访问?
- 数据库查询优化的具体措施?
-
扩展性问题
- 如何扩展到百万级用户?
- 如何加入更多推荐维度?
我在指导学生答辩时发现,评委最关注的是:
- 技术决策的合理性(为什么用这个方案)
- 系统设计的完整性(有没有考虑边界情况)
- 创新点的真实性(是不是简单拼凑功能)
建议准备一个演示环境,提前测试好所有功能点,避免现场出现技术问题影响评分。
