基于用户画像的 Python 音乐推荐系统
技术架构
本系统采用 Python 3.x 开发,后端基于 Django 框架构建前后端分离架构,数据存储选用 MySQL 或 SQLite3。开发环境为 PyCharm,运行于 Windows 平台。数据集源自 Last.fm Dataset-360K Users 公开数据集。核心算法结合了基于用户的协同过滤与 SVD 矩阵分解,通过用户评分行为预测潜在喜好,生成个性化推荐列表。
功能模块
- 首页展示:提供全部、推荐、正在播放导航及搜索功能,卡片式展示音乐信息(时长、歌手、流派)。
- 交互操作:支持播放、喜欢、不喜欢、收藏等操作,实时反馈用户偏好。
- 用户画像:首次登录后订阅音乐流派和语种,优化后续推荐精度。
- 后台管理:管理员可对歌曲信息进行增删改查,维护音乐库完整性。
- 认证体系:注册登录界面,保障用户身份验证安全。
界面概览
系统主界面顶部设有导航栏,主体区域展示音乐卡片列表。用户中心页面标注了用户已标记的喜好状态。播放界面聚焦当前歌曲信息与进度控制。后台管理以表格形式清晰呈现音乐库数据,支持筛选操作。

核心逻辑实现
推荐逻辑主要依赖 recommend 视图函数,结合用户画像构建推荐集。以下是关键代码片段,展示了如何处理用户偏好及分页渲染。
():
page_number = request.GET.get(, )
recommend_set = build_recommend(request, request.user)
paginator = Paginator(recommend_set, )
musics = paginator.page(page_number)
context = {: musics, : [], : []}
user_profile = UserProfile.objects.(user=request.user)
user_profile.exists():
user_profile = user_profile.first()
context[] = user_profile.likes.()
context[] = user_profile.dislikes.()
render(request, , context)
():
user_obj = UserProfile.objects.get(user=request.user)
music_obj = get_object_or_404(Music.objects.(), pk=pk)
user_obj.likes.add(music_obj)
user_obj.dislikes.remove(music_obj)
messages.add_message(request, messages.INFO, )
redirect_url = request.GET.get(, )
request.GET:
redirect_url +=
HttpResponseRedirect(redirect_url)
():
user_obj = UserProfile.objects.get(user=request.user)
music_obj = get_object_or_404(Music.objects.(), pk=pk)
user_obj.dislikes.add(music_obj)
user_obj.likes.remove(music_obj)
messages.add_message(request, messages.INFO, )
redirect_url = request.GET.get(, )
request.GET:
redirect_url +=
HttpResponseRedirect(redirect_url)
():
current_play
pk > :
music_obj = Music.objects.(pk=pk)
music_obj.exists():
current_play = music_obj.first()
current_play :
messages.error(request, )
HttpResponseRedirect()
render(request, , context={: current_play})
():
user_profile = UserProfile.objects.(user=request.user)
user_profile.exists():
profile_obj: UserProfile = user_profile.first()
:
messages.error(request, )
logout(request)
HttpResponseRedirect()
request.method == :
genres = request.POST.getlist(, )
languages = request.POST.getlist(, )
profile_obj.first_run =
(genres) > :
profile_obj.genre_subscribe = .join(genres)
profile_obj.save()
messages.success(request, )
profile_obj.first_run:
profile_obj.genre_subscribe =
profile_obj.save()
(languages) > :
profile_obj.language_subscribe = .join(languages)
profile_obj.save()
messages.success(request, )
profile_obj.first_run:
profile_obj.language_subscribe =
profile_obj.save()
context = {
: profile_obj.likes.(),
: profile_obj.dislikes.(),
: build_genre_ids(),
: build_languages(),
: profile_obj.genre_subscribe.split(),
: []
}
lang profile_obj.language_subscribe.split():
lang = lang.strip()
context[].append(lang)
render(request, , context=context)
():
request.GET:
messages.error(request, )
HttpResponseRedirect()
keyword = request.GET.get()
action = request.GET.get()
musics = []
action == :
musics = Music.objects.(song_name__contains=keyword)
action == :
musics = Music.objects.(artist_name__contains=keyword)
messages.info(request, )
context = {: musics, : [], : []}
request.user.is_authenticated:
user_profile = UserProfile.objects.(user=request.user)
user_profile.exists():
user_profile = user_profile.first()
context[] = user_profile.likes.()
context[] = user_profile.dislikes.()
render(request, , context)


