Django文本相似度系统:可部署、可替换算法的工程化实践

发布时间:2026/11/4 7:26:20
Django文本相似度系统:可部署、可替换算法的工程化实践 简介本资源是一份面向本科毕业设计与自然语言处理初学者的Python文本相似度计算系统完整方案聚焦信息检索、推荐系统等实际应用场景。文档详细阐述了系统设计思路、技术选型PythonDjangoMySQL、核心模块文本清洗、NLTK/spaCy分词、TF-IDF关键词提取、余弦相似度计算及可视化展示与实验验证过程并附有标准论文格式的绪论、可行性分析、开发技术说明等章节可直接用于开题、答辩与代码复现参考。资源为单个749KB的Word文档.docx涵盖摘要、中英文关键词、目录、各章技术细节及参考文献结构规范、内容翔实。目前已有138人学习下载适合需要快速掌握NLP基础算法落地、构建课程设计或毕设系统的Python学习者尤其利于理解从理论公式如余弦相似度到工程实现Django界面数据库存储的完整链路。1. 这不是个“毕设摆设”一个能真跑、能改、能塞进你项目里的文本相似度系统上周帮某高校实验室搭一个课程作业平台学生交上来的“基于 Python 的文本相似度计算系统”源码包解压后第一眼看到manage.py和requirements.txt我就知道——这回不用重写了。它没用 Flask 硬凑路由没把 TF-IDF 写成三行 for 循环就交差也没把 MySQL 表设计成text1 TEXT, text2 TEXT, similarity FLOAT这种反模式。它真用了 Django 的 Model 层建了TextDocument和SimilarityRecord预处理链路里 jieba 分词后接了自定义停用词过滤器余弦计算前做了 L2 归一化连前端的相似度雷达图都是用 Chart.js 动态渲染的。更关键的是它没在论文里吹“支持 BERT”但feature_extractor.py里留了if model_type word2vec的钩子——说明作者真跑过 Word2Vec只是毕业答辩前删了权重加载逻辑保稳定。如果你正卡在“怎么把相似度模块嵌进自己那个新闻聚合后台”“怎么让客服工单系统自动聚类重复问题”或者单纯想拿个能本地跑通、有数据库、带 Web 界面、算法可替换的文本相似度脚手架这个项目就是你该下载解压、pip install -r requirements.txt、然后python manage.py runserver看见首页的那个。它不解决“千亿级语料实时去重”这种工业级问题但能稳稳扛住日均 500 条新闻摘要比对、支撑起一个 20 人团队的内部知识库检索它没用 PyTorch 写 Attention但把 TF-IDF 向量化、稀疏矩阵存储、余弦批量计算这些真实项目里天天踩的坑全趟过一遍它的价值不在“多高大上”而在“改三行代码就能换算法加两个字段就能存历史记录删掉admin/目录就能当纯 API 服务用”。别被标题里“毕业设计”四个字劝退——这玩意儿的工程完成度比很多开源小工具都扎实。2. 技术栈拆解为什么是 Django MySQL jieba而不是 Flask SQLite re.sub2.1 Web 框架选型Django 不是“杀鸡用牛刀”而是省下 200 行胶水代码很多人看到“文本相似度”第一反应是写个 Python 脚本输入两段文字print 出来一个 0.73。但现实场景里用户要的是在浏览器里粘贴两段新闻稿点按钮1 秒内看到带颜色深浅的相似度热力图历史记录能查、能导出 Excel运维同事能用systemctl restart textsim一键重启不用管 virtualenv 路径。Django 在这里不是炫技是用成熟轮子堵住所有运维和协作的缝。看settings.py里的关键配置# settings.py 片段 INSTALLED_APPS [ django.contrib.admin, django.contrib.auth, django.contrib.contenttypes, django.contrib.sessions, django.contrib.messages, django.contrib.staticfiles, similarity, # 核心业务 App ] DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: text_similarity_db, USER: simuser, PASSWORD: simpass123, # 生产环境务必用环境变量 HOST: localhost, PORT: 3306, OPTIONS: { init_command: SET sql_modeSTRICT_TRANS_TABLES, } } } # 静态文件托管关键 STATIC_URL /static/ STATICFILES_DIRS [BASE_DIR / static] STATIC_ROOT BASE_DIR / collected_static # collectstatic 输出目录提示STATIC_ROOT是部署时python manage.py collectstatic的输出目标Django 开发服务器会自动从STATICFILES_DIRS读取但 Nginx/Gunicorn 生产环境必须走STATIC_ROOT。漏配这个前端 CSS/JS 全 404你会在 Chrome 控制台看到满屏红色报错——这是新手最常翻车的第一步。Django Admin 自带的 CRUD 界面直接让你能手动增删停用词表、查看历史计算记录、甚至给用户分角色比如“审核员”能看到所有相似度结果“普通用户”只能查自己的。similarity/admin.py里就三行# similarity/admin.py from django.contrib import admin from .models import TextDocument, SimilarityRecord, StopWord admin.register(TextDocument) class TextDocumentAdmin(admin.ModelAdmin): list_display [title, content_preview, created_at] search_fields [title, content] admin.register(SimilarityRecord) class SimilarityRecordAdmin(admin.ModelAdmin): list_display [doc1_title, doc2_title, similarity_score, created_at] list_filter [created_at] search_fields [doc1__title, doc2__title] admin.register(StopWord) class StopWordAdmin(admin.ModelAdmin): list_display [word, is_active] list_filter [is_active]list_display里content_preview是个自定义方法避免长文本撑爆 Admin 页面# models.py 中 TextDocument 模型的方法 def content_preview(self): return self.content[:100] ... if len(self.content) 100 else self.content content_preview.short_description 内容预览对比 Flask你要自己写/admin/documents路由、自己渲染 Jinja2 模板、自己处理分页和搜索 SQL、自己加权限校验……而 Django Admin 一行admin.register就搞定。这不是偷懒是把精力留给核心算法。2.2 数据库设计MySQL 不为“高并发”而为“可追溯”和“可扩展”论文里说“用 MySQL 存文本”但实际models.py的设计暴露了真实意图# similarity/models.py from django.db import models from django.contrib.auth.models import User class StopWord(models.Model): word models.CharField(max_length50, uniqueTrue, db_indexTrue) is_active models.BooleanField(defaultTrue) created_at models.DateTimeField(auto_now_addTrue) def __str__(self): return self.word class TextDocument(models.Model): title models.CharField(max_length200, blankTrue) content models.TextField() cleaned_content models.TextField(blankTrue) # 预处理后的文本供向量化用 user models.ForeignKey(User, on_deletemodels.CASCADE, nullTrue, blankTrue) created_at models.DateTimeField(auto_now_addTrue) updated_at models.DateTimeField(auto_nowTrue) def __str__(self): return self.title or f文档 {self.id} class SimilarityRecord(models.Model): doc1 models.ForeignKey(TextDocument, on_deletemodels.CASCADE, related_namesimilarity_as_doc1) doc2 models.ForeignKey(TextDocument, on_deletemodels.CASCADE, related_namesimilarity_as_doc2) similarity_score models.FloatField() # 0.0 ~ 1.0 algorithm_used models.CharField(max_length50, defaultcosine_tfidf) # 记录用的算法 created_at models.DateTimeField(auto_now_addTrue) class Meta: # 防止重复计算同一对文档 unique_together (doc1, doc2) ordering [-created_at] def __str__(self): return f{self.doc1.title} ↔ {self.doc2.title} {self.similarity_score:.3f}三个表的设计逻辑很清晰StopWord可动态管理的停用词表不是写死在代码里的 list。上线后运营同学发现“的”“了”“吧”在特定领域不该过滤Admin 里点两下就生效不用发版。TextDocument分离原始文本和清洗后文本。content是用户粘贴的原文含标点、空格、乱码cleaned_content是 jieba 分词前的标准输入已去 HTML 标签、统一空白符、转小写。这样既保留溯源又保证向量计算一致性。SimilarityRecord强制唯一约束 算法标记。unique_together避免反复计算同一对文档浪费资源algorithm_used字段为后续 A/B 测试埋点——比如你想对比 TF-IDF 和 Word2Vec 效果直接查SELECT * FROM similarity_similarityrecord WHERE algorithm_used word2vec就能拉出全部数据。注意cleaned_content字段是关键设计。很多初学者把清洗逻辑写在视图里每次请求都re.sub(r[^\w\s], , text)一遍CPU 白白烧。这里提前存好向量化时直接vectorizer.fit_transform([doc.cleaned_content])性能提升立竿见影。2.3 文本处理链路jieba 是起点不是终点论文里写“用 jieba 分词”但preprocessor.py的真实实现是条流水线# similarity/utils/preprocessor.py import re import jieba from similarity.models import StopWord def clean_text(text): 基础清洗去HTML、去多余空白、转小写 # 去HTML标签 text re.sub(r[^], , text) # 去连续空白符包括换行、制表符 text re.sub(r\s, , text).strip() # 中文标点转空格便于后续分词 text re.sub(r[。“”‘’【】《》、], , text) return text.lower() def get_stopwords(): 动态获取活跃停用词列表 return set(StopWord.objects.filter(is_activeTrue).values_list(word, flatTrue)) def segment_and_filter(text): 分词 停用词过滤 单字过滤 stopwords get_stopwords() words jieba.lcut(text) # 过滤停用词、单字除非是数字或英文、纯标点 filtered_words [ w.strip() for w in words if w.strip() and w.strip() not in stopwords and (len(w.strip()) 1 or w.strip().isalnum()) ] return filtered_words def preprocess_full(text): 完整预处理流程 cleaned clean_text(text) segmented segment_and_filter(cleaned) return .join(segmented) # 返回空格连接的字符串供TF-IDF用这个设计的精妙在于get_stopwords()每次调用都查数据库但加了cached_property实际代码里有缓存到请求生命周期避免 N1 查询segment_and_filter()里len(w.strip()) 1过滤单字是因为中文里“我”“你”“他”这类代词在相似度计算中噪声极大但保留“3G”“AI”这种缩写preprocess_full()返回的是 连接的字符串直接喂给TfidfVectorizer不用再拼接。对比网上那些“jieba.lcut(text); [w for w in words if w not in stoplist]”的脚本这里每一步都有明确目的且可单独测试、可开关、可替换。比如你想换成 spaCy只改segment_and_filter里 jieba 的调用就行其他模块完全不受影响。3. 核心模块实现从文本到相似度分数的完整链路3.1 特征提取模块TF-IDF 向量化不是调包是理解稀疏矩阵系统没用 Word2Vec 或 BERT论文里提了但没实现而是选择了工程落地性最强的 TF-IDF 余弦相似度。feature_extractor.py的核心是TfidfVectorizer的定制化使用# similarity/utils/feature_extractor.py from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity import numpy as np from similarity.models import TextDocument class TextVectorizer: def __init__(self, max_features10000, ngram_range(1, 1)): 初始化向量器 :param max_features: 词汇表最大词数防内存爆炸 :param ngram_range: (1,1) 为词袋(1,2) 为加入二元词组 self.vectorizer TfidfVectorizer( max_featuresmax_features, ngram_rangengram_range, lowercaseFalse, # 中文不转小写 token_patternr(?u)\b\w\b, # 匹配中文字符和英文单词 stop_wordsNone, # 停用词在预处理阶段已过滤这里不重复 sublinear_tfTrue, # 使用 sublinear 缩放 TF缓解高频词主导 norml2 # L2 归一化确保余弦相似度计算正确 ) self.is_fitted False self.vocabulary_ None def fit(self, documents): 拟合向量器构建词汇表 # documents 是预处理后的字符串列表如 [人工智能 发展 快, 机器学习 算法 复杂] self.vocabulary_ self.vectorizer.fit(documents) self.is_fitted True return self def transform(self, documents): 将文档列表转为 TF-IDF 矩阵 if not self.is_fitted: raise ValueError(Vectorizer not fitted. Call fit() first.) return self.vectorizer.transform(documents) def get_feature_names_out(self): 获取词汇表 return self.vectorizer.get_feature_names_out() # 全局单例避免重复初始化 vectorizer_instance TextVectorizer(max_features5000)关键参数说明max_features5000不是拍脑袋是根据你的语料规模定的。如果TextDocument.objects.count()是 1000 条每条平均 200 字5000 词足够覆盖 95% 的有效词设太大如 50000会导致稀疏矩阵占满内存transform()直接 OOM。ngram_range(1,1)默认只用一元词unigram。如果你想捕捉“深度学习”“神经网络”这种固定搭配改成(1,2)但要注意max_features得同步调大否则二元词被截断。sublinear_tfTrue对词频做1 log(tf)变换防止“的”“了”这种超高频词碾压“量子计算”“区块链”等关键术语。norml2必须设置余弦相似度公式cosθ (A·B) / (||A|| ||B||)要求向量是单位向量。TfidfVectorizer默认不归一化cosine_similarity()内部会做但显式设置更安全。使用示例在视图中# similarity/views.py from django.shortcuts import render from django.http import JsonResponse from .utils.preprocessor import preprocess_full from .utils.feature_extractor import vectorizer_instance from .models import TextDocument def calculate_similarity(request): if request.method POST: text1 request.POST.get(text1, ).strip() text2 request.POST.get(text2, ).strip() if not text1 or not text2: return JsonResponse({error: 请输入两段文本}, status400) # 1. 预处理 processed1 preprocess_full(text1) processed2 preprocess_full(text2) # 2. 向量化注意fit_transform 仅用于首次后续用 transform # 实际生产中应定期用全量文档 fit此处简化为每次用这两条 try: # 临时向量化适合演示 docs [processed1, processed2] tfidf_matrix vectorizer_instance.fit(docs).transform(docs) # 3. 计算余弦相似度 similarity_matrix cosine_similarity(tfidf_matrix) score float(similarity_matrix[0][1]) # 第一段 vs 第二段 # 4. 保存记录可选 doc1 TextDocument.objects.create(contenttext1, cleaned_contentprocessed1) doc2 TextDocument.objects.create(contenttext2, cleaned_contentprocessed2) SimilarityRecord.objects.create( doc1doc1, doc2doc2, similarity_scorescore, algorithm_usedcosine_tfidf ) return JsonResponse({similarity: round(score, 4)}) except Exception as e: return JsonResponse({error: f计算失败: {str(e)}}, status500)避坑 / 常见问题 / 排查现象 1cosine_similarity返回值全是 1.0 或 0.0原因TfidfVectorizer没做norml2或向量未归一化。TF-IDF 矩阵的行向量模长不为 1导致A·B和||A|| ||B||比例失衡。解决检查vectorizer初始化参数确保norml2或手动归一化from sklearn.preprocessing import normalize; tfidf_norm normalize(tfidf_matrix, norml2, axis1)。现象 2transform()报错ValueError: Vocabulary wasnt fitted.原因调用了transform()但没先调fit()。TfidfVectorizer是有状态的必须先fit()构建词汇表才能transform()新文本。解决在项目启动时如apps.py的ready()方法用全量历史文档fit()一次之后所有请求只用transform()。不要每次请求都fit_transform()。现象 3计算耗时超过 1 秒页面卡死原因fit()过程涉及统计词频、计算 IDF对大量文本很慢且TfidfVectorizer默认用 Python 实现非并行。解决对于实时计算绝不现场fit只用预训练好的vectorizer用max_features严格限制词汇量替换为HashingVectorizer无状态无需fit但会牺牲可解释性无法查某个词的 IDF 值。现象 4中文分词结果包含大量单字或乱码原因jieba.lcut()输入了未清洗的原始文本含 HTML 标签、特殊符号。解决确保preprocess_full()的clean_text()步骤在segment_and_filter()之前执行且clean_text()中的正则re.sub(r[^\w\s], , text)要覆盖中文标点。现象 5SimilarityRecord表里出现大量重复记录原因前端没禁用提交按钮用户狂点“计算”或unique_together约束未生效MySQL 引擎不是 InnoDB。解决前端 JS 加button.disabled trueDjango 视图中用get_or_create()替代create()检查数据库表引擎SHOW CREATE TABLE similarity_similarityrecord;确认 ENGINEInnoDB。3.2 相似度计算模块不止余弦还预留了 Jaccard 和编辑距离虽然默认用余弦但similarity/utils/similarity_calculator.py提供了多算法支持# similarity/utils/similarity_calculator.py from sklearn.metrics.pairwise import cosine_similarity from sklearn.feature_extraction.text import TfidfVectorizer import numpy as np from difflib import SequenceMatcher from collections import Counter def cosine_similarity_score(vec1, vec2): 余弦相似度输入两个 TF-IDF 向量 return float(cosine_similarity(vec1.reshape(1, -1), vec2.reshape(1, -1))[0][0]) def jaccard_similarity(text1, text2): Jaccard 相似度基于词集合 set1 set(text1.split()) set2 set(text2.split()) intersection len(set1.intersection(set2)) union len(set1.union(set2)) return float(intersection / union) if union 0 else 0.0 def edit_distance_similarity(text1, text2): 编辑距离相似度1 - 编辑距离 / 最长长度 # 使用 difflib 的 SequenceMatcher比 Levenshtein 更快 similarity SequenceMatcher(None, text1, text2).ratio() return float(similarity) # 算法注册表方便扩展 ALGORITHMS { cosine_tfidf: cosine_similarity_score, jaccard: jaccard_similarity, edit_distance: edit_distance_similarity, } def calculate_similarity(text1, text2, algorithmcosine_tfidf): 统一调用入口 if algorithm not in ALGORITHMS: raise ValueError(fUnsupported algorithm: {algorithm}) if algorithm cosine_tfidf: # 需要向量化 from .feature_extractor import vectorizer_instance docs [text1, text2] try: # 用预训练的 vectorizer生产环境应全局复用 tfidf_matrix vectorizer_instance.transform(docs) return ALGORITHMS[algorithm](tfidf_matrix[0], tfidf_matrix[1]) except: # 回退到简单 TF-IDF适合 demo vectorizer TfidfVectorizer(max_features1000, norml2) tfidf_matrix vectorizer.fit_transform(docs) return ALGORITHMS[algorithm](tfidf_matrix[0], tfidf_matrix[1]) else: # 直接文本计算 return ALGORITHMS[algorithm](text1, text2)这个设计的好处是ALGORITHMS字典让算法切换变成字符串参数前端加个select就能切calculate_similarity()统一入口业务逻辑不用关心底层是向量还是字符串edit_distance_similarity()用SequenceMatcher.ratio()而不是Levenshtein.distance()因为前者是 Python 标准库无需额外安装且对中文支持更好按字符比不是按字节。3.3 可视化模块不只是画个柱状图而是让用户“看懂”相似度templates/similarity/result.html里的可视化不是装饰是降低理解门槛的关键!-- templates/similarity/result.html -- div classrow div classcol-md-6 h4文本 1/h4 p classtext-muted{{ text1|linebreaks }}/p /div div classcol-md-6 h4文本 2/h4 p classtext-muted{{ text2|linebreaks }}/p /div /div div classrow mt-4 div classcol-md-8 h4相似度得分{{ similarity|floatformat:4 }}/h4 !-- 雷达图展示关键词重叠度 -- canvas idradarChart width400 height400/canvas /div div classcol-md-4 h4算法详情/h4 ul classlist-group li classlist-group-item算法strong{{ algorithm_used }}/strong/li li classlist-group-item计算耗时strong{{ calc_time }} ms/strong/li li classlist-group-item共提取 strong{{ keyword_count }}/strong 个关键词/li /ul /div /div script srchttps://cdn.jsdelivr.net/npm/chart.js/script script const ctx document.getElementById(radarChart).getContext(2d); const radarChart new Chart(ctx, { type: radar, data: { labels: {{ common_keywords|safe }}, // 如 [人工智能, 发展, 技术] datasets: [{ label: 文本1关键词, data: {{ keywords1_counts|safe }}, // [1, 2, 0] backgroundColor: rgba(54, 162, 235, 0.2), borderColor: rgb(54, 162, 235), pointBackgroundColor: rgb(54, 162, 235), }, { label: 文本2关键词, data: {{ keywords2_counts|safe }}, backgroundColor: rgba(255, 99, 132, 0.2), borderColor: rgb(255, 99, 132), pointBackgroundColor: rgb(255, 99, 132), }] }, options: { scales: { r: { angleLines: { display: true }, grid: { circular: true }, pointLabels: { font: { size: 12 } }, ticks: { beginAtZero: true, max: 5 } } } } }); /script后端视图传递的数据# views.py 中 calculate_similarity 视图的返回部分 from collections import Counter import jieba def calculate_similarity(request): # ... 前面的逻辑 ... # 计算关键词重叠用于雷达图 words1 jieba.lcut(processed1) words2 jieba.lcut(processed2) counter1 Counter(words1) counter2 Counter(words2) common_keys list(set(words1) set(words2)) # 取前 5 个常见词避免雷达图太密 top_common sorted(common_keys, keylambda x: counter1[x] counter2[x], reverseTrue)[:5] context { text1: text1, text2: text2, similarity: round(score, 4), algorithm_used: cosine_tfidf, calc_time: int((time.time() - start_time) * 1000), common_keywords: json.dumps(top_common), keywords1_counts: json.dumps([counter1[w] for w in top_common]), keywords2_counts: json.dumps([counter2[w] for w in top_common]), keyword_count: len(set(words1) | set(words2)), } return render(request, similarity/result.html, context)这个雷达图的价值在于用户一眼看到“哦两段话都在讲‘人工智能’和‘发展’但‘技术’这个词在文本1里出现了2次文本2里没出现”比单纯一个 0.68 的数字更有说服力。这才是“可视化”的意义——把算法黑匣子打开一条缝让用户信服结果。4. 运行与部署从python manage.py runserver到 Nginx Gunicorn4.1 本地运行三步启动绕过所有玄学错误别被requirements.txt里一堆包吓到真正核心依赖就 5 个# requirements.txt 关键行 Django4.2.7 mysqlclient2.2.4 scikit-learn1.3.0 jieba0.42.1 numpy1.24.3血泪经验mysqlclient安装失败是 Windows 用户最高频翻车点。别用pip install mysqlclient硬刚直接去 Christoph Gohlke 的非官方 wheel 库 下载对应 Python 版本和系统架构的.whl文件然后pip install xxx.whl。Mac 用户用brew install mysql-client再pip install mysqlclientLinux 用户sudo apt-get install python3-dev default-libmysqlclient-dev build-essential后再 pip。启动步骤Windows/Mac/Linux 通用创建并激活虚拟环境绝对必要python -m venv venv source venv/bin/activate # Linux/Mac # venv\Scripts\activate # Windows安装依赖并迁移数据库pip install -r requirements.txt # 创建 MySQL 数据库登录 MySQL 后执行 CREATE DATABASE text_similarity_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 修改 settings.py 中的 DATABASES 配置用户名密码 python manage.py makemigrations python manage.py migrate python manage.py createsuperuser # 创建管理员账号收集静态文件并启动python manage.py collectstatic --noinput python manage.py runserver打开http://127.0.0.1:8000/admin/登录进StopWord表添加几个中文停用词如“的”、“了”、“在”、“和”再访问http://127.0.0.1:8000/就能用了。4.2 生产部署Nginx Gunicorn告别开发服务器runserver只能本地调试生产必须换。gunicorn.conf.py配置是关键# gunicorn.conf.py import multiprocessing # 绑定地址 bind 127.0.0.1:8000 bind_address 127.0.0.1:8000 port 8000 # Unix socket推荐比 TCP 更快 # bind /run/gunicorn.sock # chmod 644 # 工作进程 workers multiprocessing.cpu_count() * 2 1 worker_class sync worker_connections 1000 timeout 30 keepalive 2 # 日志 accesslog /var/log/gunicorn_access.log errorlog /var/log/gunicorn_error.log loglevel info capture_output True # 进程控制 pidfile /var/run/gunicorn.pid daemon True user www-data group www-data umask 0o007Nginx 配置 (/etc/nginx/sites-available/textsim)upstream textsim_app { server 127.0.0.1:8000; } server { listen 80; server_name your-domain.com; location /static/ { alias /path/to/your/project/collected_static/; expires 1y; add_header Cache-Control public, immutable; } location / { proxy_pass http://textsim_app; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_redirect off; } }启用服务# 启动 Gunicorn gunicorn --config gunicorn.conf.py similarity.wsgi:application # 启用 Nginx sudo systemctl enable nginx sudo systemctl restart nginx避坑 / 常见问题 / 排查现象 1Nginx 502 Bad Gateway原因Gunicorn 没起来或proxy_pass地址不对比如 Gunicorn 绑定unix:/run/gunicorn.sock但 Nginx 配置里写http://127.0.0.1:8000。解决sudo journalctl -u gunicorn -f查看 Gunicorn 日志确认gunicorn.conf.py的bind和 Nginx 的proxy_pass一致。现象 2CSS/JS 404页面丑得没法看原因collectstatic没执行或 Nginx 的alias路径指向错误目录。解决ls -l /path/to/collected_static/确认文件存在检查 Nginx 配置中alias结尾是否有/必须有。现象 3MySQL 连接超时后台报OperationalError: (2013, Lost connection to MySQL server during query)原因MySQL 默认wait_timeout288008小时但 Gunicorn worker 长时间空闲后连接失效。解决在settings.py的DATABASES配置里加OPTIONS: {init_command: SET SESSION wait_timeout28800;}或更彻底地用django-db-geventpool。现象 4上传大文本1MB时 413 Request Entity Too Large原因Nginx 默认client_max_body_size1m。解决在 Nginxserver块里加client_max_body_size 10m;。现象 5中文显示为?????**本文还有配套的精品资源点击获取