ARTICLE DETAIL

资讯详情

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

B站弹幕分析实战:爬虫采集、词频统计、情感分析一条龙全解析

B站弹幕分析实战:爬虫采集、词频统计、情感分析一条龙全解析 简介面向计算机相关专业学生、毕业设计与课程设计场景的bilibili弹幕数据分析项目基于Python与Selenium完成弹幕爬取并集成词云分析、词频统计、情感分析与衍生指标构建覆盖爬虫、数据清洗、统计建模到可视化展示的完整流程。压缩包共1202个文件约26.08MB其中1152个csv文件为各分区词频与统计表涵盖全知识区、科学科普、社科人文、校园学习、财经、野生技术协会等板块另有16个Python源码、22张可视化png、6个交互式html页面以及xlsx汇总表、txt说明和md文档结构清晰便于按需查阅。目前已有91人学习下载。项目代码经过运行验证配套详细文档可直接用于答辩或课设展示对于想要入门弹幕数据分析的小白也可对照源码逐步理解爬虫与自然语言处理应用并在现有框架上扩展新功能。1. 拆解这套bilibili弹幕分析源码爬虫、词频、情感分析一条龙做课设或毕设的同学拿bilibili弹幕当素材做文本分析是一条很成熟的路线数据量大、话题感强、可视化效果好答辩时很容易讲出东西。但这个项目我拆过不少次最大的问题往往不在分析而在爬虫环节——bilibili的接口带签名校验和风控纯requests硬爬很容易翻车。这套基于Selenium的弹幕源码把整条链路做成了可直接跑通的项目从Selenium模拟登录拿Cookie、弹幕采集、分区词频表生成、词云渲染、情感分析到衍生指标构建和可视化源码加文档都齐。适合正在做课设、毕设、答辩前需要demo的计算机相关专业学生也适合想完整走一遍爬虫加文本分析流程的Python学习者。2. Selenium弹幕采集从登录态到cid再到弹幕JSON2.1 为什么这套源码选Selenium而不是裸requestsbilibili的弹幕接口本身不算复杂难点在风控。纯requests直接调接口要么返回-412请求被拦截要么拿到的是被截断的数据原因在于缺少浏览器环境生成的Cookie、UA、Referer这些上下文信息。常见做法是先用Selenium打开一次浏览器完成登录把完整Cookie拿出来再交给requests去调弹幕接口。这样既绕开了接口签名校验又比全程Selenium逐条滚动页面快得多。代码里先开浏览器拿Cookie这一步就是用登录态换采集稳定性属于弹幕采集里最稳的做法。Selenium在这里的角色是“登录器加Cookie提取器”不是采集主力采集主力是requests加弹幕分页接口。这套源码也是这个思路我在下面把主流程拆开讲。2.2 采集主流程从BV号到cid再到弹幕分页拉取弹幕采集的标准链路是输入BV号打开播放页从页面HTML里提取视频真实的cid再用cid去请求弹幕接口按页拉取直到取完。cid是关键因为弹幕接口只认cid不认BV号。from selenium import webdriver from selenium.webdriver.chrome.options import Options import requests import re import time from xml.etree import ElementTree as ET HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), Referer: https://www.bilibili.com/video/ } def get_cid(bv_id, cookies): 从播放页HTML中提取视频cid url fhttps://www.bilibili.com/video/{bv_id} resp requests.get(url, cookiescookies, headersHEADERS) m re.search(rcid:(\d), resp.text) if not m: raise ValueError(f无法从页面提取cid: {url}) return int(m.group(1)) def fetch_danmaku(cid, cookies): 分页拉取弹幕每页约120条 all_items [] page 1 while True: api fhttps://api.bilibili.com/x/v1/dm/list.so?oid{cid}page{page} resp requests.get(api, cookiescookies, headersHEADERS, timeout10) root ET.fromstring(resp.content) items root.findall(.//d) if not items: break for it in items: attr it.get(p, ).split(,) all_items.append({ text: it.text, time: attr[0] if len(attr) 0 else , uid_hash: attr[1] if len(attr) 1 else , }) page 1 time.sleep(0.5) return all_items # 主流程只开一次浏览器 options Options() options.add_experimental_option(excludeSwitches, [enable-automation]) driver webdriver.Chrome(optionsoptions) driver.get(https://www.bilibili.com) time.sleep(15) # 等待扫码或恢复已有登录态 cookies {c[name]: c[value] for c in driver.get_cookies()} driver.quit() cid get_cid(BV1xx411c7mD, cookies) danmaku fetch_danmaku(cid, cookies) print(len(danmaku))这段代码的逻辑是先开一次Chrome拿到完整Cookie然后从播放页HTML里用正则抠出cid最后用list.so接口按页拉弹幕。list.so返回的是XML用ElementTree解析每条弹幕的p属性里前两个字段分别是出现时间和用户ID哈希。页面里的time.sleep(15)是给登录态留时间如果你本地已经登录过bilibili这一步会直接读到会话如果是无头环境可以在这个时间段手动扫码。参数上要注意几点HEADERS里Referer必须以https://www.bilibili.com/video/开头否则弹幕接口会拒绝page从1开始递增当某一页返回空就说明拉完了不用提前算总数time.sleep(0.5)是防止请求太密触发风控这个间隔可按实际频率调稳定优先。2.3 弹幕清洗与分区存储不要直接拿原始数据做词频弹幕原始数据里有大量噪声比如空文本、重复弹幕、非常规内容。不洗直接做词频统计结果会被“哈哈哈哈”这类词淹没。清洗分三步去空、去重复、按分区归类。import pandas as pd def clean_danmaku(items, min_len2): 清洗弹幕去空、去重复、过滤超短噪声 seen set() cleaned [] for it in items: text (it.get(text) or ).strip() if len(text) min_len: continue if text in seen: continue seen.add(text) cleaned.append({text: text, time: float(it.get(time, 0))}) return pd.DataFrame(cleaned) df clean_danmaku(danmaku) df.to_csv(danmaku_tech.csv, indexFalse, encodingutf-8-sig)清洗逻辑很简单两条规则长度小于2的不要完全重复的只留一条。length过滤是因为单字弹幕大多是语气词或无效字符重复过滤是因为同一条弹幕在视频里反复刷屏是常态如果不做去重词频会严重偏向刷屏词。编码用utf-8-sig是为了后续Excel打开不乱码。存储层面你可以按分区分别建文件比如知识区、科学科普、校园学习各存一个csv。这份资源里自带的词频表也是按分区组织的采集端和统计端的命名最好保持一致后面做对比分析时省事很多。3. 词频与词云从弹幕文本到分区词频表3.1 资源里的分区词频表先搞懂统计口径解压资源后能看到一批csv包括全知识区词频统计、科学科普词频表、社科人文词频表、校园学习词频表、财经词频表、野生技术协会词频表。这些文件已经是统计好的结果字段一般是词、词性、频次或占比。全知识区是汇总口径其余是按bilibili知识分区拆分的子集。拿到词频表先别急着可视化第一件事是确认统计口径是全量弹幕还是采样弹幕是否去掉停用词词频是绝对次数还是占比不同口径下同一句话的排名会差很多。这份资源的文档里对这些做了说明我建议你拿到后先用Excel打开看看排序前20的词再决定后面分析怎么接。3.2 jieba分词与停用词弹幕文本的预处理细节弹幕和新闻正文不一样句子短、口语化重、网络梗多直接分词会出来一堆“哈哈”“真的”“绝了”这类词。词要留下来得先加载自定义词典和停用词表。import jieba from collections import Counter # 自定义词典网络梗、UP主名、缩写词 jieba.load_userdict(bilibili_dict.txt) STOP_WORDS set(open(stopwords.txt, encodingutf-8).read().split()) def tokenize_texts(texts): cnt Counter() for t in texts: for w in jieba.cut(t): w w.strip() if not w or w in STOP_WORDS or len(w) 2: continue cnt[w] 1 return cnt texts df[text].tolist() freq tokenize_texts(texts)这里有两个关键配置。load_userdict是把“yyds”“绝绝子”这类词强行切成一个整体避免被拆成乱码len(w) 2过滤的是单字词对短文本弹幕来说单字多数是语气助词留着只会干扰词云。停用词表需要你自己按弹幕场景维护把“哈哈哈哈”“啊啊啊”“哈哈哈”这类刷屏词加进去否则词频榜前排全是它们。3.3 词云生成的关键参数中文字体与去二元组词云是课设和答辩里最直观的输出但中文词云有个老坑默认字体不支持中文生成出来全是方框。另外WordCloud默认会做二元词组拼接对弹幕这种短文本反而会拼出“真的绝”“笑死我了”这类噪声组合。from wordcloud import WordCloud wc WordCloud( font_pathmsyh.ttc, # 指定中文字体否则中文显示为方框 width1200, height800, max_words200, background_colorwhite, colormapviridis, collocationsFalse, # 关闭二元组避免噪声组合词 ) wc.generate_from_frequencies(dict(freq)) wc.to_file(danmaku_wordcloud.png)参数里最值得注意的是collocationsFalse。这个开关默认是True会让WordCloud自动组合高频相邻词放在弹幕场景下很容易拼出没有意义的词组关闭后词云会纯粹按词频展示。font_path用微软雅黑或SimHei都行路径要和系统实际字体位置对齐Windows一般直接用msyh.ttc没问题。4. 情感分析与衍生指标从弹幕文本到视频质量的量化4.1 弹幕情感分析词典打分和模型哪个合适弹幕情感分析有两个路线词典打分和机器学习模型。词典法用情感词典查词给每个词打分累加模型法用训练好的模型预测情感极性。对弹幕这种场景我一般用词典法打底原因有三个弹幕太短模型很难从十几个字里判断语境网络用语更新快训练模型的数据集很快过期词典法的结果可解释性强答辩时能讲清楚每个词的分数来源。SnowNLP这类现成库可以直接算情感分数但它的训练语料偏向购物评论在弹幕上经常把“绝了”“牛”这类词判成中性或负面。词典法的做法是加载一份情感词典遍历弹幕把命中的词分值加起来再做归一化。4.2 情感打分与分区聚合的代码实现单条弹幕的情感分意义不大因为太短且口语化真正有价值的是把一个视频或一个分区的所有弹幕聚合起来看情感分布。import math # 加载情感词典格式词\t分值 lexicon {} with open(boson_lexicon.txt, encodingutf-8) as f: for line in f: parts line.strip().split(\t) if len(parts) 2: lexicon[parts[0]] float(parts[1]) NEGATION {不, 没, 别, 无, 莫, 不要} def score_danmaku(text): 单条弹幕情感打分用tanh归一化到(-1,1) words jieba.lcut(text) s 0.0 neg 1.0 for w in words: if w in NEGATION: neg -1.0 if w in lexicon: s neg * lexicon[w] neg 1.0 return math.tanh(s) df[sentiment] df[text].apply(score_danmaku) agg df.groupby(pd.cut(df[time], bins20))[sentiment].mean()这段代码的核心是逐条打分后按时间窗口聚合。time字段是弹幕在视频中出现的时间点用pd.cut切成20个窗口再对每个窗口算平均情感分就能画出一条情感随时间变化的趋势线。tanh归一化把分数压到-1到1之间防止某条弹幕因为命中多个高分词导致数值爆炸。否定词处理上代码用了一个简单的“遇否定词翻转”规则这是词典法的常见优化。注意这条规则很粗糙“不”和“没”出现时会翻转后续词的极性对于“不开心”这类词能正确变负但对“不是一般的厉害”这种双层否定就会判断错。课设场景这个精度够了如果要做更细得加规则判断否定词和情感词之间的距离。4.3 衍生指标弹幕密度、互动率与情感波动率原始弹幕数据本身是文本只有把它转成数字指标才方便横向对比不同视频。这套源码里构建的衍生指标主要围绕密度、互动和情感三个维度。指标计算公式业务含义弹幕密度弹幕总数 / 视频时长分钟视频内容对观众的情绪触发强度互动率弹幕总数 / 播放量观众愿意发弹幕参与互动的比例情感均值所有弹幕情感分的算术平均视频整体情绪倾向情感波动率情感分的标准差视频情绪起伏程度如情绪反转或高潮段弹幕密度高说明某个视频的“槽点”或“燃点”密集观众不断发弹幕反馈。互动率比单纯的播放量更有参考价值两个播放量相当的知识区视频互动率高的那个内容讨论度明显更好。情感波动率则是做内容分析时很好用的维度知识区视频的情感波动一般比野生技术协会区小因为前者情绪基调更平稳后者会随项目成败大起大落。这几个指标算出来后配合可视化模块就能直接在答辩PPT里放一张多指标对比图比单纯贴词云图更有说服力。5. 可视化落地与高频问题排查教你怎么调通图表5.1 可视化模块的落地顺序词云、柱状图、情感趋势可视化这部分资料在资源里给得比较全实际落地时我建议按“词云 → 词频柱状图 → 情感趋势线”的顺序来因为它们的依赖关系是从静态到动态调试成本依次递增。词云图是静态的出图最快用来展示整体关键词分布。词频柱状图用来比较不同分区的热词差异比如财经区和野生技术协会区的词频差异用横向柱状图最直观因为分区名通常较长纵向柱状图会被截断。情感趋势线则依赖时间窗口聚合用它来展示视频的情感起伏节奏。图表层面的核心是把DataFrame对接好词云需要的是词到频次的字典柱状图需要的是词和频次两列趋势线需要的是时间窗口和情感均值。对接关系理清后主流的matplotlib、pyecharts、ECharts都能直接渲染没必要在选型上纠结。5.2 高频踩坑记录现象、原因、解决这一小节列几个我在跑弹幕项目时反复踩过的坑每条都是实际调试经验。坑一Selenium打开bilibili直接弹滑块验证现象浏览器刚打开bilibili首页页面出现“请按住滑块拖动到最右边”的验证框程序卡住。原因Chrome的webdriver特征被网站风控识别或者短时间内从同一IP多次发起访问触发风控。解决在ChromeOptions里加excludeSwitches排除自动化提示这是最常用的一步另外不要每次运行都重新登录尽量保持持久登录态访问频率上控制一下别用同一台机器短时间刷大量视频。坑二弹幕接口返回code为-412现象requests请求list.so接口时返回的状态码是200但响应里没有弹幕XML而是一段“请求被拦截”的文本或code为-412的JSON。原因请求头里缺少Referer或者Cookie过期。bilibili的弹幕接口会校验来源页面Referer不是从视频页带过去就直接拦。解决把完整请求头带上User-Agent不能用默认的python-requestsReferer固定写成https://www.bilibili.com/video/开头。Cookie过期的话重新执行一次Selenium登录流程把新的Cookie替换进去。坑三词云生成出来中文全是方框现象词云图片能生成但所有汉字显示成一个个方框。原因WordCloud默认字体不包含中文字形需要手动指定中文字体路径。解决在WordCloud构造函数里加font_path参数指到系统的中文字体文件Windows用msyh.ttc或simhei.ttfMac用PingFang.ttc。忘记配这个参数是词云中文方框最常见的翻车点。坑四情感分析把“yyds”判成负面现象一条弹幕内容是“yyds”情感打分算出来是负值明显不合理。原因情感词典里没有收录“yyds”“绝绝子”这类新词词典不覆盖时就按0处理如果分词把它们切成了单字而单字命中词典里的负面词比如“y”没法切“绝”被拆分就会给出错误分数。解决把常用网络梗加进自定义词典同时给它们预设情感权重。比如在bilibili_dict.txt里加“yyds”再在情感词典文件里追加一行“yyds 3.0”让新词能正确参与打分。6. 验证方法与进阶扩展把弹幕分析做成可持续的小工具整套链路跑通后先别急着加功能花半小时做一次全流程验证。挑一个科普区的热门视频跑完采集到情感的完整流程然后看三个结果词频榜前20是否符合常识情感均值是否落在一个合理区间情感趋势线有没有随时间起伏。如果前20的词里有大量“哈哈哈”这类刷屏词说明停用词表还要补如果情感均值接近0可能是打分规则对短文本失效检查一下是否分词后丢词严重。这个资源结构上已经把爬虫、词频、词云、情感、衍生指标都做齐了往上扩展空间不小。我建议往“多视频对比”和“定时采集”两个方向延伸。多视频对比是把多个视频的衍生指标拉出来横向比选播放量和弹幕数相差不大的视频做配对看哪个互动率更高。定时采集用APScheduler每天凌晨跑一次把新视频的弹幕增量追加到既有csv里积累一个月就能做弹幕量随时间变化的趋势分析这是课设答辩里很加分的点。一个更实用的扩展是情感时间线和视频内容对齐。先把弹幕的情感分按时间窗口聚合再对照视频章节节点看“Introduction”“核心讲解”“总结”这几个节点的情感均值差异。实操下来知识区视频的总结段弹幕情感往往高于核心讲解段因为观众在这里情绪释放最充分。我自己的习惯是从那以后每次跑脚本都强制走一遍三件事确认Cookie写入成功、确认cid不为零、确认csv写入条数和采集条数一致。这三项过完再往下做词频和情感能省掉大部分返工。希望这套实战经验帮到你拿到这份资源后少走点弯路。本文还有配套的精品资源点击获取
返回列表