资讯详情

资讯详情

豆瓣电影数据集清洗与特征工程全流程实战

简介包含约10万条电影信息的豆瓣数据资源包面向数据分析爱好者、产品经理及电影行业研究者可用于用户观影行为挖掘、推荐算法实验与影视市场趋势观察。压缩包内共4个文件涵盖2个CSV表格、1个SQLite数据库及1个Python爬虫脚本CSV分别收录用户画像与电影基础信息便于快速载入Pandas等工具进行统计建模DB文件则存储观影记录、评论及互动数据支持SQL复杂查询爬虫程序则演示了从豆瓣抓取、清洗到入库的完整流程是学习网络爬虫的实际范例。包体大小约40.02MB结构简洁目前已有3440人学习下载。借助该数据集读者既能构建电影推荐原型、分析不同类型影片的用户口碑差异也能通过爬虫代码掌握请求发送、HTML解析与数据持久化的关键技巧为后续独立采集或处理大规模文本数据打下扎实基础。1. 豆瓣电影数据集10万.zip一份能直接拿来练手的电影评分数据到底长什么样“豆瓣电影数据集10万.zip”这个名字我见过不止一次出现在各种课程作业、博客练习和推荐系统Demo里。第一次拿到它的人多半以为自己要做的只是解压、读进来、跑个模型结果发现真正的活全在后头字段里有“暂无评分”这种字符串、年份列混着空值和括号备注、类型是“剧情/喜剧/爱情”这种用斜杠拼起来的多标签——还没开始建模光清洗就能耗掉一个晚上。这份数据集的价值在于它带评分、带评价人数、带导演演员、也带剧情简介既能做评分预测这类回归任务也能做基于内容的推荐还能拿简介做文本分析。适合正在学数据清洗的初学者也适合想快速验证某个模型效果的从业者。接下来我按拿到 zip 之后最常见的工作顺序来写体检、读取、清洗、避坑、进阶每一步都给出可以直接复制的命令和参数说明把那些只有自己跑一遍才会踩到的坑提前摆出来。2. 拿到 zip 先体检文件结构、编码探测与行数核对很多人的习惯是拿到压缩包直接双击解压、拖进 Excel看到一堆乱码和空行才开始后悔。我一般会先花五分钟在命令行里做个体检不看清楚压缩包里到底有几个文件、什么格式、什么编码后面每一步都可能翻车。2.1 zip 里可能装了什么先看字段再动手豆瓣电影的页面信息维度很固定导出成数据集时常见做法是把电影主表放在一个文件里字段大致包括电影名、导演、编剧、主演、类型、制片国家/地区、语言、上映日期、片长、又名、评分、评价人数、剧情简介。有些版本还会额外带上电影ID或者 IMDb 链接方便后续关联其他数据。这个文件大概率是 CSV 或 JSON 中的某一种。CSV 的可能性更高因为体积小、行列直观但 CSV 有个隐患剧情简介这种长文本字段里经常自带换行符导致用wc -l数行数时结果比真实数据条数多出一截。JSON 没有这个问题但解析起来更吃内存。先别急着猜用命令直接看压缩包内容才是正路。2.2 解压前的三条命令列清单、探编码、数行数打开终端进到存放 zip 的目录先列出压缩包内容清单unzip -l douban_movie_dataset.zip-l参数只列出压缩包内的文件名和压缩前后大小不会真正解压。看输出里的文件名和后缀就能判断里面是单个主表还是拆成了多个关联表。如果文件名是乱码先别慌这只是 zip 的编码标记问题解压后再确认。确认文件形态后先探测编码再决定用哪种方式读取unzip -o douban_movie_dataset.zip file *.csvfile命令会读取文件头部字节并输出编码信息常见的输出是UTF-8 Unicode text或ISO-8859系列。如果显示Non-ISO extended-ASCII多半就是 GBK 编码后面用 pandas 读取时必须指定encodinggbk否则第一行就会报UnicodeDecodeError。最后数一下行数对数据量有个直观预期wc -l *.csv这里有个关键点wc -l统计的是换行符数量如果剧情简介字段里包含换行统计结果会比实际数据行数多。我看到很多人用这个数字当“数据条数”写进报告里结果和去重后的真实行数对不上。要拿准确条数解压后用 pandas 读入再len(df)才是靠谱的做法。2.3 为什么这种规模的数据集适合用 zip 而不是 tar.gzCSV 这种纯文本文件用 zip 的 deflate 算法压缩率通常很可观10万条电影记录原始 CSV 可能有几十 MB压缩成 zip 后能缩到几 MB。tar.gz 的 gzip 算法压缩率接近但两者的使用场景有个明显差异zip 支持针对单个文件的随机访问而 tar.gz 必须先把整个归档解出来才能读取任何一个文件。如果你只想确认里面某个文件的格式用 Python 的zipfile模块可以直接读出单个文件的前几行不必解压全部内容import zipfile with zipfile.ZipFile(douban_movie_dataset.zip) as zf: print(zf.namelist()) # 查看全部内部文件名 with zf.open(主表.csv) as f: for _ in range(5): print(f.readline())这段代码先列出 zip 内的全部文件再只读取“主表.csv”的前五行用来快速预览表头和数据类型。zf.open()拿到的是文件对象可以直接按行读内存占用极小。调试阶段用这种方式反复试探数据格式比每次解压出来再删除高效得多。3. 用 pandas 读入豆瓣电影数据字段解析与缺失值分布压缩包体检完进入正式读取阶段。这一步的目标不是“读进来就行”而是把每一列的类型、缺失比例、脏数据分布摸清楚。读数十分钟清洗两小时——大部分时间其实花在这里。3.1 读取参数的两个关键点编码与数据类型读 CSV 的第一步是指定编码。前面file命令探测的结果在这里派上用场import pandas as pd df pd.read_csv( 主表.csv, encodingutf-8, # 如果报 UnicodeDecodeError 就改成 gbk dtype{评价人数: int64}, # 数值列直接指定类型避免被读成 object parse_dates[上映日期], # 日期列解析为 datetime ) print(df.shape) print(df.dtypes)dtype参数值得多说一句。CSV 里只要有一行数据格式不对pandas 就会把整列降级为 object 类型后续做算术运算时各种报错。预先指定数值列的类型可以让 pandas 在读取阶段就暴露问题而不是等到建模时才炸出来。parse_dates的作用是把“2015-08-14”这种字符串转成 datetime 类型后面按年份分组聚合会方便很多。如果不确定编码先做一个快速探测import chardet with open(主表.csv, rb) as f: raw f.read(10000) result chardet.detect(raw) print(result)chardet.detect读取文件前 10000 个字节并返回编码推测结果confidence 字段表示置信度。注意它只是“推测”不是绝对正确最终以能否顺利解析为准。我见过探测结果是 utf-8 但实际是 gbk 的情况所以读完第一行后打印一下中文列名确认没有乱码再继续。3.2 缺失值分布先看比例再决定策略读取成功之后第一件事是查看各列的缺失情况missing df.isna().sum() missing missing[missing 0].sort_values(ascendingFalse) print(missing)isna().sum()统计每列缺失值数量筛选出缺失列并按数量降序排列。不同列的缺失含义完全不同评分、评价人数这类核心字段缺失一般很少如果有明显缺失要警惕是不是被“暂无评分”这种字符串污染了导演、编剧、剧情简介缺失比较多这是数据源的天然情况导出的信息本身就不完整。处理策略上我的习惯是核心字段缺失率超过 30% 就考虑整列丢弃低于 5% 可以直接删行中间地带则要具体分析。比如编剧列缺失一半做文本分析时用不上做导演维度的聚合也用不上直接保留原样不参与建模即可没必要强行填充。3.3 脏值检查字符串混进数值列是最大的坑describe()是快速了解数值列分布的入口但如果“评分”列里有“暂无评分”这种字符串pandas 会直接跳过该列的统计输出去全是NaN。这时候要用分桶计数来检查print(df[评分].value_counts(dropnaFalse).head(10))value_counts(dropnaFalse)会把缺失值也统计进去并把频次最高的值列在最前面。如果你看到“暂无评分”出现在结果里说明这列混入了非数值内容。处理办法是把该列先转成字符串定位污染值再统一清理df.loc[df[评分].astype(str).str.contains(暂无), 评分] None df[评分] pd.to_numeric(df[评分], errorscoerce)pd.to_numeric配合errorscoerce可以把所有无法解析的值统一转成 NaN这是处理混入字符串最干净的方式。转为 NaN 之后缺失值统计和后续清洗流程就能统一处理不用再单独维护一套脏值列表。4. 把原始字段变成可用特征多标签拆分、年份提取与评分归一化数据结构理顺之后下一步是把原始字段加工成模型能直接吃的特征。这一步没有统一标准完全取决于你要做什么任务。下面是我在电影数据集上最常用的三组加工处理类型多标签、提取年份、归一化评分相关字段。4.1 电影类型字段的多标签拆分别让“剧情/喜剧”变成一个整体类型字段是典型的多标签格式一行数据可能是“剧情/喜剧/爱情”三个类型用斜杠拼接。如果直接对这个字段做value_counts统计出来的全是“剧情/喜剧/爱情”这种复合字符串完全看不出真实分布。正确做法是按分隔符拆开生成哑变量# 类型字段形如剧情/喜剧/爱情 type_dummies df[类型].fillna().str.get_dummies(sep/) df pd.concat([df, type_dummies], axis1) print(type_dummies.sum().sort_values(ascendingFalse).head(10))str.get_dummies(sep/)会按斜杠拆分每个单元格并为每种类型生成一列 0/1 标记。fillna()先把缺失值变成空字符串避免拆分时报错。sum().sort_values()输出每种类型的电影数量这才是真实的类型分布。拆完之后还有个容易忽略的细节斜杠两侧可能带空格。如果原始数据是“剧情 / 喜剧 / 爱情”这种格式str.get_dummies会把“ 剧情”“喜剧 ”当成不同的类型。稳妥做法是拆之前先统一清洗df[类型_清洗] df[类型].fillna().str.replace( , ).str.replace( , ) type_dummies df[类型_清洗].str.get_dummies(sep/)先去掉半角空格和全角空格再拆分能避免生成几十个只包含一两条数据的虚假类型列。这一步属于典型的“数据看起来干净实际全是隐藏空格”问题。4.2 上映年份提取datetime 好用但要留正则兜底如果读取时指定了parse_dates[上映日期]提取年份只需要一行df[年份] df[上映日期].dt.year.dt.year直接从 datetime 列中取出年份返回整数。但现实没有这么顺利很多版本的“上映日期”字段是“2015-08-14(中国大陆)”或“2015(中国大陆)”这种混搭格式to_datetime解析到括号时会抛异常整列解析失败。这种情况我一般用正则提取df[年份_正则] df[上映日期].astype(str).str.extract(r(19|20)\d{2})str.extract配合正则从字符串中提取第一位是 19 或 20 的四位数字适用于所有带括号的脏格式。astype(str)先把所有值统一转成字符串避免 datetime 类型报错。提取结果默认是字符串类型需要再转换df[年份_正则] pd.to_numeric(df[年份_正则], errorscoerce)转成 numeric 之后无法提取年份的行会自动变成 NaN。这里要提醒一句同时保留.dt.year和正则提取两种情况建议只保留一种避免两列口径不一致导致后续聚合出错。我一般是先尝试to_datetime失败率超过 5% 就直接走正则路线。4.3 评分与评价人数的归一化长尾分布要提前处理评分是 0 到 10 之间的浮点数取值范围固定本身不需要做标准化。真正需要处理的是“评价人数”这个字段它呈现典型的长尾分布少数热门电影的评分人数几万甚至几十万大部分冷门电影只有几百。直接用原始数值做特征模型会被少数热门样本带偏。常见处理是对数变换import numpy as np df[评价人数_数值] pd.to_numeric(df[评价人数], errorscoerce) df[评价人数_log] np.log1p(df[评价人数_数值])np.log1p计算 log(1x)比直接np.log的好处是 x0 时结果为 0 而不是负无穷。填充缺失值时也要注意评价人数缺失log 后仍然是 NaN后续建模前要做删除或填充决策。如果任务目标是预测评分排名而非绝对分值把评分转成百分位排名更合适df[评分_排名] df[评分].rank(pctTrue)rank(pctTrue)把评分转成 0 到 1 之间的百分位表示该电影评分在所有电影中的相对位置。这样处理后冷门高分片和热门中高分的可比性更强适合做 Top-N 推荐类的排序模型。5. 豆瓣电影数据集避坑指南5 个最常见的翻车现场这部分内容来自我和周围人多次处理这类数据的血泪经验。每一条都是先给现象、再解释原因、最后给解决方案照着排查能省下大量时间。5.1 现象read_csv 第一行就报 UnicodeDecodeError原因数据文件是 GBK 编码pd.read_csv默认按utf-8解析遇到中文字节序列直接抛异常。解决方案是改用gbk或更宽容的gb18030。df pd.read_csv(主表.csv, encodinggb18030)注意GBK 缺字符时用gb18030基本能兜住所有中文编码问题。不要在read_csv里设置errorsignore那会让 pandas 跳过非法字节看起来读进来了实际中文全变成乱码这种文件没法救。5.2 现象评分列是 object 类型describe() 不输出任何统计原因列里混入了“暂无评分”等字符串pandas 自动把整列降级为 object。解决方案是先转字符串定位脏值再统一转数值。df[评分] pd.to_numeric(df[评分], errorscoerce)转完之后再用df[评分].isna().sum()查看被转成 NaN 的数量确认污染范围。如果 NaN 数量异常多回到原始文件检查是不是整列都错位了。5.3 现象wc -l 数出 12 万行但 pandas 读进来只有 10 万行原因剧情简介字段里包含换行符wc -l按换行符计数把同一行数据拆成了多次。这不是数据丢失是计数口径错误。确认数据条数只认 pandas 读入后的len(df)不要用wc -l的结果。如果压缩包里是多个文件分别数行数要分别读入再对比。5.4 现象类型拆分后出现大量只含一两条数据的虚假类型原因原始数据里斜杠两侧带了半角或全角空格“剧情 / 喜剧”被拆分成了“剧情”和“ 喜剧”两个不同的键。解决方法是拆分前统一去除空格df[类型_清洗] df[类型].fillna().str.replace(r\s, , regexTrue) type_dummies df[类型_清洗].str.get_dummies(sep/)\s匹配所有空白字符包括半角空格、全角空格和制表符。清洗后再拆分虚假类型列会消失。5.5 现象按年份聚合时出现一个巨大的 NaN 组和一堆 1900 年原因年份列里有“未知”“暂无”等字符串转数值时变成 NaNgroupby会把 NaN 单独分一组另有部分条目解析出错被转成 1900 之类的异常值。解决方法是先看分布再决定要不要丢弃bad_years df[年份].isna().sum() (df[年份] 1900).sum() print(f异常年份占比: {bad_years / len(df):.2%})占比低于 5% 直接删除异常行高于 10% 就要回去检查年份提取的正则是否漏掉了某种格式。6. 三个进阶方向评分预测的输入格式、文本聚类与分布校验数据集清洗到能用的状态之后进阶玩法就看你想解决什么问题了。这里给出三个经过验证的方向每个都附带最小可跑通的代码骨架。6.1 协同过滤的输入格式转换如果你手上的版本带用户评分记录做协同过滤前要把宽表转成三元组格式triplet df[[用户ID, 电影ID, 评分]].dropna() triplet[user_code] triplet[用户ID].astype(category).cat.codes triplet[movie_code] triplet[电影ID].astype(category).cat.codesastype(category).cat.codes把字符串 ID 重编码为从 0 开始的连续整数这是大部分推荐系统库要求的输入格式。如果数据版本里没有用户评分记录只有电影评分汇总那就做不了协同过滤得先补充用户行为数据或者转向基于内容的推荐。6.2 剧情简介的文本聚类剧情简介是天然的长文本字段做文本聚类前要解决中文分词问题from sklearn.feature_extraction.text import TfidfVectorizer def jieba_tokenize(text): import jieba return .join(jieba.cut(text)) df[简介_分词] df[剧情简介].fillna().apply(jieba_tokenize) tfidf TfidfVectorizer(max_features5000) X tfidf.fit_transform(df[简介_分词])中文不能直接用空格分词必须先过一遍分词器再用空格拼接否则 TF-IDF 的特征全是整句字符串基本没有区分度。max_features5000限制特征维度防止稀疏矩阵爆炸。6.3 用交叉验证校验模型是否被数据量骗了评分预测模型的精度经常被数据分布误导交叉验证的方差能暴露这个问题from sklearn.linear_model import LinearRegression from sklearn.model_selection import cross_val_score model LinearRegression() X df[[评价人数_log, 评分_排名]].fillna(0) y df[评分].fillna(df[评分].median()) scores cross_val_score(model, X, y, cv5) print(f平均 R2: {scores.mean():.3f}标准差: {scores.std():.3f})cv5把数据分五折每折做一次验证输出五次结果的均值和标准差。标准差过大说明模型在部分数据子集上严重过拟合这时候不要急着加特征先回头检查数据划分是否按年份分层了——如果训练集全是老片、测试集全是新片分数自然难看。处理这类数据集的习惯我已经固定下来了拿到手先花半小时体检再决定清洗策略绝不直接解压开跑。数据集的坑不在数据量大而在格式不一致和隐藏的脏字符串花在清理上的时间永远值得。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →