ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python爬虫+pyecharts:影视弹幕与评分数据可视化实战

Python爬虫+pyecharts:影视弹幕与评分数据可视化实战 简介这份资源面向具备一定Python基础、希望进入影视数据分析与推荐系统领域的学习者与开发者围绕影视行业数据展开从预处理、深度学习建模到可视化呈现的完整实践。包内共273个文件以63个py脚本、38个html页面、36个pyc缓存、33张jpg与14张png图片为主另含csv、xls数据表、bin与pth模型权重、h5与pb模型文件及css、js等前端资源压缩包约120.38MB结构覆盖数据、模型、可视化与Web展示多个模块。项目将FCN用于影视数据特征提取用LSTM完成影评情感分类与票房预测并借助漏斗图、饼图、柱状图展示策划到上映的流失、类型占比与票房差异同时涉及协同过滤推荐思路。已有564人学习下载适合作为课程设计或毕业项目的参考范例帮助读者理解数据清洗、模型训练与图表输出的衔接方式并借鉴其目录组织与排错思路。1. 影视数据分析可视化从弹幕和评分里挖出真东西很多人做影视数据分析第一步就卡在数据源上。豆瓣、猫眼、灯塔这些平台的数据要么有反爬要么字段残缺要么时间跨度不够。我见过太多人兴冲冲打开 Jupyter写了两行pd.read_csv就发现手里只有一份几百条的样本连一部热播剧的弹幕量都撑不起来。这个方向真正能落地的做法是先把数据链路跑通——用 Python 爬虫拿到剧集元数据、短评、弹幕、播放量趋势再用 pandas 做清洗和特征提取最后用 pyecharts 或 ECharts 出可视化大屏。它适合两类人一类是想拿影视数据练手 Python 数据分析与可视化的入门者另一类是需要给内容团队做选题决策、给运营做投放复盘的一线从业者。核心链路就四步采集、清洗、分析、可视化。每一步都有坑但每一步也都有成熟方案。2. 数据采集用 Python 爬虫把影视数据拿到手2.1 选目标站点和字段先想清楚要回答什么问题影视数据采集最容易犯的错是先写爬虫再想分析目标。我一般会先列三个问题这部剧的评分走势和弹幕情绪是否同步哪个演员出场时弹幕密度最高短评里的高频词和官方宣传口径差多远这三个问题决定了你要采的字段。以一部在播剧为例需要采集的字段至少包括剧集基础信息剧名、导演、主演、开播日期、集数、每集播放量、短评用户名、评分、评论内容、时间、弹幕时间戳、内容、集数。如果只做评分可视化短评表就够了如果要做弹幕情绪分析弹幕表必须带时间戳否则没法对齐剧情节点。常见做法是用requests加BeautifulSoup抓静态页面用Selenium或Playwright处理动态渲染。但影视平台的短评和弹幕大多是异步加载的直接抓 HTML 拿不到。更稳的方式是打开浏览器开发者工具切到 Network 面板筛选 XHR 请求找到返回 JSON 的接口。这个接口通常带分页参数比如start和limit翻页就能拿到全量数据。注意采集频率控制在每秒 1 到 2 次请求加随机 User-Agent 和 Referer不要用多线程猛冲。影视平台的风控对高频访问很敏感一旦被封 IP换代理的成本比慢慢采高得多。2.2 写一个能翻页的采集脚本下面这个脚本以短评接口为例演示分页采集和本地落盘。实际接口地址和参数名需要你从开发者工具里自己确认不同平台差异很大。import requests import pandas as pd import time import random # 替换成你从 Network 面板里找到的真实接口 BASE_URL https://example.com/api/comments HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://example.com/movie/12345, Accept: application/json, } def fetch_comments(movie_id, max_pages50): all_comments [] for page in range(max_pages): params { movie_id: movie_id, start: page * 20, limit: 20, sort: new_score, # 按最新评分排序 } try: resp requests.get(BASE_URL, headersHEADERS, paramsparams, timeout10) resp.raise_for_status() data resp.json() except Exception as e: print(f第 {page} 页请求失败: {e}) break comments data.get(data, {}).get(comments, []) if not comments: print(f第 {page} 页无数据采集结束) break for c in comments: all_comments.append({ user: c.get(user, {}).get(name, ), rating: c.get(rating, {}).get(value, None), content: c.get(content, ), time: c.get(create_time, ), votes: c.get(votes, 0), }) print(f已采集第 {page} 页累计 {len(all_comments)} 条) time.sleep(random.uniform(1.0, 2.0)) # 随机间隔降低风控概率 return pd.DataFrame(all_comments) if __name__ __main__: df fetch_comments(movie_id12345, max_pages30) df.to_csv(comments_raw.csv, indexFalse, encodingutf-8-sig) print(f采集完成共 {len(df)} 条已保存到 comments_raw.csv)这段代码的逻辑很直白循环翻页每页请求间隔 1 到 2 秒随机休眠拿到 JSON 后提取字段最后统一存 CSV。几个关键参数需要你根据实际情况调整。max_pages控制最大翻页数防止死循环limit是每页条数有些平台固定 20有些支持 50sort参数决定排序方式按时间排和按热度排拿到的数据分布完全不同。timeout10是防止某个请求卡死整个脚本。如果接口返回的不是 JSON 而是 HTML说明你找错了请求回到 Network 面板重新筛选。采集完成后先别急着分析。打开 CSV 看一眼检查三件事总条数是否合理、评分字段有没有大量空值、评论内容有没有被截断。我踩过一次坑接口返回的评论内容只给了前 50 个字完整内容在另一个字段里结果做词频分析时全是半截句子。3. 数据清洗与特征工程把脏数据变成能分析的表3.1 短评和弹幕的清洗规则原始数据拿到手第一件事是去重和去空。短评表里同一个用户可能重复评论弹幕表里同一秒可能有多条重复内容。去重逻辑要看业务短评按user content去重弹幕按time content去重。空值处理上评分字段为空的行直接丢弃因为后续评分分布分析需要这个字段评论内容为空但评分存在的行保留用于统计评分。时间字段的格式化是另一个高频翻车点。接口返回的时间可能是 Unix 时间戳、2024-01-15这种日期字符串或者3天前这种相对时间。统一转成datetime类型后面做时间序列分析才不会报错。import pandas as pd import re from datetime import datetime, timedelta df pd.read_csv(comments_raw.csv) # 去重同一用户同一内容只保留一条 df df.drop_duplicates(subset[user, content], keepfirst) # 丢弃评分为空的行 df df.dropna(subset[rating]) # 评分转数值 df[rating] pd.to_numeric(df[rating], errorscoerce) df df.dropna(subset[rating]) # 处理相对时间 def parse_time(t): if pd.isna(t): return None t str(t).strip() if re.match(r^\d{4}-\d{2}-\d{2}, t): return pd.to_datetime(t) m re.match(r(\d)天前, t) if m: return datetime.now() - timedelta(daysint(m.group(1))) m re.match(r(\d)小时前, t) if m: return datetime.now() - timedelta(hoursint(m.group(1))) return None df[time] df[time].apply(parse_time) df df.dropna(subset[time]) # 评论长度作为特征 df[content_len] df[content].str.len() df.to_csv(comments_clean.csv, indexFalse, encodingutf-8-sig) print(df[[rating, content_len, time]].describe())清洗后的表至少包含这几列用户、评分、评论内容、时间、点赞数、评论长度。content_len这个特征在后面做“评分与评论长度关系”分析时很有用。describe()输出能帮你快速判断数据分布是否正常比如评分均值如果在 3 分以下说明这部剧口碑确实不行后续可视化会很明显。3.2 弹幕密度和情绪特征提取弹幕数据比短评更适合做时间序列可视化因为每条弹幕都有精确到秒的时间戳。核心思路是按分钟或按剧情节点聚合计算弹幕密度和情绪得分。情绪分析可以用SnowNLP或Jieba加自定义词典。影视弹幕里有很多网络用语和缩写通用情感词典经常误判。我一般会先跑一遍通用模型再人工抽 200 条检查准确率如果低于 70%就手动补充领域词典。import jieba from snownlp import SnowNLP import pandas as pd danmu pd.read_csv(danmu_raw.csv) danmu[time] pd.to_datetime(danmu[time]) # 按分钟聚合弹幕密度 danmu[minute] danmu[time].dt.floor(min) density danmu.groupby(minute).size().reset_index(namecount) # 情绪得分每条弹幕算一个 0 到 1 的分数 def sentiment_score(text): if not text or len(str(text)) 2: return None try: return SnowNLP(str(text)).sentiments except Exception: return None danmu[sentiment] danmu[content].apply(sentiment_score) danmu danmu.dropna(subset[sentiment]) # 按分钟聚合平均情绪 sentiment_by_min danmu.groupby(minute)[sentiment].mean().reset_index() # 合并密度和情绪 result pd.merge(density, sentiment_by_min, onminute, howleft) result.to_csv(danmu_features.csv, indexFalse, encodingutf-8-sig) print(result.head(10))这段代码产出的danmu_features.csv有两列核心指标每分钟弹幕条数和每分钟平均情绪得分。把这两列画成双轴折线图就能看出“哪段剧情弹幕爆发但情绪负面”——这通常是编剧翻车的地方。SnowNLP的sentiments返回 0 到 1 的分数越接近 1 越正面。注意弹幕里大量“哈哈哈”“笑死”会被判为正面但“刀”“虐”这类词在影视语境下是负面通用模型可能判错需要手动加词典修正。4. 可视化落地用 pyecharts 做影视数据大屏4.1 评分分布和短评词云评分分布用柱状图最直观词云用WordCloud或pyecharts的WordCloud组件。先看评分分布把 1 到 5 分各有多少条统计出来再算一个平均分和标准差。标准差大说明口碑两极分化这比平均分本身更有信息量。from pyecharts import options as opts from pyecharts.charts import Bar, WordCloud, Line, Grid import pandas as pd import jieba df pd.read_csv(comments_clean.csv) # 评分分布 rating_counts df[rating].value_counts().sort_index() bar ( Bar() .add_xaxis([str(int(x)) for x in rating_counts.index]) .add_yaxis(评论数, rating_counts.values.tolist(), color#5470c6) .set_global_opts( title_optsopts.TitleOpts(title评分分布), xaxis_optsopts.AxisOpts(name评分), yaxis_optsopts.AxisOpts(name评论数), ) ) bar.render(rating_dist.html) # 词云先分词再统计词频 all_text .join(df[content].dropna().astype(str).tolist()) words jieba.lcut(all_text) stopwords set([的, 了, 是, 我, 你, 他, 她, 在, 就, 都, 也, 很]) word_freq {} for w in words: if len(w) 2 or w in stopwords: continue word_freq[w] word_freq.get(w, 0) 1 top_words sorted(word_freq.items(), keylambda x: x[1], reverseTrue)[:100] wc ( WordCloud() .add(, top_words, word_size_range[12, 60]) .set_global_opts(title_optsopts.TitleOpts(title短评高频词)) ) wc.render(wordcloud.html)评分分布图能直接回答“这部剧口碑到底怎么样”。如果 1 分和 5 分都很多中间 2 到 4 分很少说明观众态度极端可能是粉丝和黑粉对冲。词云里如果出现大量演员名字说明讨论集中在演员而非剧情如果出现“节奏”“逻辑”“结局”这类词说明观众在讨论剧本本身。stopwords列表需要根据实际结果不断补充影视短评里“演技”“剧情”“画面”这些词几乎每部剧都有做对比分析时可以考虑去掉。4.2 弹幕密度与情绪双轴折线图双轴折线图是影视数据分析里最有信息量的可视化之一。左轴是弹幕密度右轴是情绪得分横轴是时间。两条线一叠加高能片段和翻车片段一目了然。from pyecharts.charts import Line from pyecharts import options as opts import pandas as pd result pd.read_csv(danmu_features.csv) result[minute] pd.to_datetime(result[minute]) result result.sort_values(minute) # 取前 120 分钟避免图太密 result result.head(120) line ( Line() .add_xaxis([t.strftime(%H:%M) for t in result[minute]]) .add_yaxis( 弹幕密度, result[count].tolist(), yaxis_index0, is_smoothTrue, color#ee6666, ) .add_yaxis( 情绪得分, [round(x, 3) for x in result[sentiment].tolist()], yaxis_index1, is_smoothTrue, color#5470c6, ) .extend_axis( yaxisopts.AxisOpts( name情绪得分, min_0, max_1, positionright, ) ) .set_global_opts( title_optsopts.TitleOpts(title弹幕密度与情绪走势), xaxis_optsopts.AxisOpts(name时间), yaxis_optsopts.AxisOpts(name弹幕条数), tooltip_optsopts.TooltipOpts(triggeraxis), ) ) line.render(danmu_sentiment.html)这张图怎么读弹幕密度突然飙升的位置通常是剧情高潮或争议点如果同一时间情绪得分骤降说明观众在骂如果情绪得分维持高位说明观众在夸。我做过一部悬疑剧的分析第 8 集结尾弹幕密度是平均值的 5 倍但情绪得分从 0.7 掉到 0.3回去看弹幕内容全是“烂尾”“逻辑崩了”。这种结论直接可以给内容团队做复盘用。提示extend_axis必须放在add_yaxis之后否则右轴不显示。这是 pyecharts 的一个常见坑很多人第一次用双轴图都会卡在这里。5. 避坑与排查影视数据分析里最容易翻车的 5 个地方5.1 采集阶段接口返回 200 但数据为空现象脚本没报错resp.status_code是 200但data[comments]是空列表。原因通常是请求头缺少关键字段比如Cookie或X-Requested-With或者分页参数从 0 开始而不是从 1 开始。解决方法是回到浏览器开发者工具把请求的完整 headers 复制下来逐个对比。另一个常见原因是接口有签名参数比如sign或token这种需要逆向 JS 或者用 Playwright 直接拦截响应。5.2 清洗阶段时间字段格式不统一导致to_datetime报错现象pd.to_datetime(df[time])抛出ValueError: Unknown string format。原因是同一列里混了多种时间格式比如一部分是2024-01-15另一部分是3天前。解决办法是用errorscoerce先强制转换把无法解析的变成NaT再单独处理相对时间。不要试图用一个正则匹配所有格式分情况写函数更稳。5.3 分析阶段SnowNLP 对影视弹幕情绪误判严重现象明显是骂人的弹幕被判成正面情绪。原因是 SnowNLP 的训练语料是电商评论和影视弹幕的用词分布差异很大。解决办法是先用 200 条人工标注数据测准确率如果低于 70%就换BERT微调或者手动构建领域情感词典。我一般会维护一个影视领域词典把“刀”“虐”“寄”“烂尾”标为负面“封神”“绝了”“上头”标为正面。5.4 可视化阶段pyecharts 图表在浏览器打开空白现象render()生成了 HTML 文件但浏览器打开是白屏。原因通常是 CDN 资源加载失败pyecharts 默认从外部 CDN 拉取 JS 库。解决办法是在set_global_opts里指定init_optsopts.InitOpts(js_host本地路径)或者直接用pyecharts的离线模式。另一个原因是数据里有NaNJSON 序列化失败检查传给add_yaxis的列表里有没有空值。5.5 部署阶段数据量大了之后内存爆掉现象本地跑 1 万条数据没问题跑到 100 万条时MemoryError。原因是 pandas 默认用object类型存字符串内存占用是实际内容的几倍。解决办法是读取时指定dtype把低基数列转成category类型比如df[rating] df[rating].astype(category)。弹幕内容如果不需要全文检索可以只保留前 100 个字符。分批处理也是常见做法用chunksize参数分块读取和聚合。6. 进阶技巧用 PyQt 做一个本地影视数据看板如果你不想每次分析都开 Jupyter可以用 PyQt 加 pyecharts 做一个本地桌面看板。核心思路是用QWebEngineView加载 pyecharts 生成的 HTML再用QComboBox切换不同剧集的数据。这样运营同学不用装 Python 环境双击 exe 就能看。import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QComboBox, QVBoxLayout, QWidget from PyQt5.QtWebEngineWidgets import QWebEngineView import pandas as pd from pyecharts.charts import Bar from pyecharts import options as opts class Dashboard(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(影视数据看板) self.resize(1200, 800) self.combo QComboBox() self.combo.addItems([剧集A, 剧集B, 剧集C]) self.combo.currentTextChanged.connect(self.update_chart) self.view QWebEngineView() self.view.setHtml(self.build_chart(剧集A)) layout QVBoxLayout() layout.addWidget(self.combo) layout.addWidget(self.view) container QWidget() container.setLayout(layout) self.setCentralWidget(container) def build_chart(self, name): # 这里替换成真实数据查询 data {1分: 120, 2分: 80, 3分: 200, 4分: 500, 5分: 800} bar ( Bar() .add_xaxis(list(data.keys())) .add_yaxis(name, list(data.values())) .set_global_opts(title_optsopts.TitleOpts(titlef{name} 评分分布)) ) return bar.render_embed() def update_chart(self, name): self.view.setHtml(self.build_chart(name)) if __name__ __main__: app QApplication(sys.argv) window Dashboard() window.show() sys.exit(app.exec_())这个看板的骨架很简单下拉框切换剧集QWebEngineView渲染图表。build_chart里目前是硬编码数据实际使用时换成从 SQLite 或 CSV 读取。render_embed()返回完整的 HTML 字符串直接塞给setHtml就行。打包成 exe 用pyinstaller注意把 pyecharts 的 JS 资源一起打进去否则换台电脑打开还是白屏。我自己的习惯是每做完一个剧集的分析就把清洗后的 CSV 和生成的 HTML 图表归档到一个按剧名命名的文件夹里。三个月后回头看哪些剧的弹幕情绪曲线和评分走势背离哪些剧的短评高频词集中在演员而非剧情一目了然。这个习惯帮我省了很多重复采集的时间也让每次复盘都有据可查。希望帮到你。本文还有配套的精品资源点击获取
返回列表