1. 先搞清楚这个项目到底要做什么,以及它适合谁
这个“Python商品推荐系统”毕业设计,核心目标很明确:搭建一个能自动抓取商品数据、分析用户行为、并给出个性化推荐的全栈Web应用。它不是一个简单的算法演示,而是一个从数据采集、处理、存储、分析到前端展示的完整工程实践。
如果你正在寻找一个能串联起Python后端、数据爬虫、算法应用和前端技术的综合性项目,这个选题非常合适。它最核心的价值在于,让你体验一个数据驱动型应用从0到1的完整生命周期,而不是只停留在某个单一环节。
项目涉及的技术栈很清晰:
- 后端与数据处理:Python + Django(Web框架) + Scrapy(爬虫框架)。
- 核心算法:Apriori算法(用于挖掘商品间的关联规则,实现“买了A的人也可能买B”的推荐)。
- 前端展示:Vue.js(构建交互式用户界面)。
- 数据分析与可视化:利用Python的Pandas、Matplotlib/Seaborn等库进行数据分析和图表生成。
对于初学者,这个项目的挑战在于如何将这些技术模块有机地串联起来,并处理好数据流。对于有经验的开发者,重点则在于如何优化爬虫效率、设计合理的数据库模型、提升Apriori算法的性能,以及实现前后端的优雅交互。
2. 环境准备与项目结构设计:别急着写代码
在动手敲第一行代码之前,先把环境和项目骨架搭好。混乱的目录结构是后期调试的噩梦。
2.1 开发环境清单
我建议在开始前,先确保你的机器上已经备齐以下环境。不要用太老的版本,避免兼容性问题。
- Python: 3.8 或 3.9 版本比较稳妥。可以使用
python --version检查。 - Node.js: Vue开发需要。安装LTS版本即可,用
node -v和npm -v检查。 - 数据库: Django默认使用SQLite(适合学习和演示),但如果数据量较大或想体验生产环境,可以安装MySQL或PostgreSQL。这里我们先以SQLite为例,它无需单独安装。
- 代码编辑器/IDE:VSCode或PyCharm。VSCode需要安装Python、Vue等插件。
2.2 创建虚拟环境与安装依赖
永远不要在系统全局Python环境里做项目。为每个项目创建独立的虚拟环境是必须养成的习惯。
# 1. 创建项目总目录 mkdir product_recommendation_system cd product_recommendation_system # 2. 创建Python虚拟环境(以venv为例) python -m venv venv # 3. 激活虚拟环境 # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate # 激活后,命令行提示符前会出现 (venv) 标识2.3 初始化项目骨架
这个项目本质上是“前后端分离”的架构:Django提供数据API,Vue负责展示。因此,我们需要两个独立的工程目录。
# 项目根目录 /product_recommendation_system 下 # 4. 创建Django后端项目 django-admin startproject backend cd backend # 在Django项目内创建核心应用,例如叫 `recommend` python manage.py startapp recommend cd .. # 5. 创建Vue前端项目(使用Vue CLI) # 确保已安装Vue CLI: npm install -g @vue/cli vue create frontend # 创建过程中,选择手动配置,确保勾选Router(路由)、Vuex(状态管理,可选但推荐)、CSS Pre-processors(如Sass/SCSS)完成后的目录结构应该类似这样:
product_recommendation_system/ ├── venv/ # Python虚拟环境 ├── backend/ # Django后端项目 │ ├── backend/ # 项目设置目录 │ │ ├── __init__.py │ │ ├── settings.py # 核心配置文件 │ │ ├── urls.py # 项目级URL路由 │ │ └── wsgi.py │ ├── recommend/ # 推荐系统核心应用 │ │ ├── migrations/ # 数据库迁移文件 │ │ ├── __init__.py │ │ ├── admin.py # 后台管理配置 │ │ ├── apps.py │ │ ├── models.py # 数据模型定义(核心!) │ │ ├── tests.py │ │ └── views.py # 视图函数/类 │ └── manage.py └── frontend/ # Vue前端项目 ├── node_modules/ ├── public/ ├── src/ │ ├── assets/ │ ├── components/ # Vue组件 │ ├── router/ # 路由配置 │ ├── store/ # Vuex状态管理(可选) │ ├── views/ # 页面视图 │ ├── App.vue │ └── main.js ├── .gitignore ├── babel.config.js ├── package.json └── vue.config.js # Vue项目配置文件2.4 安装后端核心依赖
在激活的虚拟环境下,进入backend目录,创建requirements.txt文件并安装。
# backend/requirements.txt Django>=3.2, <4.0 # 选择一个稳定的版本 djangorestframework # 用于构建RESTful API django-cors-headers # 解决Vue前端跨域访问Django API的问题 scrapy # 爬虫框架 pandas # 数据分析 numpy # 数值计算 scikit-learn # 机器学习库,可能用于数据预处理或其它算法 matplotlib # 绘图,用于可视化(后端生成图表图片或数据) jieba # 中文分词(如果处理中文商品数据)安装命令:
cd backend pip install -r requirements.txt注意:
Scrapy和Django在同一个项目中有时会因为事件循环(Event Loop)冲突,尤其是在Django 3.x的异步特性下。如果遇到奇怪的问题,一个稳妥的做法是将爬虫作为一个独立的脚本或服务来运行,通过文件或数据库与Django主应用交换数据。这是初期最容易忽略的坑。
3. 核心模块实现:从数据模型到爬虫
不要试图一次性把所有功能写完。按照数据流动的顺序来:先定义数据怎么存,再想办法把数据抓回来存进去,然后才能谈分析和推荐。
3.1 定义数据模型 (models.py)
在backend/recommend/models.py中,设计核心的数据表。这是整个系统的基石。
from django.db import models class User(models.Model): """用户模型(如果不需要复杂注册,可以简化)""" username = models.CharField(max_length=100, unique=True) # 可以添加更多字段,如邮箱、注册时间等 created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return self.username class Product(models.Model): """商品模型""" # 爬虫抓取的商品ID或自增ID external_id = models.CharField(max_length=100, blank=True, null=True) name = models.CharField(max_length=200) # 商品名称 category = models.CharField(max_length=100, blank=True) # 类别 price = models.DecimalField(max_digits=10, decimal_places=2) description = models.TextField(blank=True) image_url = models.URLField(blank=True) # 可以添加销量、评分、上架时间等字段用于热门推荐 sales_count = models.IntegerField(default=0) created_at = models.DateTimeField(auto_now_add=True) class Meta: ordering = ['-created_at'] def __str__(self): return self.name class UserBehavior(models.Model): """用户行为日志(核心数据源)""" BEHAVIOR_TYPES = ( ('view', '浏览'), ('cart', '加入购物车'), ('buy', '购买'), ('collect', '收藏'), ) user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='behaviors') product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='behaviors') behavior_type = models.CharField(max_length=10, choices=BEHAVIOR_TYPES) # 行为权重:例如购买权重为5,加入购物车为3,浏览为1。用于在关联规则计算中加权。 weight = models.IntegerField(default=1) timestamp = models.DateTimeField(auto_now_add=True) class Meta: indexes = [ models.Index(fields=['user', '-timestamp']), models.Index(fields=['product']), ] def __str__(self): return f'{self.user} {self.get_behavior_type_display()} {self.product}' class AssociationRule(models.Model): """存储Apriori算法计算出的关联规则""" antecedent = models.CharField(max_length=500) # 前件,如商品ID列表 "1,5,7" consequent = models.CharField(max_length=500) # 后件,如商品ID "10" support = models.FloatField() # 支持度 confidence = models.FloatField() # 置信度 lift = models.FloatField() # 提升度 created_at = models.DateTimeField(auto_now_add=True) # 规则计算时间 def __str__(self): return f'{self.antecedent} => {self.consequent} (conf: {self.confidence:.2f})'定义好模型后,生成并应用数据库迁移:
python manage.py makemigrations python manage.py migrate3.2 构建Scrapy爬虫抓取商品数据
在项目根目录或backend同级,创建一个独立的爬虫项目是更清晰的做法。
# 在项目根目录 /product_recommendation_system 下 scrapy startproject product_spider cd product_spider scrapy genspider example_spider example.com # 替换为目标网站你需要根据目标网站(例如一个电商网站的商品列表页)编写爬虫。这里给出一个高度简化的示例,重点在于理解流程和数据存储。
# product_spider/product_spider/spiders/example_spider.py import scrapy import json from datetime import datetime from urllib.parse import urljoin import sys import os # 将Django项目路径加入系统路径,以便在爬虫中导入Django模型 sys.path.append(os.path.join(os.path.dirname(__file__), ‘../../../backend’)) os.environ.setdefault(‘DJANGO_SETTINGS_MODULE’, ‘backend.settings’) import django django.setup() from recommend.models import Product class ExampleSpiderSpider(scrapy.Spider): name = ‘example_spider’ allowed_domains = [‘example.com’] start_urls = [‘https://example.com/products/page1’] # 起始页 def parse(self, response): # 1. 解析列表页,提取商品详情页链接 product_links = response.css(‘div.product-item a::attr(href)’).getall() for link in product_links: full_url = urljoin(response.url, link) yield scrapy.Request(full_url, callback=self.parse_product_detail) # 2. 翻页逻辑(示例) next_page = response.css(‘a.next-page::attr(href)’).get() if next_page: yield scrapy.Request(urljoin(response.url, next_page), callback=self.parse) def parse_product_detail(self, response): # 3. 解析商品详情页,提取字段 # 使用CSS选择器或XPath,这里需要根据目标网站实际结构调整 item = {} item[‘external_id’] = response.css(‘span.product-id::text’).get(‘’).strip() item[‘name’] = response.css(‘h1.product-title::text’).get(‘’).strip() item[‘price’] = float(response.css(‘span.price::text’).re_first(r‘[\d\.]+’)) # 提取价格数字 item[‘category’] = response.css(‘nav.breadcrumb a::text’).getall()[-1] # 取最后一个分类 item[‘description’] = ‘ ‘.join(response.css(‘div.description *::text’).getall()).strip() item[‘image_url’] = response.css(‘img.main-image::attr(src)’).get(‘’) # 4. 保存到数据库(使用Django的ORM) # 避免重复,根据external_id更新或创建 product, created = Product.objects.update_or_create( external_id=item[‘external_id’], defaults={ ‘name’: item[‘name’], ‘price’: item[‘price’], ‘category’: item[‘category’], ‘description’: item[‘description’], ‘image_url’: item[‘image_url’], } ) self.logger.info(f‘{“Created” if created else “Updated”} product: {product.name}’) yield item # 也可以将item传递给pipeline进行后续处理关键点:
- 目标网站分析:使用浏览器开发者工具,找到商品列表和详情的HTML结构,确定正确的CSS选择器或XPath。
- 反爬策略:真实网站通常有反爬。你需要设置
DOWNLOAD_DELAY、User-Agent,甚至考虑使用scrapy-rotating-proxies等中间件。毕业设计中,务必遵守目标网站的robots.txt协议,并控制请求频率,避免对对方服务器造成压力。 - 数据清洗:在
parse方法中做好数据清洗,处理缺失值、格式化价格和日期。 - 独立运行:在
product_spider目录下运行scrapy crawl example_spider来测试爬虫。确保它能正确抓取并存入Django数据库。
3.3 实现Apriori算法进行关联规则挖掘
Apriori算法是经典的关系规则挖掘算法。我们可以在Django中创建一个管理命令 (management/commands/) 或一个独立的脚本,定期运行来计算规则。
首先,准备数据。我们需要将用户行为数据转换成Apriori算法需要的“事务”列表格式。
# backend/recommend/utils/apriori.py (新建文件) import pandas as pd from itertools import combinations from collections import defaultdict from recommend.models import UserBehavior, AssociationRule def prepare_transactions(min_support_count=5): """ 从UserBehavior表中准备事务数据。 每个事务是一个用户在一段时间内(例如一次会话)发生的行为对应的商品集合。 这里简化处理:将每个用户的所有‘购买’行为商品作为一个事务。 min_support_count: 最小支持度计数(事务数),用于初步过滤低频商品。 """ # 获取所有购买行为 buy_behaviors = UserBehavior.objects.filter(behavior_type=‘buy’).select_related(‘user’, ‘product’) # 按用户分组,收集其购买过的商品ID user_transactions = defaultdict(set) for behavior in buy_behaviors: user_transactions[behavior.user_id].add(str(behavior.product_id)) # 商品ID作为项 # 转换为事务列表 transactions = list(user_transactions.values()) # 初步过滤:只保留出现次数>=min_support_count的商品 item_counts = defaultdict(int) for transaction in transactions: for item in transaction: item_counts[item] += 1 frequent_items = {item for item, count in item_counts.items() if count >= min_support_count} # 过滤事务,只保留频繁项 filtered_transactions = [] for transaction in transactions: filtered_trans = {item for item in transaction if item in frequent_items} if filtered_trans: # 过滤后可能为空 filtered_transactions.append(filtered_trans) return filtered_transactions, list(frequent_items) def apriori(transactions, min_support=0.01, min_confidence=0.5): """ 实现Apriori算法。 transactions: 事务列表,每个事务是商品ID的集合。 min_support: 最小支持度。 min_confidence: 最小置信度。 返回: 符合条件的关联规则列表,每条规则格式为 (antecedent, consequent, support, confidence, lift) """ num_transactions = len(transactions) min_support_count = min_support * num_transactions # 1. 找出所有频繁1项集 item_counts = defaultdict(int) for transaction in transactions: for item in transaction: item_counts[frozenset([item])] += 1 L1 = {itemset: count for itemset, count in item_counts.items() if count >= min_support_count} current_L = L1 all_frequent_itemsets = {1: current_L} k = 2 # 2. 迭代找出频繁k项集 while current_L: # 生成候选k项集 candidates = set() itemsets_list = list(current_L.keys()) for i in range(len(itemsets_list)): for j in range(i+1, len(itemsets_list)): itemset_i = itemsets_list[i] itemset_j = itemsets_list[j] # 如果前k-2项相同,则可以连接 if len(itemset_i.union(itemset_j)) == k: candidates.add(itemset_i.union(itemset_j)) # 计算候选集的支持度计数 candidate_counts = defaultdict(int) for transaction in transactions: transaction_set = set(transaction) for candidate in candidates: if candidate.issubset(transaction_set): candidate_counts[candidate] += 1 # 筛选出频繁k项集 current_L = {itemset: count for itemset, count in candidate_counts.items() if count >= min_support_count} if current_L: all_frequent_itemsets[k] = current_L k += 1 # 3. 从频繁项集中生成关联规则 rules = [] for k, itemsets in all_frequent_itemsets.items(): if k < 2: continue for itemset, support_count in itemsets.items(): itemset_list = list(itemset) # 生成所有非空真子集作为前件 for r in range(1, k): for antecedent in combinations(itemset_list, r): antecedent_set = frozenset(antecedent) consequent_set = itemset - antecedent_set if not consequent_set: continue # 计算前件的支持度 antecedent_support_count = all_frequent_itemsets[len(antecedent_set)][antecedent_set] # 计算置信度 confidence = support_count / antecedent_support_count if confidence >= min_confidence: # 计算提升度 Lift = P(AB)/(P(A)P(B)) consequent_support_count = all_frequent_itemsets[1][frozenset(list(consequent_set))] lift = (support_count / num_transactions) / ( (antecedent_support_count / num_transactions) * (consequent_support_count / num_transactions) ) rules.append(( ‘,‘.join(sorted(antecedent_set)), # 前件商品ID列表 ‘,‘.join(sorted(consequent_set)), # 后件商品ID列表 support_count / num_transactions, # 支持度 confidence, # 置信度 lift # 提升度 )) return rules def generate_and_save_rules(): """主函数:准备数据,运行Apriori,保存规则到数据库""" print(“准备事务数据...”) transactions, _ = prepare_transactions(min_support_count=10) # 参数可调 if len(transactions) < 50: # 数据量太少,规则无意义 print(“事务数据不足,无法生成有效规则。”) return print(f“共处理 {len(transactions)} 条事务。”) print(“运行Apriori算法...”) rules = apriori(transactions, min_support=0.05, min_confidence=0.3) # 参数可调 # 清空旧规则(可选,或增量更新) AssociationRule.objects.all().delete() print(f“生成 {len(rules)} 条规则,正在保存...”) rule_objs = [] for ante, cons, sup, conf, lift in rules: rule_objs.append(AssociationRule( antecedent=ante, consequent=cons, support=sup, confidence=conf, lift=lift )) AssociationRule.objects.bulk_create(rule_objs) print(“规则保存完成!”)然后,创建一个Django管理命令来方便调用:
# 在 backend/recommend/management/commands/ 目录下创建 generate_rules.py# backend/recommend/management/commands/generate_rules.py from django.core.management.base import BaseCommand from recommend.utils.apriori import generate_and_save_rules class Command(BaseCommand): help = ‘Generate association rules using Apriori algorithm’ def handle(self, *args, **options): self.stdout.write(‘Starting rule generation...’) generate_and_save_rules() self.stdout.write(self.style.SUCCESS(‘Successfully generated association rules’))运行命令:
python manage.py generate_rules算法调优与注意事项:
- 数据量:Apriori算法在项集很多时计算量巨大。毕业设计可以使用小规模模拟数据(几百个用户,几千条行为记录)来演示。
- 参数调整:
min_support(最小支持度)和min_confidence(最小置信度)是关键参数。支持度太高可能找不到规则,太低则规则太多且可能是噪声。需要根据数据分布调整。 - 性能:对于大规模数据,可以考虑使用
mlxtend库中优化过的Apriori实现,或者使用FP-Growth等更高效的算法。 - 行为权重:在
prepare_transactions函数中,我们只用了‘购买’行为。更精细的做法是融合浏览、加购等行为,并赋予不同权重(如UserBehavior.weight字段),在生成事务时进行加权处理。
4. 构建Django REST API与Vue前端
数据有了,规则算好了,现在需要让前端能访问到。
4.1 创建Django REST API
首先,安装并配置djangorestframework和django-cors-headers。
# backend/backend/settings.py INSTALLED_APPS = [ ... ‘rest_framework’, ‘corsheaders’, # 添加 ‘recommend’, # 你的应用 ] MIDDLEWARE = [ ‘corsheaders.middleware.CorsMiddleware’, # 尽量放在最前 ‘django.middleware.security.SecurityMiddleware’, ... ] # 配置CORS,允许Vue前端开发服务器的地址访问 CORS_ALLOWED_ORIGINS = [ “http://localhost:8080”, # Vue默认开发服务器端口 “http://127.0.0.1:8080”, ]然后,创建序列化器(Serializer)和视图(View)。
# backend/recommend/serializers.py from rest_framework import serializers from .models import Product, AssociationRule class ProductSerializer(serializers.ModelSerializer): class Meta: model = Product fields = ‘__all__’ # 或指定需要的字段,如 [‘id’, ‘name’, ‘price’, ‘category’, ‘image_url’] class AssociationRuleSerializer(serializers.ModelSerializer): # 可以嵌套序列化商品详情,这里只返回ID,前端再根据ID请求商品详情 antecedent_products = serializers.SerializerMethodField() consequent_products = serializers.SerializerMethodField() class Meta: model = AssociationRule fields = [‘id’, ‘antecedent’, ‘consequent’, ‘support’, ‘confidence’, ‘lift’, ‘antecedent_products’, ‘consequent_products’] def get_antecedent_products(self, obj): product_ids = [int(id) for id in obj.antecedent.split(‘,’) if id] products = Product.objects.filter(id__in=product_ids) return ProductSerializer(products, many=True).data def get_consequent_products(self, obj): product_ids = [int(id) for id in obj.consequent.split(‘,’) if id] products = Product.objects.filter(id__in=product_ids) return ProductSerializer(products, many=True).data# backend/recommend/views.py from rest_framework import generics, viewsets from rest_framework.response import Response from rest_framework.decorators import action from django.db.models import Count, Q from .models import Product, UserBehavior, AssociationRule from .serializers import ProductSerializer, AssociationRuleSerializer class ProductListView(generics.ListAPIView): """商品列表""" queryset = Product.objects.all() serializer_class = ProductSerializer # 可以添加分页、过滤、搜索等功能 # pagination_class = PageNumberPagination # filter_backends = [DjangoFilterBackend, SearchFilter] # filterset_fields = [‘category’] # search_fields = [‘name’, ‘description’] class ProductDetailView(generics.RetrieveAPIView): """商品详情""" queryset = Product.objects.all() serializer_class = ProductSerializer class RecommendationView(generics.GenericAPIView): """推荐接口""" def get(self, request, *args, **kwargs): # 这里实现多种推荐逻辑的融合 user_id = request.query_params.get(‘user_id’, None) product_id = request.query_params.get(‘product_id’, None) # 基于商品的推荐 recommended_products = [] # 1. 基于关联规则的推荐 (Apriori) if product_id: # 查找包含该商品作为前件的规则,按置信度或提升度排序 rules = AssociationRule.objects.filter( antecedent__contains=str(product_id) ).order_by(‘-confidence’)[:10] # 取前10条 for rule in rules: consequent_ids = [int(id) for id in rule.consequent.split(‘,’) if id and int(id) != int(product_id)] products = Product.objects.filter(id__in=consequent_ids) recommended_products.extend(products) # 2. 热门推荐 (兜底) if not recommended_products: recommended_products = Product.objects.order_by(‘-sales_count’)[:20] # 去重 seen_ids = set() unique_products = [] for p in recommended_products: if p.id not in seen_ids: seen_ids.add(p.id) unique_products.append(p) serializer = ProductSerializer(unique_products[:10], many=True) # 最终返回10个 return Response(serializer.data) class AssociationRuleViewSet(viewsets.ReadOnlyModelViewSet): """关联规则查询接口(用于后台数据分析展示)""" queryset = AssociationRule.objects.all().order_by(‘-confidence’) serializer_class = AssociationRuleSerializer配置URL路由:
# backend/backend/urls.py from django.contrib import admin from django.urls import path, include from rest_framework.routers import DefaultRouter from recommend.views import ProductListView, ProductDetailView, RecommendationView, AssociationRuleViewSet router = DefaultRouter() router.register(r‘rules’, AssociationRuleViewSet) # /api/rules/ urlpatterns = [ path(‘admin/’, admin.site.urls), path(‘api/’, include(router.urls)), path(‘api/products/’, ProductListView.as_view(), name=‘product-list’), path(‘api/products/<int:pk>/’, ProductDetailView.as_view(), name=‘product-detail’), path(‘api/recommend/’, RecommendationView.as_view(), name=‘recommend’), ]4.2 开发Vue前端界面
进入frontend目录,安装必要的依赖(如axios用于HTTP请求,element-ui或ant-design-vue作为UI组件库)。
cd frontend npm install axios element-plus # 以Element Plus为例修改src/main.js引入组件库。
// frontend/src/main.js import { createApp } from ‘vue’ import App from ‘./App.vue’ import router from ‘./router’ import ElementPlus from ‘element-plus’ import ‘element-plus/dist/index.css’ const app = createApp(App) app.use(router) app.use(ElementPlus) app.mount(‘#app’)创建几个核心页面组件:
- 商品列表页 (ProductList.vue):展示所有商品,可以点击查看详情。
- 商品详情页 (ProductDetail.vue):展示单个商品信息,并调用推荐接口显示“猜你喜欢”。
- 数据分析页 (Analysis.vue):展示关联规则列表、支持度、置信度等,可以用图表可视化(如ECharts)。
以商品详情页为例:
<!-- frontend/src/views/ProductDetail.vue --> <template> <div class=“product-detail”> <el-row :gutter=“20”> <el-col :span=“16”> <el-card> <template #header> <div class=“card-header”> <span>{{ product.name }}</span> </div> </template> <el-image :src=“product.image_url” fit=“contain” style=“width: 100%; max-height: 400px;” /> <el-descriptions :column=“2” border> <el-descriptions-item label=“价格”>{{ product.price }} 元</el-descriptions-item> <el-descriptions-item label=“类别”>{{ product.category }}</el-descriptions-item> </el-descriptions> <div style=“margin-top: 20px;”> <h3>商品描述</h3> <p>{{ product.description }}</p> </div> </el-card> </el-col> <el-col :span=“8”> <el-card header=“猜你喜欢”> <div v-if=“recommendations.length === 0”>加载推荐中...</div> <el-row :gutter=“10”> <el-col :span=“12” v-for=“item in recommendations” :key=“item.id” style=“margin-bottom: 15px;”> <el-card shadow=“hover” @click=“goToProduct(item.id)” style=“cursor: pointer;”> <el-image :src=“item.image_url” style=“width: 100%; height: 120px;” fit=“cover” /> <div style=“padding: 10px 0;”> <div class=“text-ellipsis”>{{ item.name }}</div> <div style=“color: #f56c6c; font-weight: bold;”>¥{{ item.price }}</div> </div> </el-card> </el-col> </el-row> </el-card> </el-col> </el-row> </div> </template> <script setup> import { ref, onMounted } from ‘vue’ import { useRoute, useRouter } from ‘vue-router’ import axios from ‘axios’ const route = useRoute() const router = useRouter() const productId = route.params.id const product = ref({}) const recommendations = ref([]) const API_BASE = ‘http://localhost:8000/api’ // Django后端地址 onMounted(async () => { // 1. 获取商品详情 try { const prodRes = await axios.get(`${API_BASE}/products/${productId}/`) product.value = prodRes.data } catch (error) { console.error(‘获取商品详情失败:’, error) ElMessage.error(‘商品不存在’) router.push(‘/’) return } // 2. 获取推荐商品 try { const recRes = await axios.get(`${API_BASE}/recommend/`, { params: { product_id: productId } }) recommendations.value = recRes.data } catch (error) { console.error(‘获取推荐失败:’, error) ElMessage.warning(‘推荐加载失败’) } }) const goToProduct = (id) => { router.push(`/product/${id}`) } </script> <style scoped> .text-ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } </style>配置Vue路由 (src/router/index.js) 将页面连接起来。
4.3 实现数据可视化
在数据分析页面,可以使用ECharts来可视化关联规则。首先安装ECharts:
npm install echarts然后在Analysis.vue组件中引入并使用。例如,绘制一个展示支持度与置信度关系的散点图,或展示Top N关联规则的柱状图。
<!-- frontend/src/views/Analysis.vue (部分代码) --> <template> <div> <h2>关联规则分析</h2> <el-table :data=“rules” style=“width: 100%”> <el-table-column prop=“id” label=“ID” width=“80” /> <el-table-column label=“规则”> <template #default=“{ row }”> <span v-for=“p in row.antecedent_products” :key=“p.id”> {{ p.name }} & </span> => {{ row.consequent_products[0]?.name }} </template> </el-table-column> <el-table-column prop=“support” label=“支持度” :formatter=“formatPercent” /> <el-table-column prop=“confidence” label=“置信度” :formatter=“formatPercent” /> <el-table-column prop=“lift” label=“提升度” /> </el-table> <div ref=“chartRef” style=“width: 100%; height: 400px; margin-top: 30px;”></div> </div> </template> <script setup> import { ref, onMounted, onUnmounted } from ‘vue’ import * as echarts from ‘echarts’ import axios from ‘axios’ const chartRef = ref(null) let chartInstance = null const rules = ref([]) const API_BASE = ‘http://localhost:8000/api’ const formatPercent = (row, column, cellValue) => { return (cellValue * 100).toFixed(2) + ‘%’ } onMounted(async () => { // 获取规则数据 const res = await axios.get(`${API_BASE}/rules/`) rules.value = res.data.results || res.data // 初始化图表 if (chartRef.value) { chartInstance = echarts.init(chartRef.value) const option = { title: { text: ‘关联规则散点图 (支持度 vs 置信度)’ }, tooltip: { formatter: function (params) { return `规则ID: ${params.data[2]}<br/>支持度: ${(params.data[0]*100).toFixed(2)}%<br/>置信度: ${(params.data[1]*100).toFixed(2)}%` } }, xAxis: { type: ‘value’, name: ‘支持度’ }, yAxis: { type: ‘value’, name: ‘置信度’ }, series: [{ data: rules.value.map(r => ([r.support, r.confidence, r.id])), type: ‘scatter’, symbolSize: 10 }] } chartInstance.setOption(option) } }) onUnmounted(() => { if (chartInstance) { chartInstance.dispose() } }) </script>5. 系统联调、部署与项目总结
5.1 前后端联调与问题排查
启动后端服务:
cd backend python manage.py runserver默认运行在
http://127.0.0.1:8000。访问http://127.0.0.1:8000/api/products/应能看到JSON格式的商品列表。启动前端开发服务器:
cd frontend npm run serve默认运行在
http://localhost:8080。跨域问题:确保
backend/settings.py中的CORS_ALLOWED_ORIGINS包含了前端地址 (http://localhost:8080)。如果还有问题,检查浏览器控制台Network标签页。数据问题:如果API返回为空,检查Django后台 (
http://127.0.0.1:8000/admin) 是否有数据。确保爬虫已运行并导入了数据,且Apriori算法已生成规则。推荐逻辑验证:在商品详情页,查看浏览器开发者工具的Network请求,确认
/api/recommend/?product_id=X的请求是否成功,返回的数据结构是否正确。
5.2 简单的部署考虑(用于演示)
毕业设计答辩通常需要现场演示。一个简单可靠的方案是:
- 后端:使用
python manage.py runserver 0.0.0.0:8000在演示电脑上运行。如果需要更稳定,可以用Gunicorn+Nginx,但对本地演示来说,Django开发服务器足够。 - 前端:运行
npm run build生成静态文件 (dist目录)。可以将这些文件拷贝到后端Django的静态文件目录,并配置Django来服务这些静态文件,这样只需要运行一个Django服务即可同时提供API和前端页面。- 将
frontend/dist下的所有文件复制到backend/static/目录。 - 在
backend/backend/urls.py中添加:from django.views.generic import TemplateView urlpatterns = [ …, path(‘’, TemplateView.as_view(template_name=‘index.html’)), # 指向前端入口 ] - 配置
settings.py中的STATICFILES_DIRS和TEMPLATES的DIRS,确保能找到index.html。
- 将
5.3 项目总结与扩展方向
完成以上步骤,一个具备数据爬取、关联规则挖掘、API服务和可视化前端的商品推荐系统原型就搭建完成了。在毕业设计答辩中,你可以重点阐述:
- 技术选型理由:为什么用Django(快速开发、ORM强大)、Scrapy(专业的爬虫框架)、Vue(响应式、生态丰富)、Apriori(经典易懂)。
- 系统架构:清晰说明前后端分离、数据流(爬虫->数据库->算法->API->前端)。
- 核心算法实现:讲解Apriori算法的原理、在你的数据上是如何应用的、参数调优过程。
- 演示效果:现场展示商品列表、详情页、以及基于当前商品的“猜你喜欢”推荐。
- 数据分析展示:在分析页面展示挖掘出的关联规则,并用图表进行解读。
可能的扩展与优化(可以作为毕业设计的亮点或未来工作):
- 推荐策略融合:结合基于内容的推荐(商品属性相似度)、协同过滤(用户相似度)和关联规则,实现混合推荐。
- 实时性:将用户最新的行为(如浏览)通过WebSocket或轮询实时加入推荐计算。
- 算法优化:用FP-Growth算法替代Apriori处理更大规模数据;引入时间衰减因子,让近期行为权重更高。
- 系统优化:为频繁查询的推荐结果添加缓存(如Redis);对爬虫任务进行队列管理(如Celery)。
- 可视化增强:使用关系图展示商品间的关联关系;对用户行为序列进行可视化分析。
最后,也是最实际的建议:在开发过程中,每一步都做好测试,多打印日志。遇到问题,先检查数据(数据库里有没有?)、再检查接口(API返回对不对?)、最后看前端(请求发了吗?数据绑定对了吗?)。这个项目涉及环节多,按模块逐个攻破,最终整合,是成功的关键。