Bilibili数据分析 B站爬虫|Bilibili 弹幕信息情感分析 Snow NLP 情感词典

需求分析

该代码旨在分析B站视频弹幕数据,包括情绪分类、情感分析和按秒计算弹幕数量。分析结果将作为一个CSV文件保存。

功能介绍

  1. 读取弹幕文件和词库文件。
  2. 对弹幕内容进行清洗和分词。
  3. 对弹幕进行情绪分类和情感分析。
  4. 按秒计算每个弹幕的情绪数量和情感得分。
  5. 将分析结果保存为CSV文件。

重点代码介绍

  1. 定义了document_to_list()函数,用于将文档内容读取为列表。

  2. 定义了clean_text()函数,用于对弹幕内容进行清洗。

  3. 定义了count()函数,用于计算每个弹幕的情绪数量。

  4. 定义了sentiment_score()函数,用于计算每个弹幕的情感得分。

  5. 定义了Analyze()函数,用于分析弹幕数据并将结果保存为CSV文件。

  6. 清理文本

def clean_text(text):
    # 使用正则表达式删除特殊字符
    text = re.sub(r"[^\w\s]", "", text)
    emoji_pattern = re.compile("["
                               u"\U0001F600-\U0001F64F"  # emoticons
                               u"\U0001F300-\U0001F5FF"  # symbols & pictographs
                               u"\U0001F680-\U0001F6FF"  # transport & map symbols
                               u"\U0001F1E0-\U0001F1FF"  # flags (iOS)
                               u"\U00002702-\U000027B0"
                               "]+", flags=re.UNICODE)
    text = emoji_pattern.sub(r'', text)
    return text.strip()

这个函数用于清理文本,去除特殊字符和表情符号,使得分析过程更加准确。

  1. 计算情绪
def count(cut_list):
    emo_cnt = {"anger": 0, "disgust": 0, "fear": 0, "joy": 0, "sadness": 0}
    danmuku_emotion = []
    for word in cut_list:
        if word in anger:
            emo_cnt["anger"] += 1
        if word in disgust:
            emo_cnt["disgust"] += 1
        if word in fear:
            emo_cnt["fear"] += 1
        if word in joy:
            emo_cnt["joy"] += 1
        if word in sadness:
            emo_cnt["sadness"] += 1

    emo_max = max(emo_cnt.values())

    if emo_max == 0:
        danmuku_emotion.append("none")
    else:
        for key, value in emo_cnt.items():
            if value == emo_max:
                danmuku_emotion.append(key)

    if len(danmuku_emotion) == 1:
        danmuku_emotion = danmuku_emotion[0]
    else:
        danmuku_emotion = "complex"

    return emo_cnt

这个函数用于计算情绪向量。对于给定的分词列表,它将检查每个词是否属于预定义的情绪类别,并更新情绪计数。

  1. SNowNLP情感分析
def sentiment_score(text):
    if not text or text.isspace():
        return 0  # 返回默认值,例如0
    # 使用snownlp进行情感分析
    s = SnowNLP(text)
    # 返回情感分析得分
    return s.sentiments

这个函数使用SNowNLP库进行情感分析,根据给定的文本返回情感得分。

  1. 分析函数

这是主要的分析函数,它负责读取弹幕文件,进行清理和分词,然后计算情绪和情感得分,最后将结果保存到CSV文件。

  1. 循环处理所有视频
for cid in tqdm(cid_list):
    ...
    Analyze(cid)

这部分代码负责遍历视频列表,调用分析函数处理每个视频的弹幕。在处理过程中,它会检查文件是否存在以及是否已完成分析,确保不重复处理。

注意事项

  1. 请确保已安装所需的库(例如:pandas、jieba、re、snownlp、matplotlib等)。

  2. 请确保文件路径正确,并且所需的情绪词汇表、停用词表和弹幕文件存在。

  3. 代码中的路径可能需要根据您的实际情况进行调整。

  4. 本程序的运行时间可能较长,因为它需要处理大量的弹幕数据。可以考虑使用多线程或多进程优化代码,提高运行速度。

  5. Snownlp可能无法完全准确地分析情感,您可以尝试使用其他情感分析库,以获得更准确的结果。

完整代码

下面是本文介绍的完整代码:

# 导入必要的库
import pandas as pd
import jieba
import re
from snownlp import SnowNLP
import matplotlib as mpl
import matplotlib.pyplot as plt
import os
from tqdm import tqdm

def document_to_list(path):
    # 读取文档,按行划分,返回列表
    print("正在将%s转换为文本列表, 请稍等..." % path)
    doc_list = []
    with open(path, 'r', encoding='utf-8') as doc_f:
        for line in doc_f.readlines():
            line = line.strip('\n')
            doc_list.append(line)
    return doc_list

# 对弹幕内容进行分词和清理
def clean_text(text):
    # 使用正则表达式删除特殊字符
    text = re.sub(r"[^\w\s]", "", text)
    emoji_pattern = re.compile("["
                               u"\U0001F600-\U0001F64F"  # emoticons
                               u"\U0001F300-\U0001F5FF"  # symbols & pictographs
                               u"\U0001F680-\U0001F6FF"  # transport & map symbols
                               u"\U0001F1E0-\U0001F1FF"  # flags (iOS)
                               u"\U00002702-\U000027B0"
                               "]+", flags=re.UNICODE)
    text = emoji_pattern.sub(r'', text)
    return text.strip()

def count(cut_list):
    # 闭包函数B,构建情绪向量 emo_cnt, 判断微博情绪(存在无情绪 None 和复杂情绪 [..., ...]情况)
'''
    :param cut_list:
    :return emo_cnt: 情绪向量
            wb_emo: 判断的本条微博情绪
'''
    emo_cnt = {"anger": 0, "disgust": 0, "fear": 0, "joy": 0, "sadness": 0}
    danmuku_emotion = []
    for word in cut_list:
        if word in anger:
            emo_cnt["anger"] += 1
        if word in disgust:
            emo_cnt["disgust"] += 1
        if word in fear:
            emo_cnt["fear"] += 1
        if word in joy:
            emo_cnt["joy"] += 1
        if word in sadness:
            emo_cnt["sadness"] += 1

    emo_max = max(emo_cnt.values())

    if emo_max == 0:
        danmuku_emotion.append("none")
    else:
        for key, value in emo_cnt.items():
            if value == emo_max:
                danmuku_emotion.append(key)

    if len(danmuku_emotion) == 1:
        danmuku_emotion = danmuku_emotion[0]
    else:
        danmuku_emotion = "complex"

    return emo_cnt

# SNowNLP情感分析
def sentiment_score(text):
    if not text or text.isspace():
        return 0  # 返回默认值,例如0
    # 使用snownlp进行情感分析
    s = SnowNLP(text)
    # 返回情感分析得分
    return s.sentiments

global anger_path, disgust_path, fear_path, joy_path, sadness_path
anger_path = "0_Res/anger.txt"
disgust_path = "0_Res/disgust.txt"
fear_path = "0_Res/fear.txt"
joy_path = "0_Res/joy.txt"
sadness_path = "0_Res/sadness.txt"

anger = document_to_list(anger_path)
disgust = document_to_list(disgust_path)
fear = document_to_list(fear_path)
joy = document_to_list(joy_path)
sadness = document_to_list(sadness_path)

global stopwords
stopwords = document_to_list("0_Res/stopwords_list.txt")
document_list = document_to_list("0_Res/weibo.txt")

def Analyze(cid):

    DANMUKU_PATH = f'/Volumes/SSD/Data/Danmuku/{cid}.csv'
    SAVE_PATH = f'/Volumes/SSD/Data/AnaDanmuku/{cid}.csv'

    print(cid)

    df = pd.read_csv(DANMUKU_PATH, encoding='utf-8', error_bad_lines=False)

    # 删除重复数据
    df = df.drop_duplicates()

    df['content'] = df['content'].astype(str)
    df['content_clean'] = df['content'].apply(clean_text)

    df['emo_cnt'] = df['content_clean'].apply(count)

    df['anger'] = df['emo_cnt'].apply(lambda x:x['anger'])
    df['disgust'] = df['emo_cnt'].apply(lambda x:x['disgust'])
    df['fear'] = df['emo_cnt'].apply(lambda x:x['fear'])
    df['joy'] = df['emo_cnt'].apply(lambda x:x['joy'])
    df['sadness'] = df['emo_cnt'].apply(lambda x:x['sadness'])
    df['sentiment'] = df['content_clean'].apply(sentiment_score)  
  
  
    # 将progress列的数据类型转换为浮点数  
    df['progress'] = pd.to_numeric(df['progress'], errors='coerce')  
  
    # 去除progress为空的行  
    df = df.dropna(subset=['progress'])  
  
    # 将毫秒转换为秒并向下取整  
    df['progress'] = df['progress'].astype(int)  
    df['second'] = df['progress'] // 1000  
  
    # 按照progress_seconds分组并计算每秒的angry、joy、disgust、fear和sadness值总和  
    sum_per_second = df.groupby('second')[['anger', 'joy', 'disgust', 'fear', 'sadness']].sum().reset_index()  
  
    # 将结果赋值给一个新的数据框  
    result_df = pd.DataFrame(sum_per_second)  
  
    # 按照progress_seconds分组并计算每秒的sentiment列的平均值  
    sentiment_mean_per_second = df.groupby('second')['sentiment'].mean().reset_index()  
  
    # 按照second分组并计算每秒的弹幕总数  
    danmuku_count_per_second = df.groupby('second')['content'].count().reset_index()  
  
    # 将计算得到的sentiment平均值和弹幕总数添加到result_df数据框中  
    result_df = result_df.merge(sentiment_mean_per_second, on='second')  
    result_df = result_df.merge(danmuku_count_per_second, on='second')  
  
    # 计算sentiment_count_product列的值,该值为每组的弹幕总数与sentiment平均值的乘积  
    result_df['sentiment_count_product'] = result_df['sentiment'] * result_df['content']  
  
    # 保存结果到CSV文件  
    result_df.to_csv(SAVE_PATH)  
  
    return  
  
  
# 读取csv中的bvid、cid  
video_list = pd.read_csv('/Volumes/SSD/Data/getVideoinfo/getVideoinfo_byhot.csv')  
cid_list = video_list['cid'].values.tolist()  
  
for cid in tqdm(cid_list):  
    DANMUKU_PATH = f'/Volumes/SSD/Data/Danmuku/{cid}.csv'  
    SAVE_PATH = f'/Volumes/SSD/Data/AnaDanmuku/{cid}.csv'  
  
    # 读取弹幕文件  
    if not os.path.exists(DANMUKU_PATH):  
        print(f"Skipping bvid {cid} due to missing file(s).")  
        continue  
  
    if os.path.exists(SAVE_PATH):  
        print(f"Skipping bvid {cid} due to finish.")  
        continue  
  
    Analyze(cid)

你可能感兴趣的:(数据分析,爬虫,自然语言处理)