☰
文本分析驱动的新闻推荐系统:从TF-IDF到用户画像的完整实战指南
2026/10/22 2:54:37 网站建设 项目流程

每到毕业设计选题季,总会有人拿着“文本分析的新闻推荐”这个题来问:到底是写爬虫,还是做算法,还是搞一个后台管理页面?同样一句话,每个人理解差别很大。我干脆以自己做过的一套方案为底,项目内部编号77053,把题面拆解、架构设计、文本分析、推荐策略、测试评估和答辩准备完整讲一遍。

先说结论:这个题目的核心竞争力不是新闻后台,而是“文本分析”和“推荐”这两块。你只有让用户真正感知到“推荐结果会随着我看过的内容变化”,才算把题做出了味道。下面的内容适合正在准备毕业设计或课程设计的计算机相关专业学生,也适合想用Python快速入门推荐系统实战的同学。

1. 题面拆解:新闻推荐系统到底要完成哪些事

1.1 先分清“新闻管理系统”和“新闻推荐系统”

不少初稿开题会写成这样:管理员录入新闻,用户注册登录后能看到新闻列表,按分类点点点。这不是推荐系统,这严格来说只是一个“新闻管理系统”。

新闻管理系统关心的是新闻内容的增删改查和展示;新闻推荐系统关心的是如何把合适的新闻推给合适的用户。二者的核心差别在于:推荐系统必须建模“用户、新闻、行为”这三者之间的关系,并且利用这个关系去预测用户可能感兴趣但还没有看到的内容。

从这个角度说,如果你只是把新闻列表换一个“猜你喜欢”的名字,答辩时很容易被一句话问倒:推荐算法在哪里?数据特征是什么?推荐结果是怎么排出来的?

1.2 绕不开的四条核心链路

一次合格的文本分析新闻推荐,至少要串起四条链路。

第一条是新闻数据链路:新闻进入系统后,先做清洗,再做中文分词、停用词过滤、关键词提取、TF-IDF特征计算,最后把特征向量和关键词存进数据库。

第二条是用户行为链路:用户注册登录后,浏览过哪条新闻、收藏了什么、点了多少次“不感兴趣”,这些行为都要被记录成结构化日志。

第三条是用户画像链路:根据用户的历史行为,给每个用户维护一个关键词权重表,比如这个用户对“芯片”权重1.8,对“体育”权重0.3,这就构成了他的兴趣画像。

第四条是推荐展示链路:系统拿用户画像去和新闻特征做匹配,通过相似度计算生成候选集,再经过多样性重排后产出TopN列表。

四条链路合在一起,文本分析的价值就落到了实处:没有文本分析,新闻只能靠分类和标签匹配;有了文本分析,系统才能理解新闻内容本身,才能算出“这篇看起来在讲手机的文章,其实和那篇半导体新闻关系很深”。

1.3 功能边界与工作量控制

我见过不少同学想把系统做得特别庞大,加爬虫、加全文检索、加实时推送、加APP端小程序端,结果每个点都做得不深。

如果时间只有4到6周,我建议把功能边界收敛为这样几块:

  • 新闻管理:新闻导入、分类维护、上下架,数据可以来自公开数据集或少量合规爬取。
  • 用户端:注册登录、个性化推荐、新闻详情、搜索、收藏、不感兴趣反馈、历史记录。
  • 管理员端:用户管理、新闻管理、行为数据统计、推荐日志查看。
  • 文本分析模块:文本清洗、分词、关键词提取、特征向量生成。
  • 推荐模块:热点召回、相似内容召回、用户画像重排、冷启动兜底。

把“推荐日志”单独做成一个功能点很聪明。它能让你在答辩时拿出真实数据:某个用户昨天浏览了什么,系统今天推荐了什么,推荐理由是什么。这比空口说“我的算法很准”有说服力得多。

2. 技术栈与整体架构:怎么选才能让文本分析不别扭

2.1 技术选型对比

这个题目在不同院校里对应的技术栈五花八门。有人用Java写,有人用PHP写,有人前后端分离,有人直接做一个微信小程序。到底怎么选?

方案优势劣势适合场景
Python + Flask + MySQL + Vue文本分析生态强,jieba、sklearn开箱即用,代码量小工程化结构需要自己控制大多数本科毕设
Java + Spring Boot + Vue工程规范,面试友好中文分词和文本特征处理要引第三方库,代码量明显偏大课题要求Java
PHP + ThinkPHPWeb开发快文本分析生态弱,算法部分不好展开偏管理的系统
微信小程序作为前端展示方便算法部分仍需要后端支撑,工作量双倍需要移动端演示

我自己最后选了Python + Flask + MySQL + Vue。理由很直接:新闻推荐的核心难点在文本特征提取和相似度计算,而Python在这方面的成熟库最多。Flask足够轻量,适合把推荐逻辑以REST API方式暴露给前端。MySQL存业务数据没有压力,1万条新闻、100个用户的行为数据量级,完全跑得动。

如果你的评审老师特别看重Java,也有一个折中方案:用Python单独写一个“文本分析推荐服务”,对外提供HTTP接口,Java后端通过HTTP调用。这也是一种真实工程里常见的跨语言服务架构,放到论文里反而能体现系统设计能力。

2.2 模块划分和数据流向

整个系统我建议分成三层。

展示层用Vue或者原生页面,负责登录注册、新闻列表、推荐流、用户行为上报。

服务层用Flask写业务接口,但要继续拆成四个服务:用户服务、新闻服务、文本分析服务、推荐服务。文本分析服务和推荐服务不建议跟业务接口揉在同一个文件里,因为它们的运行节奏完全不同。业务接口面向用户请求,需要毫秒级响应;文本分析任务是批量离线计算,可能一次跑几十秒。如果混在一起,定时任务一跑,页面接口就会被卡住。

数据层用MySQL保存基础数据和中间计算结果。三个核心数据集合是:新闻内容及特征、用户信息及行为、用户画像及推荐结果。

数据流大致是这样:采集或导入新闻后,定时任务对新增内容做分词和向量化,结果写回新闻表;用户在前端产生浏览、收藏、不感兴趣等行为后,接口把行为写入行为表;推荐模块每天或每几小时跑一次,把生成结果写入推荐结果表;用户请求推荐接口时,后端直接读推荐结果表返回。

“离线计算 + 在线读取”是推荐系统非常经典的工程设计。文本分析耗时,用户请求不能等;推荐结果不需要实时变化,稍微延迟几分钟完全没问题。

2.3 一个清晰的工程目录

工程目录不用太复杂,但一定要让人看得懂。

news_recommend/ ├── app.py # Flask 入口 ├── config.py # 配置项 ├── models/ # 数据库模型与ORM ├── services/ │ ├── text_processor.py # 分词与TF-IDF计算 │ └── recommender.py # 推荐引擎 ├── apis/ # 接口路由层 ├── scripts/ │ └── import_news.py # 新闻数据导入与离线任务 └── web/ # Vue前端

这个结构的好处是,论文里的“模块设计”章节可以直接照着画图,每一层对应一个模块,评审老师扫一眼就知道系统边界在哪里。

3. 数据库设计:五张核心表和一套行为日志方案

3.1 基础表:用户、分类、新闻

用户表是系统的地基。字段不需要太多,但密码一定不要明文存,用哈希处理。

CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password_hash VARCHAR(128) NOT NULL, nickname VARCHAR(50), role VARCHAR(20) DEFAULT 'user', created_at DATETIME DEFAULT CURRENT_TIMESTAMP );

分类表可以很简单,但要注意新闻分类和推荐分类在语义上不一定完全一致。推荐系统本身还应该根据文本内容提取标签,分类只是其中一个维度。

新闻表是重点,它要承载文本分析的结果。我的设计是这样:

CREATE TABLE t_news ( id INT PRIMARY KEY AUTO_INCREMENT, category_id INT, title VARCHAR(200) NOT NULL, content TEXT NOT NULL, summary VARCHAR(500), source VARCHAR(100), keywords VARCHAR(255), tfidf_vector TEXT, publish_time DATETIME, is_active TINYINT DEFAULT 1, created_at DATETIME DEFAULT CURRENT_TIMESTAMP );

这里有几个容易忽略的点。keywords字段保存的是文本分析提取出来的关键词,比如“芯片|半导体|国产替代|算力”,前端展示新闻详情时可以直接展示,成为“文本分析可视化”的一部分。tfidf_vector保存的是文本向量,在十万级数据量下直接存JSON字符串问题不大;如果数据量再大,就拆一张单独的新闻向量表。content字段建议用TEXT而不是VARCHAR(1000),因为新闻正文长度很容易超。

3.2 行为表:推荐系统的“燃料”

没有行为数据,推荐算法就是个空壳。行为表是推荐系统的燃料库,设计时必须包含用户、新闻、行为类型、发生时间。

CREATE TABLE t_behavior ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, news_id INT NOT NULL, behavior_type VARCHAR(20) DEFAULT 'view', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_user_time (user_id, created_at) );

behavior_type取值建议就四种:view浏览、favorite收藏、share分享、unlike不感兴趣。不要搞太复杂,四种足够。

多数学生容易忽视的一点是:行为数据的价值密度不同。一条“收藏”行为背后代表的兴趣强度,远远大于一次随手点击。所以后面做用户画像时,不同行为要配不同权重。

3.3 用户画像表:把行为变成可解释的兴趣

用户画像表设计成“用户 + 关键词 + 权重”的三元组:

CREATE TABLE t_user_interest ( user_id INT NOT NULL, keyword VARCHAR(50) NOT NULL, weight FLOAT NOT NULL DEFAULT 0, updated_at DATETIME, PRIMARY KEY (user_id, keyword) );

这张表解决了两个问题。

第一,它让推荐变得可解释。管理员在后台能看到“用户U001最关注芯片、人工智能、半导体”,这就用数据回答了“推荐结果凭什么这么排”。

第二,它降低了在线计算压力。推荐时先读用户兴趣表里权重最高的前N个关键词,再拿这些关键词去匹配新闻,速度快很多,不用每次实时对几十万字做向量运算。

定时任务跑完后,把用户画像写入这张表。如果用户的行为变化了,权重自然变化,下一次定时任务更新即可。

对于推荐结果表同理,可以建一张t_recommend,字段包括用户ID、新闻ID、排序分、推荐理由、生成时间。推荐理由可以写成“因为你看了《国产芯片的新突破》,所以推荐《半导体行业观察》”。

4. 文本分析链路:从中文正文到可计算的向量

4.1 为什么推荐质量首先取决于文本分析

很多初学者会把注意力放在相似度算法上,其实推荐效果的上限在文本分析这一步就基本确定了。

打个比方:文本分析是把一篇篇文章压缩成一个“特征指纹”。如果指纹提取得不好,后面任何算法都是在拿垃圾特征做计算,结果自然不可靠。反过来,只要文本特征能准确反映新闻主题,哪怕只用最简单的余弦相似度,也能得到可观的推荐效果。

这个题目叫“文本分析的新闻推荐”,不是叫“协同过滤的新闻推荐”,原因就在这里。你的核心工作量必须放在文本分析上。

4.2 中文分词与停用词过滤

中文文本处理的第一步是分词。我这边直接使用jieba,配合自定义词典和停用词集合。

import jieba STOPWORDS = set([ "的", "了", "在", "是", "我", "有", "和", "就", "不", "人", "都", "一", "一个", "上", "也", "很", "到", "说", "要", "去", "你", "会", "着", "没有", "看" ]) def cut_content(text): words = jieba.lcut(text) filtered = [] for w in words: w = w.strip() if w and w not in STOPWORDS and len(w) > 1: filtered.append(w) return filtered

这里有两个细节值得说一下。

一个是len(w) > 1这个条件。中文新闻里单字词往往没有独立语义,过滤掉能明显减少噪声。但要注意,有些通用词比如“中”“美”“俄”在新闻里很关键,直接把单字全砍会丢失信息。我建议保留长度等于1的实义词,方法是把这类字词加入白名单。

另一个是自定义词典。jieba默认词典对专业术语识别不好,“深度学习”可能会被切成“深”“度”“学习”,可以主动加载自定义词典:

jieba.add_word("生成式人工智能") jieba.add_word("大语言模型")

这一步在论文里可以作为一个小创新点,叫“基于领域词典的中文分词优化”,不复杂但是能讲出道理。

4.3 TF-IDF特征与关键词提取

分词之后,就要把每一篇新闻变成一个向量。最常用也最容易讲清楚的是TF-IDF。

TF是词频,表示某个词在这篇文章里出现了多少次;IDF是逆文档频率,表示这个词语在整个语料库里有多稀罕。两者相乘,得到的权重既保留“文章中反复出现的主题词”,又降低了“的、了、是”这类高频无意义词的干扰。

sklearn的TfidfVectorizer可以直接用,但中文文本要传入自定义tokenizer:

from sklearn.feature_extraction.text import TfidfVectorizer vectorizer = TfidfVectorizer( tokenizer=cut_content, max_features=2000 ) corpus = [news1_content, news2_content, news3_content] tfidf_matrix = vectorizer.fit_transform(corpus)

max_features=2000的意思是只保留所有词汇表里DF值排名前2000的词。这个限制很重要。新闻语料里词汇量动辄上万,维度太高计算量会大好几倍,而且很多低频词只在某一篇文章里出现过,对推荐没有帮助。

除向量之外,还可以用jieba的extract_tags直接提取每篇文章的关键词,也很有趣:

from jieba.analyse import extract_tags tags = extract_tags(news_content, topK=10, withWeight=True) # 输出示例:[('芯片', 12.34), ('半导体', 8.91), ...]

这些关键词既存储到新闻表用于前端展示,也作为用户画像里的标签维度。这样就把高维向量空间压缩成了人们能看懂的低维标签空间,推荐结果也有了解释性。

4.4 余弦相似度:判断两篇新闻“像不像”

特征向量生成后,判断新闻之间相似度最直接的办法就是计算余弦相似度。

余弦相似度的几何含义就是两个向量在方向上的夹角。夹角越小越相似。实现起来很简单:

import numpy as np def cosine_similarity(vec_a, vec_b): a = np.array(vec_a, dtype=np.float64) b = np.array(vec_b, dtype=np.float64) norm = np.linalg.norm(a) * np.linalg.norm(b) if norm < 1e-9: return 0.0 return float(a.dot(b) / norm)

但如果你真在系统里对所有新闻两两计算相似度,那就要翻车了。一万条新闻两两组合约5000万次计算,哪怕一秒算1000对,也要十几个小时。

实际工程上的做法是“召回-排序”。先通过关键词重叠、分类过滤、标签匹配,快速筛出几百条候选新闻,再在候选集里算余弦相似度精细排序。这和搜索引擎先粗排再精排的思路是一样的。

5. 推荐引擎实现:从用户画像到TopN新闻列表

5.1 用户画像的权重策略

用户画像不能简单记成“用户看过哪几个分类”,而要把行为强度差异体现出来。我的权重设计如下:

行为类型权重说明
view0.3一次普通浏览
favorite0.6主动收藏,兴趣明显
share0.5分享行为,兴趣较强
unlike-0.8表达了不感兴趣

在此基础上还要加时间衰减。用户一周前的兴趣和昨天的兴趣,对当前推荐的影响应该不同。做法是给权重乘一个衰减系数:

from collections import defaultdict from datetime import datetime, timedelta def build_interest(user_id, behaviors): interest = defaultdict(float) now = datetime.now() for b in behaviors: base = {"view": 0.3, "favorite": 0.6, "share": 0.5, "unlike": -0.8}[b["behavior_type"]] days_ago = (now - b["created_at"]).days decay = 1 / (1 + days_ago * 0.1) for tag in b["keywords"]: interest[tag] += base * decay return interest

注意unlike是负权重的。如果一个用户对“娱乐八卦”连续点了三次不感兴趣,这个标签权重就会下降到负数,后续推荐时应该主动过滤掉。

5.2 推荐流程:召回、精排、重排、写入结果表

推荐引擎不要放到在线接口里实时计算,我的做法是写一个离线任务脚本,每天定时执行。

第一步,召回候选新闻。从用户画像里取权重最高的前20个关键词,找出包含这些关键词的新闻,再补充当天热门新闻和用户最近浏览新闻的相似新闻,作为候选池。

第二步,精排。对候选新闻计算综合打分:

def score_news(user_interest, news_keywords, hot_score, recency): score = 0.0 for kw in news_keywords: if kw in user_interest: score += user_interest[kw] score += hot_score * 0.2 # 兼顾热门程度 score += recency * 0.1 # 新近发布加分 return score

第三步,重排和过滤。同一个二级分类的新闻最多保留3条,避免推荐流全是同一个领域的文章;相似度太高的新闻直接去掉一条,保证内容多样性。

第四步,把最终结果写入t_recommend表,同时记录推荐理由。前端接口直接读取这张表就行了。

5.3 冷启动和兜底策略

推荐系统有一个绕不开的问题叫冷启动,具体到新闻推荐有三种情况。

第一种,新用户没有行为记录。这时候画像表是空的,直接做个性化推荐没有基础。兜底方案是推荐热门新闻,同时按新闻分类做一轮均匀抽取,让用户看到的推荐结果不至于全是同一个分类。

第二种,老用户长期不登录,兴趣已经过期。我的做法是让时间衰减系数把旧兴趣权重压得很低,同时提升热门新闻所占比例。

第三种,新新闻没有点击数据。新闻入库后立刻做文本分析、提取关键词和向量,这样即使没有用户行为,只要遇到了关键词匹配的用户画像,就能被推荐出去。

这三种情况在论文里值得单独写一个“推荐系统冷启动问题分析”小节,稍微提到就够,但这是评委很容易追问的细节。

6. 功能测试与推荐效果评估:怎么证明系统真的有用

6.1 功能测试怎么设计

功能测试要覆盖核心流程,而不是只测登录注册。我整理了一份用例表,可以直接参考:

用例编号测试场景操作步骤预期结果
T01新用户登录推荐注册新账号进入首页返回热门新闻,接口正常
T02用户浏览行为采集点击新闻详情并返回行为表新增一条view记录
T03用户点击“不感兴趣”点击某条新闻的不感兴趣按钮行为表记录unlike,后续推荐减少此类新闻
T04管理员导入新闻上传TXT或JSON格式新闻数据新闻表新增记录,文本分析任务能被执行
T05增量推荐更新手动运行离线推荐任务推荐结果表刷新,API返回新数据
T06冷启动兜底新用户无任何行为请求推荐返回热门榜,不返回空白页

功能测试不需要自动化工具,把每一步的操作结果截图,整理成测试报告放到论文里就是一份很好的“系统测试”章节材料。

6.2 离线评估:准确率、召回率、F1

推荐效果不能靠感觉说“还挺准”,最好能用数据表达。新闻推荐最常用的三个指标是准确率、召回率、F1。

准备方式是把用户行为数据切分:比如使用用户前80%的行为作为训练集,也就是用来构建用户画像;后20%的行为作为测试集,用来验证推荐结果是否命中。这种做法能够避免训练集和测试集交叉泄漏。

这里的“准确率”是推荐命中率,指推荐给用户的N条新闻里,有多少条出现在该用户的测试集行为中;“召回率”指测试集里用户真正感兴趣的新闻中,系统推荐出了多少条。

F1是二者的调和平均,公式是:

F1 = 2 * 准确率 * 召回率 / (准确率 + 召回率)

我跑过几轮对比,结果可以做成表格放进论文:

版本策略准确率召回率F1
v1仅热门推荐0.160.080.11
v2加入TF-IDF文本相似度0.270.140.18
v3用户画像 + 热度加权 + 多样性重排0.340.190.24

这个结果仅供参考,不同数据规模下数字会变,但趋势是很明显的:加入文本分析之后,效果比纯热门推荐肉眼可见地提高。

6.3 评估过程中踩过的坑

做评估时最容易犯一个错误:把用户测试期点过的新闻当“该推荐”的标准,但用户没点不代表不感兴趣。所以在论文里要写清楚,离线指标只能反映“系统是否命中用户真实行为”,并不能完全代表用户满意度。

我的做法是补一份人工评价:请5个人分别体验系统,看完推荐后给“感兴趣/一般/不感兴趣”三档打分。这个人工评价结果放到“用户满意度分析”小节,和离线指标互为补充。

不建议为了指标好看把推荐列表改成用户已经看过的新闻,那是作弊。推荐系统本来就该推荐用户没看过的内容,测试集里命中的部分才是有效推荐。

7. 论文结构、演示Demo和答辩问答的实战提醒

7.1 论文里要突出什么

这个题目的论文和技术报告,“系统设计”部分不要只画一个三层架构图就完事。特别要画一张“文本分析数据流图”,把从新闻正文到分词、关键词、TF-IDF向量、用户画像映射的每一个步骤都标清楚。

摘要里要强调的不应该是“我用了什么语言、什么框架”,而应是“如何利用文本分析技术提取新闻特征,结合用户行为构建兴趣画像,实现新闻的个性化推荐”。这样评审老师第一眼就知道你的核心工作在哪里。

论文目录大致可以是:绪论、相关技术介绍、需求分析、系统总体设计、推荐算法设计、系统实现、系统测试、总结与展望。其中“推荐算法设计”和“系统实现”这两章要写得最详细,算法设计里重点写文本特征计算和用户画像构建,系统实现里给关键代码片段和界面截图。

7.2 演示 Demo 的三条建议

第一,准备一个“有历史行为”的演示账号。现场注册的新账号一进去只能看到热门推荐,体现不出个性化效果,容易被误判成“没有推荐算法”。演示账号提前浏览过若干条科技类新闻,一进入推荐列表就会看到科技类相关内容,效果完全不同。

第二,切换对比。在演示时先打开“热门推荐”,再打开“个性化推荐”,让老师直观看到两组列表的差异,然后点开某条推荐新闻,显示它关联的用户兴趣关键词,例如“根据你关注的高通芯片新闻推荐了这篇半导体报道”。

第三,准备一组离线任务日志。现场快速演示一遍定时任务从“读取新新闻”到“更新推荐结果”的完整日志输出。这比单纯展示页面更能证明系统结构的完整性。

7.3 答辩高频问题及回答思路

答辩老师最喜欢问的几个问题,提前准备好答案。

问:为什么不用协同过滤? 答:协同过滤依赖大量用户行为记录,新闻场景下用户行为稀疏,冷启动问题严重;基于文本分析的内容推荐可以充分利用新闻本身的内容特征,在行为稀疏时也能给出合理结果,因此更贴合题目主题。

问:TF-IDF特征维度很高,性能怎么办? 答:离线任务预计算结果,在线接口只读取结果;同时在召回阶段通过关键词匹配缩小候选集,只有候选新闻才计算完整相似度,避免全量两两计算。

问:文本分析和向量模型相比是不是过时了? 答:向量模型近年很流行,但可解释性弱、计算资源要求高;本科毕业设计用TF-IDF配合关键词画像,能够清晰展示从文本到特征的完整链路,工程落地简单,效果也足够说明问题。如果学有余力,可以在论文展望里提“下一步可以引入向量模型做语义增强”。

问:新闻数据哪里来的? 答:使用公开可用的新闻语料和合规采集的少量演示数据,只做算法验证和教学演示,不涉及版权内容和商业用途。

我的最后一点体会

这个系统从头到尾做完,最值的不是那套代码,而是把“文本分析到底怎么支撑推荐”想明白了。很多同学纠结用哪个框架更高级、哪个算法更潮,其实在毕业设计这个尺度上,把文本分析链路走得完整、把推荐逻辑说得自洽、把数据表设计得规范,比单纯调出一个高指标更管用。我也不建议直接拿别人写好的成品去交,老师只要顺着你的接口、表结构、相似度计算过程追问几层,没有亲自做过的人很快就露馅。自己从头把系统跑通,哪怕界面朴素一点,答辩时那股踏实感也是装不出来的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询