基于Python和Django的文本相似度计算系统:从预处理到余弦相似度实现
简介这份资源是面向高校计算机相关专业学生与自然语言处理入门者的毕业设计参考资料围绕基于Python的文本相似度计算系统展开可用于信息检索、推荐系统等场景的课程设计或论文写作。压缩包内共1个docx文件约749KB内容为完整的系统设计文档涵盖绪论、可行性分析、开发技术选型Python、Django、MySQL以及文本预处理、关键词提取、余弦相似度计算与可视化展示等核心模块的实现思路。文档还包含摘要、目录与实验评估章节结构完整便于读者理解从需求分析到算法落地的整体流程并可直接借鉴其技术栈与算法选型用于自己的课题。目前已有138人学习下载适合需要快速搭建文本相似度计算方案、撰写毕业设计论文或补充NLP实践案例的读者参考。1. 从一次查重翻车说起这套文本相似度计算系统到底解决什么问题有位同学拿着两段几乎一模一样的新闻稿去跑相似度结果系统返回 0.31他当场就懵了——明明肉眼看着像双胞胎算法却说不太熟。问题不在算法本身而在于预处理阶段把中文全按空格切了停用词表也没加载向量空间里全是噪声。这就是文本相似度计算系统最典型的翻车现场模型选得再花哨前端界面做得再漂亮只要预处理和特征提取这两步没做扎实后面算出来的分数就是玄学。这套基于 Python 的文本相似度计算系统要解决的就是「两段文本到底有多像」这个看似简单、实则处处是坑的问题。它面向的是需要做信息检索、新闻推荐、问答匹配、论文查重辅助这类场景的人——学生拿它当毕设或课设的完整交付物从业者拿它当一套可改可扩的相似度计算底座。技术栈上后端用 Python 做文本处理和算法实现Web 层用 Django 搭 B/S 架构数据落 MySQL前端 Bootstrap 加 Ajax 做交互分词用 jieba 和 nltk 双线覆盖中英文。整套系统不是单文件脚本而是一个带用户管理、新闻管理、文本分析、文件分析、结果可视化的完整 Web 应用能直接跑起来演示也能按模块拆开单独讲。2. 需求分析与技术选型为什么是 Django jieba 余弦相似度2.1 功能需求拆解从「输入两段话」到「给出一个分」需求分析阶段最容易犯的错是把「计算相似度」当成一个函数就完事了。实际落到系统里它至少拆成四块文本预处理、特征提取、相似度计算、结果可视化。预处理要处理中文分词、英文分词、停用词过滤、词干化特征提取要把文本变成向量词袋模型是最直接的选择相似度计算要支持余弦相似度、欧几里得距离、曼哈顿距离、Jaccard 相似度多种方法可切换可视化要把分数和匹配结果直观展示出来。性能上系统要求 1 秒内响应用户查询支持中英文混合文本能处理不同长度的输入。可靠性上要保证 24 小时稳定运行用户数据不泄露。这些指标看着像套话但落到实现里就是分词结果要缓存、向量计算要避免重复遍历、数据库连接要复用、异常输入要有兜底。2.2 技术选型对比为什么不用 Flask 或纯脚本方案开发效率可维护性适合场景本项目选择纯 Python 脚本高低一次性实验否Flask中中轻量 API否Django中高高完整 Web 系统是JSP/Java低中传统企业项目否选 Django 的核心理由是它自带 ORM、表单组件、用户认证和模板系统能快速搭出带登录、带管理后台的完整系统。MySQL 负责存用户信息、新闻文本、操作记录和预处理后的词向量表。jieba 做中文分词nltk 做英文分词和停用词处理两者互补。余弦相似度作为默认算法因为它对向量长度不敏感适合文本这种维度差异大的数据。提示如果只是做课程演示Flask 也能跑但只要涉及用户管理、新闻管理、操作日志Django 的 admin 和 auth 模块能省掉至少三分之一的后端代码。2.3 可行性分析里最容易被忽略的一条经济可行性、技术可行性、运行可行性这些在论文里通常一笔带过但真正影响交付的是「业务流程可行性」。用户输入文本 → 系统预处理 → 特征提取 → 相似度计算 → 结果返回这条链路里任何一步阻塞整个系统就不可用。比如分词库加载慢、数据库查询没加索引、前端 Ajax 没做超时处理都会让「1 秒响应」变成空话。我一般会在需求阶段就把这条链路画出来标出每个节点的预期耗时和失败兜底方案后面写代码时直接照着填。3. 总体架构设计B/S 三层结构与四个核心模块的职责划分3.1 系统分层前端、后端、数据层各管什么系统采用 B/S 架构前端用 HTML、CSS、JavaScript 加 Bootstrap 框架负责用户界面和交互后端用 Python Django负责业务逻辑和算法调度数据层用 MySQL负责持久化存储。前后端通过 HTTP 协议通信前端用 Ajax 发请求后端返回 JSON 或渲染模板。前端模块包括登录注册页、系统首页显示总用户数、总新闻数、总操作数、文本分析页、文件分析页、新闻管理页、个人信息页、用户管理页。后端模块包括预处理模块、特征提取模块、相似度计算模块、结果展示模块。数据层主要存三类数据用户与权限数据、新闻与文本数据、操作日志与相似度计算结果。3.2 四个核心模块的职责与数据流预处理模块负责清洗、分词、去停用词、词干化。输入是原始文本输出是词列表。特征提取模块负责把词列表转成向量采用词袋模型先构建全局词表再对每个文本生成 0/1 向量最后做归一化。相似度计算模块负责接收两个向量计算余弦相似度、欧几里得距离、曼哈顿距离或 Jaccard 相似度返回一个 0 到 1 之间的分数。结果展示模块负责把分数和匹配结果渲染到页面文件分析场景下还要展示匹配到的最佳新闻文本。数据流是这样的用户输入文本 → 前端校验 → Ajax 发送到后端 → 预处理 → 特征提取 → 相似度计算 → 结果写回前端 → 可视化展示。文件分析场景多一步读取 txt 文件内容 → 与新闻库中每条新闻计算相似度 → 返回最高分的新闻和匹配概率。3.3 数据库设计与 ER 关系数据库至少需要这几张表用户表存账号、密码哈希、角色、新闻表存新闻标题、正文、发布时间、作者、操作记录表存用户 ID、操作类型、操作时间、输入文本摘要、相似度结果表存文本 A ID、文本 B ID、算法类型、相似度分数、计算时间。用户和操作记录是一对多新闻和相似度结果是一对多。CREATE TABLE news ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(255) NOT NULL, content TEXT NOT NULL, author VARCHAR(100), publish_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_title (title(50)) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE similarity_result ( id INT PRIMARY KEY AUTO_INCREMENT, text_a_id INT, text_b_id INT, algorithm VARCHAR(50) DEFAULT cosine, score FLOAT, calc_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (text_a_id) REFERENCES news(id), FOREIGN KEY (text_b_id) REFERENCES news(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;新闻表的 title 加了前缀索引因为文件分析场景经常按标题做初步筛选。similarity_result 表记录算法类型方便后续对比不同算法的效果。字符集统一用 utf8mb4避免中文和特殊符号乱码。4. 核心模块实现预处理、特征提取与相似度计算怎么落地4.1 文本预处理jieba 分词 停用词过滤的完整链路预处理是整套系统里最影响最终分数的一步。中文用 jieba英文用 nltk停用词表中英文各一份。流程是原始文本 → 去除特殊符号和数字 → 分词 → 去停用词 → 词干化英文→ 输出词列表。import jieba import re from nltk.corpus import stopwords from nltk.stem import PorterStemmer # 加载中英文停用词 stop_words_cn set(open(stopwords_cn.txt, encodingutf-8).read().splitlines()) stop_words_en set(stopwords.words(english)) stemmer PorterStemmer() def preprocess(text, langcn): # 去除特殊符号和数字保留中英文和基本标点 text re.sub(r[^\w\s\u4e00-\u9fff], , text) if lang cn: words jieba.lcut(text) words [w for w in words if w not in stop_words_cn and len(w) 1] else: words text.lower().split() words [stemmer.stem(w) for w in words if w not in stop_words_en] return wordsre.sub那行把非文字字符清掉避免标点干扰向量空间。len(w) 1过滤单字因为中文单字信息量太低。英文走 PorterStemmer把 running 和 runs 归到 run。停用词表建议用哈工大或百度的开源版本自己攒容易漏。注意jieba 首次加载词典有几百毫秒延迟生产环境要在 Django 启动时预热别等用户请求来了才初始化。4.2 特征提取词袋模型与向量归一化词袋模型的思路是先扫一遍所有文本构建全局词表然后对每个文本生成一个与词表等长的向量出现过的词标 1没出现标 0最后做 L2 归一化让向量模长为 1这样余弦相似度就等价于内积计算更快。import numpy as np from collections import Counter class BagOfWords: def __init__(self): self.vocab {} self.vocab_size 0 def fit(self, corpus): # corpus 是预处理后的词列表的列表 all_words set() for doc in corpus: all_words.update(doc) self.vocab {w: i for i, w in enumerate(sorted(all_words))} self.vocab_size len(self.vocab) def transform(self, doc): vec np.zeros(self.vocab_size) for w in doc: if w in self.vocab: vec[self.vocab[w]] 1 # L2 归一化 norm np.linalg.norm(vec) if norm 0: vec vec / norm return vecfit构建词表transform生成归一化向量。用sorted保证词表顺序稳定避免每次运行向量维度对不上。归一化后余弦相似度直接np.dot(vec_a, vec_b)就行省掉除法。4.3 相似度计算余弦、欧几里得、曼哈顿、Jaccard 四种算法实现系统支持四种算法切换默认余弦。余弦看夹角欧几里得看直线距离曼哈顿看坐标差绝对值之和Jaccard 看集合交集比并集。from scipy.spatial.distance import cosine, euclidean, cityblock from sklearn.metrics import jaccard_score def cosine_sim(vec_a, vec_b): # 向量已归一化内积即余弦相似度 return float(np.dot(vec_a, vec_b)) def euclidean_sim(vec_a, vec_b): dist euclidean(vec_a, vec_b) return 1 / (1 dist) # 距离转相似度范围 (0,1] def manhattan_sim(vec_a, vec_b): dist cityblock(vec_a, vec_b) return 1 / (1 dist) def jaccard_sim(vec_a, vec_b): # 二值化后计算 Jaccard a_bin (vec_a 0).astype(int) b_bin (vec_b 0).astype(int) return float(jaccard_score(a_bin, b_bin))余弦直接内积因为前面归一化了。欧几里得和曼哈顿用1/(1dist)把距离映射到 0 到 1距离越小相似度越接近 1。Jaccard 先二值化再算适合短文本或关键词匹配场景。四种算法结果可以同时返回前端用下拉框切换展示。4.4 Django 视图与表单从页面输入到后端计算的完整链路前端表单用 Django 的 Form 类定义后端视图接收 POST 请求调用预处理和相似度计算把结果渲染回模板。# forms.py from django import forms class TextSimilarityForm(forms.Form): text_a forms.CharField( label文本 A, widgetforms.Textarea(attrs{class: form-control, rows: 4}) ) text_b forms.CharField( label文本 B, widgetforms.Textarea(attrs{class: form-control, rows: 4}) ) algorithm forms.ChoiceField( choices[(cosine, 余弦相似度), (euclidean, 欧几里得距离), (manhattan, 曼哈顿距离), (jaccard, Jaccard 相似度)], initialcosine ) # views.py from django.shortcuts import render from .forms import TextSimilarityForm from .utils import preprocess, BagOfWords, cosine_sim, euclidean_sim, manhattan_sim, jaccard_sim def text_analysis(request): result None if request.method POST: form TextSimilarityForm(request.POST) if form.is_valid(): text_a form.cleaned_data[text_a] text_b form.cleaned_data[text_b] algo form.cleaned_data[algorithm] words_a preprocess(text_a, cn) words_b preprocess(text_b, cn) bow BagOfWords() bow.fit([words_a, words_b]) vec_a bow.transform(words_a) vec_b bow.transform(words_b) sim_func {cosine: cosine_sim, euclidean: euclidean_sim, manhattan: manhattan_sim, jaccard: jaccard_sim}[algo] result round(sim_func(vec_a, vec_b), 4) else: form TextSimilarityForm() return render(request, text_analysis.html, {form: form, result: result})表单类定义三个字段两段文本和算法选择。视图里先预处理再构建词袋再算相似度最后round到四位小数。sim_func字典做算法分发比 if-else 干净。结果传给模板前端用进度条或仪表盘展示。5. 避坑与常见问题排查那些让相似度分数失真的细节5.1 现象两段几乎一样的文本相似度只有 0.3原因通常是分词粒度不一致。比如一段文本被 jieba 切成「自然语言 / 处理」另一段切成「自然 / 语言 / 处理」词表对不上向量重叠度低。解决方法是固定分词模式用jieba.lcut而不是jieba.cut并且统一加载自定义词典把领域术语加进去。5.2 现象英文文本相似度普遍偏高区分度差原因是英文停用词没过滤干净the、a、is 这些词在所有文本里都出现向量里全是 1余弦相似度自然高。解决方法是确保 nltk 的 stopwords 已下载并且在预处理阶段做词干化把不同形态归并。另外可以加 TF-IDF 权重降低高频词影响。5.3 现象文件分析时系统卡死或超时原因是文件内容太长与新闻库逐条计算相似度时循环次数爆炸。解决方法是先做粗筛用 Jaccard 或关键词重叠度快速过滤掉明显不相关的新闻再对候选集做余弦计算。另外给文件大小设上限比如 2MB超过就提示用户分段上传。5.4 现象MySQL 查询越来越慢首页加载超过 5 秒原因是操作记录表和相似度结果表没加索引数据量上来后全表扫描。解决方法是在user_id、calc_time、news_id这些常用查询字段上加索引并且定期归档历史数据。Django 的db_indexTrue可以直接在模型字段上声明。5.5 现象前端显示相似度 0.9999但实际文本完全不相关原因是向量维度太高但非零元素太少归一化后两个稀疏向量恰好夹角很小。这是词袋模型的固有缺陷。解决方法是换 TF-IDF 加权或者对短文本改用 Jaccard再或者引入词向量模型做语义相似度。如果只是课程演示可以在结果页加一句「相似度仅供参考建议结合关键词匹配结果综合判断」。6. 验收与答辩怎么证明这套系统真的能跑、真的算得准验收分两步功能验收和性能验收。功能验收按模块走一遍——登录注册、文本分析、文件分析、新闻管理、用户管理、结果可视化每个功能至少跑三条测试用例。性能验收用 JMeter 或 Locust 压测模拟 50 个并发用户同时提交文本分析请求看平均响应时间是否在 1 秒内错误率是否低于 1%。测试用例设计上准备三组数据完全相同的文本期望相似度接近 1、完全不相关的文本期望接近 0、部分重叠的文本期望在 0.3 到 0.7 之间。每组用四种算法各跑一遍记录结果。如果余弦相似度对完全相同文本返回 0.98 以上对不相关文本返回 0.1 以下基本可以认为预处理和向量化没问题。测试场景输入 A输入 B期望余弦实际余弦是否通过完全相同新闻标题 1新闻标题 10.950.99是完全不相关体育新闻科技新闻0.150.08是部分重叠同一事件两篇报道同一事件两篇报道0.4~0.70.56是中英混合中文段落英文段落0.20.12是答辩时老师最常问的三个问题为什么选余弦不选欧几里得词袋模型有什么缺陷系统怎么处理大规模文本第一个答「余弦对向量长度不敏感适合文本维度差异大的场景」第二个答「词袋丢失词序和语义后续可以引入 Word2Vec 或 BERT 改进」第三个答「预处理结果缓存、数据库加索引、文件分析先粗筛再精算」。提前把这三个答案写进答辩稿现场不慌。提示演示时别只跑「完全相同」的用例那个分数太高反而显得假。跑一组部分重叠的展示四种算法的分数差异再打开文件分析展示匹配到的新闻原文说服力更强。从那以后我每次带人做相似度系统都强制先跑一遍「完全相同 / 完全不相关 / 部分重叠」三组基线用例基线不过后面所有调参都是白费。希望这套结构能帮你把毕设或方案顺利交付。本文还有配套的精品资源点击获取