简介这份资源面向计算机、人工智能、大数据、数学、电子信息等专业的学生与相关技术学习者提供一套基于Python与机器学习的豆瓣电影数据分析完整项目可用于课程设计、期末大作业或毕业设计参考。压缩包共16个文件约28.18MB包含Python源码、HTML作业页面、PDF实验报告、Markdown说明文档以及txt词表与数据文件、jpg词云与可视化图片、rar评论数据包等覆盖从数据预处理、中文分词、词云绘制到机器学习建模分析的完整流程。项目代码经过严格调试下载后可直接运行但需要具备一定Python与机器学习基础才能读懂并二次调试。目前已有351人学习下载读者可借此掌握豆瓣影评数据的清洗与统计方法、词云与可视化呈现技巧并参考实验报告的结构与结论撰写思路快速搭建自己的数据分析项目框架。1. 豆瓣电影数据分析项目从零到一把源码跑起来豆瓣电影 Top250 的数据分析几乎是每个 Python 初学者绕不开的练手项目。但大多数人卡在第一步拿到一份「源码报告.zip」之后不知道从哪下手环境配不对、爬虫跑不通、图表出不来。这个项目本质上做三件事用爬虫把电影数据抓下来用 pandas 做清洗和特征提取再用机器学习模型做评分预测或聚类分析。适合有 Python 基础、想找一个完整数据分析项目练手的人也适合正在准备机器学习期末复习、需要一份能写进简历的实战案例。我见过太多人把源码下载下来改个路径就报错然后就放弃了。下面把我自己跑通这套流程的完整路径拆开讲包括环境配置、数据采集、特征工程、模型训练和可视化每一步都给可复现的命令和参数。2. 环境搭建与数据采集把豆瓣 Top250 抓下来2.1 Python 环境配置用 conda 隔离依赖很多人第一步就翻车是因为直接往系统 Python 里装包版本冲突之后连 pip 都用不了。我一般用 conda 建一个独立环境Python 版本选 3.9 或 3.10这两个版本对 pandas、scikit-learn、matplotlib 的兼容性最稳。# 创建独立环境指定 Python 3.10 conda create -n douban_ml python3.10 -y # 激活环境 conda activate douban_ml # 安装核心依赖版本锁定避免兼容问题 pip install requests2.31.0 pandas2.0.3 numpy1.24.3 \ scikit-learn1.3.0 matplotlib3.7.2 seaborn0.12.2 \ beautifulsoup44.12.2 jieba0.42.1 wordcloud1.9.2这里锁版本不是多此一举。pandas 2.x 和 numpy 1.24 搭配最稳scikit-learn 1.3 的 API 和旧版教程基本兼容。如果你用 VSCode装好 Python 插件后按 CtrlShiftP 选解释器指向 conda 环境里的 python.exe 就行。Linux 系统安装 Python 的话建议直接用 miniconda比系统自带的 Python 省心得多。注意不要用 pip install 全局安装也不要在 base 环境里折腾。环境隔离是后悔药出问题直接删环境重建五分钟搞定。2.2 爬取豆瓣 Top250请求头与分页逻辑豆瓣 Top250 的页面结构很规整每页 25 部电影共 10 页。核心是构造请求头里的 User-Agent不加的话大概率返回 418。下面是最小可运行爬虫import requests from bs4 import BeautifulSoup import pandas as pd import time import random # 请求头关键是 User-Agent HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 } def fetch_page(start): 抓取单页start 为起始序号 0,25,50... url fhttps://movie.douban.com/top250?start{start} resp requests.get(url, headersHEADERS, timeout10) resp.raise_for_status() return resp.text def parse_page(html): 解析单页返回电影列表 soup BeautifulSoup(html, html.parser) items soup.select(div.item) movies [] for item in items: title item.select_one(span.title).get_text(stripTrue) rating item.select_one(span.rating_num).get_text(stripTrue) # 评价人数在 star 后面的文本里 votes item.select_one(div.star).find_all(span)[-1].get_text(stripTrue) quote_tag item.select_one(span.inq) quote quote_tag.get_text(stripTrue) if quote_tag else movies.append({ title: title, rating: float(rating), votes: int(votes.replace(人评价, )), quote: quote }) return movies all_movies [] for start in range(0, 250, 25): html fetch_page(start) all_movies.extend(parse_page(html)) time.sleep(random.uniform(1.5, 3.0)) # 随机延时降低被封风险 df pd.DataFrame(all_movies) df.to_csv(douban_top250.csv, indexFalse, encodingutf-8-sig) print(f共抓取 {len(df)} 条记录)逻辑说明fetch_page负责发请求parse_page用 BeautifulSoup 的 CSS 选择器提取标题、评分、评价人数和短评。time.sleep加随机延时是血泪经验固定间隔请求容易被识别。参数方面timeout10防止请求挂死encodingutf-8-sig保证 Excel 打开不乱码。如果返回 418检查 User-Agent 是否完整如果某页解析为空打印resp.status_code和resp.text[:500]看返回内容。3. 特征工程与可视化把数据变成能喂给模型的样子3.1 数据清洗与特征构造原始数据只有标题、评分、评价人数和短评直接喂给模型信息量太少。我一般会从标题里提取年份和国家从短评里做情感倾向再构造几个衍生特征。import re import jieba from sklearn.feature_extraction.text import TfidfVectorizer # 从标题里提取年份如 肖申克的救赎(1994) - 1994 def extract_year(title): match re.search(r\((\d{4})\), title) return int(match.group(1)) if match else None df[year] df[title].apply(extract_year) df[title_clean] df[title].str.replace(r\(\d{4}\), , regexTrue).str.strip() # 评价人数取对数缩小量纲差距 import numpy as np df[votes_log] np.log1p(df[votes]) # 短评分词后做 TF-IDF df[quote] df[quote].fillna() df[quote_seg] df[quote].apply(lambda x: .join(jieba.cut(x))) tfidf TfidfVectorizer(max_features200, stop_words[的, 了, 是, 在]) quote_tfidf tfidf.fit_transform(df[quote_seg]) print(TF-IDF 矩阵形状:, quote_tfidf.shape) print(年份缺失数:, df[year].isna().sum())extract_year用正则从标题里抠年份votes_log做对数变换是因为评价人数从几千到几百万量纲差距太大取对数后分布更接近正态。TF-IDF 的max_features200控制维度避免短评文本太稀疏。停用词表只放了几个高频虚词实际用的时候可以换成更完整的表。如果年份缺失多检查标题格式是否统一有些版本可能没有括号年份。3.2 可视化评分分布与年份趋势数据分析项目里图表是报告的门面。用 matplotlib 和 seaborn 画两张核心图就够了评分分布直方图和年份-平均评分折线图。import matplotlib.pyplot as plt import seaborn as sns # 设置中文字体Windows 用 SimHeiMac 用 Arial Unicode MS plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False fig, axes plt.subplots(1, 2, figsize(14, 5)) # 评分分布 sns.histplot(df[rating], bins15, kdeTrue, axaxes[0], color#4C72B0) axes[0].set_title(豆瓣 Top250 评分分布) axes[0].set_xlabel(评分) axes[0].set_ylabel(电影数量) # 年份趋势 year_rating df.groupby(year)[rating].mean().reset_index() sns.lineplot(datayear_rating, xyear, yrating, axaxes[1], markero) axes[1].set_title(年份与平均评分趋势) axes[1].set_xlabel(年份) axes[1].set_ylabel(平均评分) plt.tight_layout() plt.savefig(douban_eda.png, dpi150) plt.show()font.sans-serif设成 SimHei 是为了中文不乱码Mac 上换成Arial Unicode MS。bins15让评分分布看得更细kdeTrue加核密度曲线。年份趋势图能看出哪些年代的片子评分高这个结论可以直接写进报告。保存用dpi150够清晰又不至于文件太大。4. 机器学习建模评分预测与聚类分析4.1 用随机森林预测评分有了年份、评价人数对数、TF-IDF 特征就可以做一个回归任务预测电影评分。我一般先用随机森林试水因为它对特征缩放不敏感调参也简单。from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_squared_error, r2_score from scipy.sparse import hstack # 组合数值特征和 TF-IDF 特征 numeric_features df[[year, votes_log]].fillna(0).values X hstack([numeric_features, quote_tfidf]).tocsr() y df[rating].values X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) rf RandomForestRegressor( n_estimators200, max_depth10, min_samples_split5, random_state42, n_jobs-1 ) rf.fit(X_train, y_train) y_pred rf.predict(X_test) print(fRMSE: {mean_squared_error(y_test, y_pred, squaredFalse):.3f}) print(fR2: {r2_score(y_test, y_pred):.3f})hstack把稀疏的 TF-IDF 矩阵和稠密的数值特征拼在一起tocsr()转成压缩稀疏行格式节省内存。随机森林的n_estimators200是树的数量max_depth10控制过拟合min_samples_split5防止叶子节点太细。RMSE 在 0.3 以内算正常R2 超过 0.5 说明特征有一定解释力。如果 R2 太低优先检查年份缺失是否太多或者短评特征是否有效。4.2 KMeans 聚类把电影分成几类除了回归还可以用 KMeans 做无监督聚类看看 Top250 能分成几种类型。用评分和评价人数对数两个维度就够了方便可视化。from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler # 只用两个维度做聚类方便画图 cluster_data df[[rating, votes_log]].dropna() scaler StandardScaler() cluster_scaled scaler.fit_transform(cluster_data) # 肘部法则选 K inertias [] for k in range(2, 9): km KMeans(n_clustersk, random_state42, n_init10) km.fit(cluster_scaled) inertias.append(km.inertia_) # 假设选 K4 km_final KMeans(n_clusters4, random_state42, n_init10) df[cluster] km_final.fit_predict(cluster_scaled) plt.figure(figsize(8, 6)) sns.scatterplot( datadf, xrating, yvotes_log, huecluster, paletteSet2, s60 ) plt.title(豆瓣 Top250 聚类结果K4) plt.savefig(douban_cluster.png, dpi150) plt.show()StandardScaler标准化是必须的否则评价人数的量纲会主导距离计算。n_init10让 KMeans 多跑几次取最优避免局部最优。肘部法则看 inertias 下降变缓的点一般 K3 到 5 之间。聚类结果可以解释为高分高热度、高分低热度、中分高热度、中分低热度。这个结论写进报告很有说服力。5. 避坑与排查跑这个项目最容易翻车的五个地方5.1 爬虫返回 418 或空数据现象requests.get返回 418或者 BeautifulSoup 解析出来是空列表。原因豆瓣对请求头校验严格缺少 User-Agent 或请求频率太高。解决补全 User-Agent加time.sleep(random.uniform(1.5, 3.0))如果还不行就换 IP 或降低频率。别用多线程猛冲封了要等很久。5.2 中文显示成方块现象matplotlib 图表里中文全是方框。原因默认字体不支持中文。解决plt.rcParams[font.sans-serif] [SimHei]Mac 换成Arial Unicode MSLinux 装wqy-microhei字体后指定对应名称。改完记得重启 kernel。5.3 TF-IDF 矩阵维度爆炸现象短评做 TF-IDF 后特征几万维内存爆了。原因没限制max_features且没过滤停用词。解决设max_features200到 500加min_df2过滤低频词停用词表用 jieba 自带的或自定义。维度控制在几百以内模型训练才跑得动。5.4 随机森林 R2 为负现象模型 R2 小于 0比直接猜平均值还差。原因特征和评分没有线性关系或者训练集太小。解决检查年份缺失比例如果超过 30% 就考虑去掉这个特征增加短评 TF-IDF 的权重换 GradientBoosting 或 XGBoost 试试。Top250 只有 250 条数据R2 能到 0.4 以上就不错了别期望太高。5.5 聚类结果每次不一样现象KMeans 跑两次聚类标签完全不同。原因KMeans 对初始中心敏感且没设随机种子。解决random_state42固定种子n_init10多跑几次取最优。另外标准化必须做否则量纲大的特征会主导结果。6. 进阶技巧用交叉验证和特征重要性把报告写扎实跑通基础流程之后想让报告更有说服力我一般加两个东西交叉验证和特征重要性排序。交叉验证能说明模型稳定性特征重要性则直接告诉你哪些因素最影响评分。from sklearn.model_selection import cross_val_score from sklearn.ensemble import RandomForestRegressor rf_cv RandomForestRegressor( n_estimators200, max_depth10, random_state42, n_jobs-1 ) cv_scores cross_val_score( rf_cv, X, y, cv5, scoringr2 ) print(f5折交叉验证 R2: {cv_scores.mean():.3f} (/- {cv_scores.std():.3f})) # 特征重要性 rf_cv.fit(X_train, y_train) importances rf_cv.feature_importances_ # 前两个是 year 和 votes_log print(f年份重要性: {importances[0]:.3f}) print(f评价人数重要性: {importances[1]:.3f}) print(f短评 TF-IDF 总重要性: {importances[2:].sum():.3f})交叉验证的cv5把数据分五份轮流验证scoringr2看模型泛化能力。标准差小于 0.1 说明模型稳定。特征重要性里如果年份和评价人数加起来超过 0.5说明数值特征主导如果 TF-IDF 总重要性高说明短评文本有区分度。这个结论可以直接写进报告的分析部分。还有一个技巧把聚类结果和评分预测结果交叉分析。比如看看哪个聚类的预测误差最大往往能发现异常样本。我一般会把误差最大的十条电影打印出来人工检查是不是数据有问题。这个习惯帮我抓过好几次爬虫解析错误。最后说个我自己的教训别一上来就追求复杂模型。这个项目数据量小随机森林和 KMeans 足够用把特征工程和可视化做扎实比换个深度模型效果好得多。我见过有人非要用 LSTM 处理 250 条数据结果过拟合到没法看。先把基础流程跑通再逐步加特征、换模型每一步都记录参数和结果报告自然就厚实了。希望帮到你。本文还有配套的精品资源点击获取