B站数据分析实战:Python爬虫+词云+趋势图全流程教程

发布时间:2026/10/11 20:54:53
B站数据分析实战:Python爬虫+词云+趋势图全流程教程
做 B 站数据分析这件事我琢磨了很久。一开始是在某个科技区的视频下面看评论发现大家吵的东西很有意思弹幕刷屏的内容也很有代表性就想着能不能把这堆零散的文字变成能看懂的趋势图表。后来试了一圈发现最顺手的路径其实就是标题里那个组合爬虫拿数据、词云看热度词、趋势图看涨跌节奏。整套流程做完数据从无到有再从有到看得懂对新手来说是一条正反馈来得最快的路。这篇内容适合谁刚接触 Python 没多久、想找一个完整练手项目的同学或者想摸清 B 站某个分区、某个话题热度规律的内容创作者。不需要你有爬虫基础也不需要你懂多深的数据分析能装环境、能跑代码就行。我会把每一步为什么要这么做、有哪些坑提前给你指出来按照这个流程走下来你得到的不仅是一张图和一堆词而是整套能迁移到其他平台的分析思路。1. 项目整体设计与思路拆解1.1 为什么选“爬虫 词云 趋势图”这个组合很多人一上来就想爬全站数据或者把所有视频的弹幕都抓一遍。这个目标听着很酷但实际跑起来会发现存储撑不住、接口频次限制、清洗数据洗到怀疑人生。新手做数据分析最难的不是工具而是“把问题变小”。我最后把项目的范围收窄成一个可执行的问题选择某个特定主题比如某个热点话题、某个单集动画、某个活动标签抓取它相关的公开视频信息与评论内容用词云看出大家讨论的焦点用趋势图看出热度随时间的变化。这样一个闭环链路既覆盖了数据获取、数据清洗、文本分析、可视化这几个核心环节又不会让数据量大到超出学习范围。词云负责回答“大家说了什么”趋势图负责回答“什么时候在说”数据采集则负责把这两个问题的原料端上来。三者分开看都很简单串起来就形成了完整的分析能力。这也是我把这个组合推荐给新手的原因每一步都能独立验证结果不会出现跑了半天不知道对错的情况。1.2 数据源与字段范围的取舍B 站能拿到的公开数据其实比想象中要多但很多新手会栽在这里什么都想抓抓完不知道怎么用。我开始做的时候列了十几个字段包括视频标题、播放量、弹幕数、点赞数、投币数、收藏数、分享数、发布时间、UP 主评论内容、评论时间、评论点赞数。字段太多带来两个问题存储冗余和任务线拉长。实际跑下来后我把字段精简为两组。第一组是视频维度标题、播放量、发布时间、弹幕数。这组数据用来画趋势图和判断热度起伏。第二组是评论文本维度评论内容、评论时间。这组数据用来做语言清洗和词云展示。就这八个不到的字段已经足够回答大部分新手关心的问题。提示字段选择有一个原则——先想清楚你要回答什么问题再回头定字段。反过来做的话你大概率会陷入“收集了一堆数据但不知道下一步干什么”的困境。1.3 技术栈与工具选型背后的理由技术选型我走的是最省心的路线Python requests pandas jieba wordcloud matplotlib。这几个库都是数据分析和中文处理领域非常成熟的方案以下几个选型理由你可以参考requests简单直接能快速拿数据不像某些框架要重新学一套逻辑。pandas数据处理的事实标准尤其在做时间序列聚合的时候一行代码就能完成按日统计。jieba中文分词方案里最傻瓜化的选择。其他分词工具不是不好但对小白来说初始化配置就已经劝退了。wordcloud生成词云图的经典库支持 shape_mask、配色、字体设定能做出好看的成品图。matplotlib趋势图的主力库。虽然 plotly 的交互效果更好但 matplotlib 从零理解和修改的成本更低。不需要上分布式的、高并发的那些工具。这个项目的数据量级决定了一台普通电脑、一个 Python 脚本就足够了。过早引入复杂工具只会让新手在环境的坑里来回折腾。2. 核心细节解析与实操要点2.1 公开数据的安全获取边界关于爬虫有一点必须说在最前面我们只获取对所有人可见的公开信息不碰用户私密数据不绕过平台的任何访问限制不提交恶意高频请求。在实操层面我的做法是抓取“网站向所有访客展示的公开列表页数据”。你可以打开浏览器开发者工具的网络面板刷新页面找到那个返回列表数据的请求观察它的请求参数和返回结构。正常看到的数据就是这个页面本身加载时就会请求的数据是网站公开内容的一部分。我们做的事情相当于让程序去完成“打开页面、看数据、记录下来”这个过程只是看得更快一些。在这个前提下仍然要遵守几个底线原则请求频率控制在人类社会正常浏览水平我实测下来每次请求间隔不少于两秒避免给服务器造成压力。只做只读操作不进行任何提交、登录、修改动作。数据用途限定个人学习与统计分析不用于商业用途。不公开他人隐私信息分析时只聚焦整体趋势。2.2 请求头伪装与频率控制很多第一次写爬虫的朋友会遇到“代码明明没问题但返回数据异常或直接被拒绝”的情况。大部分原因不是 IP 被封而是请求没有带浏览器身份标识。服务器看到的是一个陌生的、不带任何信息的程序请求出于安全策略自然不会给数据。解决办法很简单在请求里带上常规的浏览器请求头信息模拟正常访客行为。这里有几个关键字段需要填User-Agent告诉服务器“我是一个浏览器”。Referer告诉服务器“我是从哪个页面跳转过来的”。Accept-Language告诉服务器“我期望什么语言的内容”。构造好的请求头之后再配合一个请求间隔。我会在代码里写一个time.sleep()的函数调用每次请求之间强制休息两秒以上。为什么是两秒因为大多数站点的访问日志里真人连续点击页面也不会低于一秒一次两秒是个既不浪费时间、也不触发风控的稳妥数值。2.3 数据清洗与字段规范化拿到的原始数据就像刚从菜市场买回来的菜带着泥土和黄叶子得先收拾一遍再下锅。我在这个项目里总结出三项必须做的清洗工作第一评论去重。评论区里经常出现“哈哈哈哈”刷屏或者同一句口号被复制几十万遍。如果不做去重词云里会被这些重复内容直接淹没。我处理的方法是先做全量去重再做基于相似度的过滤。比如连续重复超过两次的短句合并成一条统计。第二过滤无关符号。评论里夹杂的大量表情符号、链接、用户、星号乱码分词之后都会变成垃圾 token影响词云的可读性。我用正则表达式把这些内容统一替换成空格。第三时间字段格式化。视频发布时间原始数据可能是时间戳也可能是带时区的格式。不做格式化后面按天聚合趋势图的时候会报类型错误。这里统一转换成 年-月-日 格式并作为后续分组统计的字段。实操心得清洗会比采集更花时间。第一次做这个项目时我把八成的时间都花在了清洗上。别急着跳过这一节数据洗干净了后面的词云和趋势图才会好看、可信。3. 实操过程与核心环节实现3.1 环境准备开始之前先确保本机安装了 Python 3.8 以上版本。命令行执行下面这个指令能看到版本号就说明环境没问题python --version然后安装项目所需的库。我建议用pip install一条一条装出问题的时候好定位pip install requests pandas jieba wordcloud matplotlib如果安装 wordcloud 时提示缺少 Visual C 环境手动去装对应版本的构建工具就行这一步没有捷径。我建议在正式跑数据之前先拿一本国内小说的评论试着生成一次词云确认环境全部就绪再进入完整流程。3.2 数据采集代码实现数据采集是整个流程的源头我分两个函数来做。一个是获取列表页数据的方法一个是解析并保存数据的模块。import requests import time import csv HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://www.example.com/, Accept-Language: zh-CN,zh;q0.9, } def fetch_page(page_num): params { page: page_num, page_size: 20, } resp requests.get(https://api.example-data.com/search, paramsparams, headersHEADERS, timeout10) if resp.status_code ! 200: print(f请求失败状态码{resp.status_code}) return None return resp.json()这段代码里有三个细节值得注意。第一个是timeout10设置请求超时时间避免网络卡住时程序一直挂着不动。第二个是状态码判断只有返回 200 才继续防止把错误页面当成数据去解析。第三个是参数拼接方式用params传参而不是直接在 URL 里拼字符串这样更清晰且能自动转义特殊符号。拿到数据之后需要从嵌套的 JSON 里把目标字段抽出来。B 站这种结构化的返回数据通常视频信息在某个固定的层级下评论信息在另一个接口里。我用一个通用的遍历方式去解析def parse_video_items(raw): items [] for item in raw.get(data, {}).get(list, []): items.append({ title: item.get(title), play_count: item.get(play_count), pub_date: item.get(pub_date), danmaku_count: item.get(danmaku_count), }) return items这里给新手一个提醒不同页面返回的字段名可能不相同甚至同一个字段在不同页面叫法都不一样。正确做法是先打印一次返回结果的原始 JSON肉眼看清结构再写解析代码。不要盲目相信网上的教程字段名。采集到一定量之后及时存成 CSV。存 CSV 的时候有个容易忽略的地方newline。Python 里打开 CSV 文件不指定这个参数写入时会出现空行。with open(data.csv, w, newline, encodingutf-8) as f: writer csv.DictWriter(f, fieldnames[title, play_count, pub_date, danmaku_count]) writer.writeheader() writer.writerows(video_items)3.3 评论采集与文本清洗评论数据比视频信息麻烦一点因为评论内容往往分页加载而且内容中存在大量噪声。我设计了一个两遍清洗的策略。第一遍在采集阶段过滤长度小于两个字的评论直接丢弃只保留普通字符、中文、数字、常用标点把换行符和多余空格压缩掉。第二遍在分析阶段过滤用正则去噪匹配掉用户名、http链接、纯表情符号区域。import re def clean_comment(text): text re.sub(r\w, , text) text re.sub(rhttp\S, , text) text re.sub(r[^\u4e00-\u9fa5a-zA-Z0-9。、,.!?], , text) text re.sub(r\s, , text) return text.strip()清洗完毕后再把评论按时间分组。趋势图需要的是“每一天有多少条评论”或者“每一天某个关键词被提及多少次”这类时序数据因此把评论时间转成日期格式然后分组统计import pandas as pd df pd.read_csv(comments.csv) df[comment_time] pd.to_datetime(df[comment_time]) daily_count df.groupby(df[comment_time].dt.date).size().reset_index(namecount)3.4 中文分词与词云生成分词这一步新手最常见的误区是直接拿原始评论文本去生成词云。这也行但效果会很差——因为“的”“了”“是”“在”这些停用词会霸占词频榜。我用的处理流程是先分词、再过滤停用词、最后统计词频。import jieba from collections import Counter stopwords set() with open(stopwords.txt, r, encodingutf-8) as f: for line in f: stopwords.add(line.strip()) words [] for comment in df[clean_content]: for word in jieba.cut(comment): if word.strip() and word not in stopwords and len(word) 1: words.append(word) word_counts Counter(words).most_common(100)需要注意默认的 jieba 分词偶尔会出现收尾不完整的词比如“这个视”“频不错”。这类噪声会在词云里显示为碎片词。想解决这个问题添加自定义词典或者把常见网感词加进 jieba 的词库。例如jieba.add_word(一键三连) jieba.add_word(泪目) jieba.add_word(破防了)生成词云图的代码from wordcloud import WordCloud import matplotlib.pyplot as plt wc WordCloud( width1920, height1080, background_colorwhite, font_pathC:/Windows/Fonts/simhei.ttf ) wc.generate_from_frequencies(dict(word_counts)) plt.imshow(wc, interpolationbilinear) plt.axis(off) plt.savefig(wordcloud.png, dpi300)词云图最容易踩的坑就是中文显示成方框。原因一定是font_path没设置或设置错误。Windows 系统下可以指定simhei.ttfMac 下可以指定PingFang.ttc。这一步没有中间路线字体路径设不对词云图必出乱码。3.5 趋势图绘制趋势图要回答的问题很简单这个主题的热度是涨了还是跌了在什么时候达到峰值。我用评论数量随日期的变化来代表热度。daily_count daily_count.sort_values(bydate) plt.figure(figsize(14, 6)) plt.plot(daily_count[date], daily_count[count], markero, linestyle-) plt.title(热度趋势图) plt.xlabel(日期) plt.ylabel(评论数量) plt.grid(True, alpha0.3) plt.xticks(rotation45) plt.tight_layout() plt.savefig(trend.png, dpi300)这段代码里有一个细节plt.xticks(rotation45)。X 轴日期文字较多的时候不旋转会全部重叠在一起。tight_layout则是自动调整边距避免标签被裁剪。如果趋势图上数据波动太厉害、看着像心电图可以通过移动平均来平滑daily_count[smooth] daily_count[count].rolling(window3, win_typetriang).mean() plt.plot(daily_count[date], daily_count[smooth], colororange, label平滑曲线)窗口设为 3 表示每三个点的平均值作为当前点的值适合做短周期内的趋势判断不会抹掉关键的峰值。4. 常见问题与排查技巧实录4.1 请求返回的数据和浏览器看到的不一致这个现象很多新手遇到过。页面里明明有视频列表代码请求回来的却是空数组。原因基本是请求参数里少了一两个关键参数。浏览器加载的时候发出的请求参数是完整齐全的而我们自己拼参数时容易漏掉page_size、order这类的必填项。解决办法是打开开发者工具的 Network 面板找到翻页时新产生的那个请求对照看它的 Query String Parameters 有哪些凑齐再发。这也是我在实操中最常用的调试手段。4.2 数据保存到 CSV 后中文乱码CSV 里的中文变成乱码通常是编码问题。用文本编辑器打开正常用 Excel 打开乱码那么可以改用utf-8-sig编码来保存文件这样 Excel 就能正确识别。with open(data.csv, w, encodingutf-8-sig, newline) as f:4.3 词云生成时出现“Font error”字体错误的报错信息看起来很长其实核心就一句找不到指定路径下的字体文件。如果不想依赖固定绝对路径可以用系统字体模块来自动定位。在 Windows 上使用matplotlib的字体管理器查找系统字体是一个更稳妥的方案。这样代码携带到其他电脑也能正常运行。4.4 趋势图时间轴乱序或日期显示不全如果分组结果有多余的空日期趋势图会从中间断掉。想补全日期序列用pd.date_range生成连续日期并左连接即可实现。时间轴显示不全的问题是日期标签重叠导致增大画布尺寸、降低刻度数量就能解决。4.5 采集任务跑太慢可不可以多线程加速短回答可以但新手不建议。多线程调试难度提升一个量级而且频率一旦控制不好就容易遇到限制。这个数据集量级下单线程跑几百条数据也就几分钟的事情不需要为了缩短几秒时间把稳定性搭进去。等流程完全跑通以后想优化性能再考虑多线程也不迟。5. 从数据到结论延伸做法的参考当词云和趋势图都生成后项目其实才完成了一半。更有价值的是针对结果做简单的对比分析。比如把分词结果按时间切片对比该主题在发布前三天和发布一周后讨论焦点是否发生了迁移。我在实操中做过一个有意思的分析某视频刚发布时词云输入里的高频词是“期待”“蹲一个”三天后变成了“还原”“特效”一周后又变成了“二刷”“细节”。如果不做时间维度上的拆解只生成一张总词云这些内容是挤在一起的什么结论也看不出。另一种延伸是把趋势曲线分段做回归分析比如计算前后三个时间段内的平均值和标准差。标准差能看出讨论热度是否集中、有没有爆发式传播。这些操作不需要额外引入新的库pandas 全都支持完全可以顺着现有代码往下写。还有一个小技巧把词云图和高频词 Top20 表格放在趋势图的峰值节点旁边分析时就能直接回答“热度最高的时候大家聊的是什么”这个关键问题。最后说一点个人经验爬虫加数据分析这类项目最忌讳埋头写代码。我在跑了很多遍之后发现数据采集技术只是最表层的东西真正决定项目上限的是你对业务的理解和问题拆分的能力。当你能把一个模糊的“想看 B 站数据”拆成“观察某主题讨论焦点的变化趋势”这个清晰问题时代码写起来自然就顺了。对新手来说完成这个项目的意义不在于做出了漂亮的图而在于你走通了一条从采集到清洗到分析再到可视化的完整链路。以后无论面对什么平台、什么格式的数据这条链路随时可以复刻、调整、迁移。