20252415 2025-2026-2 《Python程序设计》实验四报告

20252415 2025-2026-2 《Python程序设计》实验四报告

课程:Python程序设计
班级:2524
姓名:陈至晟
学号:20252415
实验教师:王志强
实验日期:2026年6月6日
必修/选修:公选课


目录

  1. 项目概述
  2. 需求分析
  3. 系统设计
  4. 实现过程
  5. 实验结果
  6. 遇到的问题及解决方案
  7. 不足与改进方向
  8. 课程总结与学习感悟
  9. 参考资料

1. 项目概述

1.1 选题背景

B站(bilibili)作为国内最大的视频弹幕网站,评论区汇聚了大量用户对视频内容的情感表达。这些评论数据对内容创作者和品牌方具有重要的参考价值,但手动分析成千上万条评论既耗时又不现实。本系统实现了一套完整的评论舆情分析工具:自动爬取指定B站视频的评论数据,通过三种不同的情感分析方法对评论进行情感倾向判断,并以交互图表和词云的形式直观展示分析结果。

1.2 项目文件结构

├── main.py                          # 命令行入口
├── app.py                           # Streamlit Web 仪表盘
├── config.py                        # 全局配置 + Cookie持久化工具
├── analysis/
│   ├── preprocess.py                # 文本预处理(清洗/分词/去重/停用词过滤)
│   └── sentiment.py                 # 情感分析(三种方法 + 融合优化)
├── crawlers/
│   └── bilibili.py                  # B站评论爬虫
├── visualization/
│   └── charts.py                    # 可视化图表 + 词云生成
├── data/
│   ├── .bilibili_cookie             # Cookie持久化文件
│   ├── sentiment_dict.txt           # 情感词典(29,634 词)
│   ├── stopwords.txt                # 停用词表(6,320 词)
│   └── training_data.csv            # 贝叶斯训练数据(687 条,自循环扩增)
└── output/
    ├── *.html                       # pyecharts 交互图表
    ├── *_交互.html                  # 交互式词云
    ├── *.png                        # WordCloud 静态词云
    ├── sentiment_results.csv        # 分析结果数据
    └── logs/app.log                 # 运行日志

2. 需求分析

2.1 功能需求

需求 说明
评论爬取 通过BV号或OID自动抓取B站视频评论区,支持翻页
文本预处理 清洗、去重、分词、去停用词
多方法情感分析 SnowNLP + 情感词典 + 朴素贝叶斯三种方法并行分析
综合判定 置信度加权融合三方法结果,动态阈值分类
可视化展示 饼图、柱状图、词云等交互式图表
训练数据闭环 自动收割高置信样本,持续提升模型效果
Cookie持久化 首次输入后自动保存,避免重复获取
Web仪表盘 Streamlit图形化操作界面
数据导出 支持CSV下载分析结果

2.2 其它需求

  • 爬虫请求频率控制(1.5秒间隔),避免被B站限流
  • 文件冲突自动处理(被占用时加时间戳另存)
  • Cookie过期检测与引导更新
  • 日志记录便于问题追踪

3. 系统设计

3.1 系统架构

系统采用模块化分层架构:

┌─────────────────────────────────────────────────────────┐
│                      交互层                              │
│       main.py (CLI)              app.py (Streamlit)     │
├─────────────────────────────────────────────────────────┤
│                      分析层                              │
│    preprocess.py         sentiment.py                   │
│    (清洗/分词/去重)       (SnowNLP/词典/贝叶斯/综合)     │
├─────────────────────────────────────────────────────────┤
│                    可视化层                              │
│    charts.py                                            │
│    pyecharts交互图表  +  WordCloud词云                   │
├─────────────────────────────────────────────────────────┤
│                    数据层                                │
│    crawlers/bilibili.py    config.py                    │
│    B站API调用 / CSV保存  全局配置 / Cookie管理           │
│    sentiment_dict.txt      stopwords.txt                │
│    29,634词情感词典        6,320词停用词表               │
└─────────────────────────────────────────────────────────┘

3.2 工作流程

B站API  →  原始CSV文件
                ↓
          [正则清洗] → [去重] → [jieba分词] → [去停用词]
                ↓
        ┌───────┼───────┐
        ↓       ↓       ↓
    SnowNLP   词典   朴素贝叶斯
        ↓       ↓       ↓
        └── 加权融合 ────┘
                ↓
            综合数据
                ↓
     [统计摘要]  [交互图表]  [词云PNG/HTML]

3.3 模块职责

模块 文件 功能
配置 config.py 路径、爬虫参数、情感阈值、Cookie保存
爬虫 crawlers/bilibili.py B站评论API调用、BV号转OID、翻页抓取
预处理 analysis/preprocess.py 正则清洗、jieba分词、停用词过滤、去重
情感分析 analysis/sentiment.py 三种分析方法 + 置信度加权融合 + 自适应阈值 + 自训练收割
可视化 visualization/charts.py pyecharts交互图表 + WordCloud词云
入口 main.py 命令行参数解析、交互式输入
仪表盘 app.py Streamlit Web界面

3.4 情感分析策略对比

方法 原理 优点 缺点
SnowNLP 基于贝叶斯模型的电商评论语料预训练 可直接使用,判别性强 领域偏差(B站≠电商)
情感词典 29,634词外部词典逐词加权评分,含否定词链+程度副词 可解释性强,覆盖面极广 隐式情感/讽刺仍难覆盖
朴素贝叶斯 TF-IDF + MultinomialNB三分类,687条标注 可随训练数据持续进化 相对保守

4. 实现过程

4.1 评论爬取

通过 B站官方 API x/v2/reply 分页获取评论,按热度排序,支持 BV 号自动转 OID:

# crawlers/bilibili.py
url = f"https://api.bilibili.com/x/v2/reply?pn={page}&type=1&oid={self.oid}&sort=2"
resp = requests.get(url, headers=self.headers, timeout=TIMEOUT)
data = resp.json()
# 防御:API 返回 data: null 时优雅停止而非崩溃
reply_data = data.get("data")
if reply_data is None:
    return []
for reply in reply_data.get("replies", []):
    results.append({
        "用户名": reply["member"]["uname"],
        "评论内容": reply["content"]["message"],
        "点赞数": reply.get("like", 0),
    })

Cookie 管理:首次运行时提示用户从浏览器 F12 → Network 复制 Cookie,自动保存到 data/.bilibili_cookie 文件。后续启动自动加载,无需重复输入。爬虫结束后自动检测 Cookie 是否过期(仅获取到 ≤3 条评论且翻页返回 data: null),如过期则输出更新指引。

4.2 文本预处理

预处理流水线(analysis/preprocess.py):

  1. 正则清洗:去除 URL、[表情]#话题#@提及、非中英文字符
  2. 去空去重:删除空文本和完全重复的评论
  3. jieba分词:精确模式分词
  4. 停用词过滤:从 data/stopwords.txt 加载 6,320 个停用词,来源为哈工大停用词表 + 百度停用词列表 + 四川大学机器智能实验室停用词库 + 中文停用词库 去重合并
  5. 特征提取:添加文本长度、词数等统计特征

4.3 三种情感分析方法

方法1:SnowNLP

直接调用 SnowNLP(text).sentiments 获得 0~1 情感分数。底层基于朴素贝叶斯模型,使用中文电商评论语料(好评/差评)预训练。判别性强但存在领域偏差——B站评论与电商评论的语言风格差异显著。

方法2:情感词典

基于 29,634 词的外部情感词典(data/sentiment_dict.txt),每个词赋予权重分。词典来源:

来源 词数 说明
项目自建 ~500 B站网络用语 + 通用情感词,人工标注权重
NTUSD(台大) 10,206 正负各约 5k,覆盖面最广
HowNet(知网) 7,475 情感词 + 评价词 + 程度词,语义分类精细
大连理工 11,522 七分类情感本体库,含 1~9 级强度标注

核心算法实现了否定词链程度副词调制——否定词(如"不"、"没有")连锁翻转后续情感词极性,遇到标点自动切断;程度副词(如"非常"×2.0、"有点"×0.7)调节紧邻的情感词强度:

# analysis/sentiment.py — 情感词典评分核心
_NEGATORS = {"不", "没", "别", "从未", "绝不", "毫不", "毫无", ...}  # 21词
_DEGREE_ADVERBS = {
    "非常": 2.0, "超级": 2.5,  # 强化
    "有点": 0.7, "稍微": 0.5,  # 弱化
    "挺": 1.3, "比较": 0.8,    # 适中
    # ... 共32词
}

def score(self, text: str) -> float:
    words = list(jieba.cut(text))
    total, count = 0.0, 0
    multiplier, negate = 1.0, 1.0
    for w in words:
        if w in self._NEGATORS:
            negate *= -1                    # 翻转后续情感极性
        elif w in self._DEGREE_ADVERBS:
            multiplier = self._DEGREE_ADVERBS[w]  # 程度调制
        elif w in self.word_scores:
            total += self.word_scores[w] * multiplier * negate
            count += 1
            multiplier = 1.0                # 情感词消费后重置
            negate = 1.0
        elif w in {",", "。", "!", "?", ";"}:
            negate = 1.0                    # 标点切断否定链
            multiplier = 1.0
        else:
            multiplier = 1.0                # 非情感词重置乘数
    raw = total / max(count, 1)
    return max(0.0, min(1.0, (raw + 2) / 4))

方法3:朴素贝叶斯

使用 TfidfVectorizer(max_features=5000)+ MultinomialNB 构建三分类器:

# analysis/sentiment.py — 贝叶斯模型训练与概率映射
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB

# 训练:分词后 TF-IDF 向量化 → 多项式贝叶斯拟合
segmented = [" ".join(jieba.cut(t)) for t in texts]
self.vectorizer = TfidfVectorizer(max_features=5000)
self.classifier = MultinomialNB(alpha=1.0)
X = self.vectorizer.fit_transform(segmented)
self.classifier.fit(X, labels)

# 预测:三分类概率加权映射为连续分数
def score(self, text: str) -> float:
    seg = " ".join(jieba.cut(text))
    X = self.vectorizer.transform([seg])
    proba = self.classifier.predict_proba(X)[0]
    classes = list(self.classifier.classes_)
    score_map = {"正向": 1.0, "中性": 0.5, "负向": 0.0}
    score = 0.5
    for cls, p in zip(classes, proba):
        score += p * (score_map[cls] - 0.5)
    return max(0.0, min(1.0, score))
  • 训练数据从 data/training_data.csv 加载(当前 687 条,含正向 304、中性 252、负向 131)
  • 分词后 TF-IDF 向量化
  • 三分类概率加权映射为连续分数:

\[score = 0.5 + \sum_{cls} P(cls) \times (map[cls] - 0.5) \]

其中 \(map = \{正向: 1.0, 中性: 0.5, 负向: 0.0\}\)

训练数据来源包含摄影、时政、赛车、学术、军武、社会评论、心理健康、创意内容等 8+ 种话题领域。

综合判定:置信度加权融合 + 自适应阈值

三种方法的结果通过置信度加权融合:分数越远离 0.5(即判断越确定)的权重越高,贝叶斯在训练数据超过 200 条时额外 ×1.5 权重。同时基于中位数绝对偏差(MAD)动态校准正/负向阈值:

# 置信度权重:越远离 0.5 越可信
def _confidence_weight(score):
    return abs(score - 0.5) * 2  # [0, 1]

# 自适应阈值:基于 MAD 动态校准
def compute_adaptive_thresholds(scores):
    med = scores.median()
    mad = max((scores - med).abs().median(), 0.05)
    pos_th = min(0.70, med + 0.5 * mad)
    neg_th = max(0.30, med - 0.5 * mad)
    # 防止阈值倒挂导致中性消失
    if pos_th - neg_th < 0.10:
        gap = 0.5 * (pos_th + neg_th)
        pos_th = min(0.70, gap + 0.08)
        neg_th = max(0.30, gap - 0.08)
    return pos_th, neg_th

4.4 可视化

交互图表(pyecharts)

图表 类型 说明
情感分布比例 环形饼图 正/中/负三维占比
情感分数分布 柱状图 0~1 分数区间分布
热门关键词 TOP20 横向柱状图 高频词排名
活跃用户 TOP10 柱状图 评论数最多用户
方法对比 分组柱状图 三种分析结果对比

词云(WordCloud + pyecharts)

每个情感类别(全部/正向/中性/负向)同时生成静态 PNG 和交互式 HTML 两种词云。HTML 词云支持悬停查看词频。词云采用 TF-IDF 风格的对比加权算法:在全语料中常见的通用词降权,在特定情感子集中集中的特征词升权,确保词云反映真实的情感特征而非通用主题词:

# visualization/charts.py — 对比加权词频
def _build_contrastive_frequencies(subset_df, full_df, text_col, min_freq=2):
    sub_freq = _word_counter(subset_df, text_col)
    full_freq = _word_counter(full_df, text_col)
    weighted = {}
    for word, count in sub_freq.items():
        if count < min_freq:
            continue
        # 独特性 = 子集频次 / 全量频次
        distinctiveness = count / max(full_freq.get(word, 0), 1)
        # 混合权重:30% 原始频次 + 70% 独特性加成
        weight = count * (0.3 + 0.7 * distinctiveness)
        weighted[word] = round(weight, 1)
    return weighted

4.5 Web 仪表盘

Streamlit 提供一站式操作界面:

  • 侧边栏选择数据来源(爬取B站 / 上传CSV)
  • Cookie配置区:无Cookie时自动展开,输入后自动保存
  • 可选分析方法的开关(SnowNLP/词典/贝叶斯可独立启停)
  • 进度条展示处理流水线进度
  • 选项卡切换查看不同图表(情感分布/关键词分析/方法对比/词云)
  • 一键下载完整分析结果CSV

B站 API 对未认证请求限制严格。系统设计了一套完整的 Cookie 管理方案:

# config.py
COOKIE_FILE = "data/.bilibili_cookie"

def load_cookie() -> str:
    if os.path.exists(COOKIE_FILE):
        with open(COOKIE_FILE, "r", encoding="utf-8") as f:
            return f.read().strip()
    return ""

def save_cookie(cookie: str):
    os.makedirs("data", exist_ok=True)
    with open(COOKIE_FILE, "w", encoding="utf-8") as f:
        f.write(cookie.strip())

过期检测逻辑(crawlers/bilibili.py):爬取结束后,若总评论数 ≤ 3 条且第 2 页 API 返回 data: null,则判定 Cookie 可能已过期,自动输出详细更新指引。

4.7 异常处理

系统对常见异常做了容错处理:

  • 文件占用:CSV/图片/词云保存时如遇文件被占用,自动添加时间戳另存
  • 网络异常:爬虫单页失败不中断整体流程,跳过当前页继续
  • API异常:B站 API 返回 data: null 时正确停止翻页,不崩溃
  • Cookie缺失/过期:提供交互式输入引导 + 自动过期检测提示
  • 字体缺失:通过 matplotlib.font_manager 自动搜索系统中文字体备用

5. 实验结果

5.1 系统主界面

系统主界面

5.2 分析结果界面

分析结果1

分析结果2

分析结果3

分析结果4

5.3 自动保存分析结果

保存结果

5.4 演示视频

点击观看演示视频

5.5 项目文件

项目全部文件


6. 遇到的问题及解决方案

问题一:一开始进行分析时没有负向评论。

解决方案:发现是词典覆盖率低 + 原本手动标注训练数据负向不足并且没有进行否定词处理。查找资料后通过合并 NTUSD(台大)、HowNet(知网)和大连理工情感词典;增加手动标注训练数据;进行否定词处理保证正确率。

问题二:词云被虚词污染。

解决方案:发现是停用词词库不足,查询后下载并去重合并了哈工大停用词表、百度停用词列表、四川大学机器智能实验室停用词库等进行预处理。

问题三:在 Cookie 过期后每次进行爬取都要重新输一遍。

解决方案:每次输入新 Cookie 都会保存在 data/.bilibili_cookie 中,下次进行爬取时会直接调用,不需要重复输入。


7. 不足与改进方向

发现了一些不足并通过询问 AI 得到了改进方向:

  • 子评论(楼中楼):目前爬虫仅抓取一级评论,B站 API 的嵌套 replies 字段中的楼中楼数据未采集,这部分往往包含更丰富的情感素材
  • 模型评估体系:目前缺少精确率/召回率/F1 等量化评估指标,可增设交叉验证模块
  • 时序分析:评论带有 ctime 时间戳,可增加评论情感随时间变化的趋势分析
  • 平台扩展:目前仅支持 B站 单一平台,可扩展至微博、知乎等多源采集
  • 预训练模型应用:可尝试引入 BERT 等预训练模型进行零样本情感分类,与传统方法对比

8. 课程总结与学习感悟

8.1 课程总结

  • 基本语法与变量:掌握变量命名、赋值、运算符等核心语法,建立对程序基本结构的认知。
  • Gitee的使用:学会了使用 Gitee 进行版本控制以及云端存储。
  • 基本数据类型:理解整型、浮点型、字符串、布尔型的适用场景和相互转换。
  • 流程控制语句:条件判断实现分支逻辑,循环处理重复任务,异常处理增强程序健壮性。
  • 序列类型:列表动态增删,元组不可变配置,字典键值快速查找,集合并集去重。
  • 字符串与正则表达式:在切片和 str 方法基础上,用正则模式匹配高效完成文本查找替换。
  • 函数:封装可复用逻辑,参数传入、返回值传出,降低重复与耦合。
  • 面向对象程序设计:类将数据和方法绑定,继承复用代码,组合构建灵活结构。
  • 模块:独立 .py 文件组织代码,import 按需引用,拆分大型项目为可管理的单元。
  • Socket 编程:理解 TCP/IP 协议栈,掌握客户端与服务端的网络通信编程。

8.2 学习感悟

以前的我对 python 一无所知,而 python 学习故事开始正是因为学长学姐的大力推荐以及我对 python 这门语言的好奇。经过这一整学期的学习,说不上对 python 精通,但也是小有所成了。

回想起第一堂课,强哥诙谐的教学风格让我对 python 的学习兴趣更加浓烈,也为我打开了 python 学习新世界的大门。我仍记得强哥用蛋炒饭和盖浇饭来类比面向过程和面向对象的区别(还有每节课前的神奇手势签到,印象很深刻)。之后从基础语法学到流程控制语句,从变量类型学到序列类型,从字符串与正则表达式学到函数,还有 Socket 编程等等,在老师的指引下 python 似乎并没有我想象中的那么困难。我仍然记得在我自己打出计算器 py 程序时的欣喜,还有学完 Socket 后与同学进行通讯连接并成功互发消息的激动。python 的便利与快捷似乎无处不在。

说回这次结课项目,这是对一整个学期课程学习到的知识的综合实践。虽然一定程度上借助了 AI 的力量,但是在对项目不断优化与改进的过程中也加深了我对 python 知识的理解,提高了我的实践水平。

总而言之,强哥不仅人帅而且讲的课也很好,我会带着对 python 学习的热情继续学习下去的!

8.3 一点点小建议

希望强哥在带着我们打代码时可以一起写上注释(怕有时候跟不上);课后可以分享一下优秀实验报告以及上课打的源代码,方便进行复盘总结。


9. 参考资料

[1] 大连理工情感词汇库 - GitCode

[2] 知网情感词典 (HowNet Sentiment Dictionary) - GitCode

[3] NTUSD 台湾大学情感词典 - GitCode

[4] 中文停用词表合集 (哈工大/百度/川大) - Gitee

[5] Bilibili 评论区 API 文档 - bilibili.com

[6] SnowNLP: 中文情感分析库 - PyPI

[7] jieba 中文分词库 - PyPI

[8] scikit-learn: Machine Learning in Python

[9] pyecharts: 基于 ECharts 的 Python 交互图表库 - PyPI

[10] Streamlit: 数据应用快速构建框架

[11] wordcloud: 词云生成库 - PyPI

[12] matplotlib: Python 数据可视化库

[13] Python爬取B站视频评论区情感分析:从数据采集到价值挖掘 - 腾讯云

[14] 从零掌握SnowNLP:中文情感分析与模型训练全指南 - 百度开发者中心

[15] 基于Python情感词典的文本情感分析实践指南 - 百度开发者中心

[16] 中文情感分析实战:机器学习方法与SnowNLP对比分析 - CSDN

[17] 豆瓣影评情感分析项目:朴素贝叶斯模型训练详解 - GitCode

posted @ 2026-06-15 22:16  无忆江南  阅读(22)  评论(0)    收藏  举报