Python大作业——弹幕数据分析(获取url cid 弹幕 画出词云)

 首先要知道我们要做什么,要是茫无目的那就说明都做不了,我们这次的数据分析

步骤:

① 获取视频url

② 获取视频cid

③ 获取视频弹幕

第一步:获取视频url

解析我在弹幕里直接写了,可以自己看一看,我就不在博客里写了,不好排版

Get_Url代码部分

from bs4 import BeautifulSoup
from selenium import webdriver

url = 'https://space.bilibili.com/517327498/channel/seriesdetail?sid=60119'
print("------------开始罗翔说评论地址------------")  # 2021-11-6 edg VS DK

# chrome驱动,需要放在Python安装的目录下
driver = webdriver.Chrome(r"C:\Users\12430\AppData\Local\Programs\Python\Python39\chromedriver.exe")

driver.get(url)
data = driver.page_source  # 获取到页面信息
# print(data)
soup = BeautifulSoup(data, 'lxml')  # 我们可以利用他解析HTML代码,并且在解析HTML代码的时候,如果
# HTML代码不规范或者不完整,lxml解析器会自动修复或补全代码,从而提高效率
# print(soup)  # 不信你查看一下

count = 1
res = []

all = soup.find_all('li', attrs={'class': 'small-item fakeDanmu-item'})  # 找到这个专辑里的视频,只要是
# B站的专辑都在这个class里
print("all: ", end="")
print(all)
# print(all)  # 不信你看一下

for li in all:
    if count <= 15:  # 罗翔的这个专辑视频就15个,所以爬15次
        a = li.find('a', attrs={'class': 'cover cover-normal'})
        print("这是a: ", end="")
        print(a)
        res.append('https:' + a.get("href"))  # href的格式是//www.bilibili.com/video/BV1k44y1g7Dp这样
        count += 1
    else:
        break
print(res)
with open('Urls/链接.txt', 'w') as f:  # 将结果放入这个文件里
    for link in res:
        f.write(link + '\n')
print("已将全部链接放入到链接.txt文件中")

第二步:获取视频cid

① 首先我们要定位到这个cid到第放在了什么鬼地方,发现在下面这个位置

 在script中,我们发现cid好出现在了这个window.__playinfo__中,所以我们要想办法获取他它,这边有一个办法就是startswith()这个方法

② 知道cid位置以后就想这么获取它就行了 

首先用startswith()获取链接,不知道为什么罗翔老师的视频不是//cn开头而是//upos(图一),

通过正则表达式的方法获取到cid的部分(图二)。

 然后通过一系列的处理获取前9个数字,至此获取cid成功

Get_Cid 代码部分

from bs4 import BeautifulSoup
from selenium import webdriver
import re
import time

cids = []
Urls = []

cid_start = 15
# sets_end = len(open('Urls/Link.txt', 'r').readlines()) + sets_start - 1

anime_name = "《罗翔读评论》"

with open('Urls/Link.txt', 'r') as f:
    for line in f.readlines():
        Urls.append(line.strip())

print("开始爬取动漫" + anime_name + "所有视频的Cid")

# chrome驱动,需要放在Python安装的目录下
driver = webdriver.Chrome(r"C:\Users\12430\AppData\Local\Programs\Python\Python39\chromedriver.exe")
link = ''  # 需要先定义,后面查找url的时候可能会应为找不到url而导致link未定义
cid = ''
for url in Urls:
    driver.get(url)
    data = driver.page_source
    soup = BeautifulSoup(data, 'lxml')

    all = soup.find_all('script')
    for a in all:
        if str(a).startswith("<script>window.__playinfo__"):  # 必须这样否则找不到
           res = a
    # print(res)

    links = re.split(r':', str(res))
    # print("-----------------------")
    # print(links)

    for url in links:
        # 这个链接前面是域名,中国的都是以cn开头,但是现在好像不是了
        if url.startswith("//"):  # 不知道为什么有些视频不是//cn开头的,所以用//来实现
            print(url)
            print("-----------------------")
            link = url
            break
    cid = re.findall(".*/(.*)-1-.*", link)

    # 获取到视频的cid,存进数组然后一起存进Cid.txt文件中
    cid = cid[0]

    # 处理特殊情况下,长度不符合cid,去除尾部部分
    # 需要根据Cid进行特殊处理
    if len(cid) > 9:
        length = len(cid)
        a = length - 9
        cid = cid[:-a]
        # print(cid)
    cids.append(cid)

    # 每抓完一个网页休眠5秒
    time.sleep(5)
    print(anime_name + "第" + str(cid_start) + "集Cid爬取完毕")
    cid_start -= 1
    print(cid)

with open('Urls/Cid.txt', 'w') as f:
    for id in cids:
        f.write(id + '\n')

print("已将全部视频的Cid放入到Cid.txt文件中")

第三步:获取视频的弹幕

① 首先我们需要一个headers 获取 用户代理 和 Cookie,这个自己用F12找找就行,

然后开始寻找视频的时间,首先我们发现罗翔老师读评论的第一期和最后一期好像是2020/7/14和

2022/4/29来着,那么我们就设置2020/7/13到2022/4/30来寻找视频日期

②  开始获取弹幕数据,这边就不用图片了,代码太长了

 第一步,首先要知道<d></d>这个标签里的这么多数字是什么

第一个参数 17.57900 记作DM_time,是弹幕在视频中出现的时间,以秒数为单位。

第二个参数 1 记作DM_mode,是弹幕的模式1…3 滚动弹幕 4底端弹幕 5顶端弹幕 6.逆向弹幕 7精准定位 8高级弹幕

第三个参数 25 记作DM_font,是字号, 12非常小,16特小,18小,25中,36大,45很大,64特别大

第四个参数 16777215 记作DM_color,是字体的颜色以HTML颜色的十进制为准

第五个参数 1653783652  记作DM_realTime,是发送弹幕的时间戳

第六个参数 0 记作DM_pool,是弹幕池 0普通池 1字幕池 2特殊池(高级弹幕)

第七个参数 def8eb39记作DM_userID,是发送者的ID,用于“屏蔽此弹幕的发送者”功能

第八个参数 1062823650710860544 记作DM_id,是弹幕在弹幕数据库中rowID,也就是这条弹幕是历史总弹幕的第几条

第九个参数 11 这个玩意好像没什么用?

弹幕本体 记作DM_text

 Get_BulletChat部分

import re
import jieba
from bs4 import BeautifulSoup
import time
import pandas as pd
import requests
import datetime

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
                  "Chrome/102.0.5005.63 Safari/537.36",
    "Connection": "keep-alive",
    # 这个cookie的获取方法在文档中已说明
    "Cookie": "buvid3=F2278A54-EB90-05CA-6AC4-25606AB151A009225infoc; CURRENT_FNVAL=4048; "
              "b_lsid=10C7E85F4_1812E57B9D8; _uuid=1CCF3FED-9CA9-4996-69104-410F55A4243FE10121infoc; "
              "buvid4=AD4080D0-B5E7-8064-1B21-3F1AFE743B2510754-022060418-h0Kdr2RwgtcfH98SEPYQ1Q%3D%3D; "
              "buvid_fp=16eaee4e45e32b194417c0880035c419; CURRENT_BLACKGAP=0; blackside_state=0; sid=7hhsqlnl; "
              "rpdid=|(J|)Yllu|k~0J'uYlRJJmmuY; "
              "b_timer=%7B%22ffp%22%3A%7B%22333.788.fp.risk_F2278A54%22%3A%221812E57C17E%22%2C%22333.999.fp"
              ".risk_F2278A54%22%3A%221812E5BBF35%22%7D%7D "
}
sets = 15  # 最新一期的数字
anime_name = "罗翔读评论"
dates = []  # 日期数组,用于填充url
# 遍历日期  包括begin和end的日期  生成类似2020-04-29的格式的日期
begin = datetime.date(2020, 7, 13)
end = datetime.date(2022, 4, 30)
# 需要一天一天找,所以比较慢
d = begin
delta = datetime.timedelta(days=1)  # 一天一天找
while d <= end:
    dates.append(str(d.strftime("%Y-%m-%d")))
    d += delta
# print(dates)
#

Cids = []  # Cid数组,用于填充url
with open('Urls/Cid.txt', 'r') as f:
    for line in f.readlines():
        Cids.append(line.strip())
print(Cids)  # 15--1

for cid in Cids:
    print("正在爬取第" + str(sets) + "期的" + anime_name + "弹幕...")

    # 每次都要重置这些数据
    dm_data = []  # 弹幕数据
    dm_text = []  # 弹幕本体
    # 弹幕的八个参数和弹幕本体
    DM_time = []  # 弹幕时间
    DM_mode = []  # 弹幕模式
    DM_font = []  # 弹幕类型
    DM_color = []  # 弹幕颜色
    DM_realTime = []  # 弹幕时间戳
    DM_pool = []  # 弹幕池
    DM_userID = []  # 弹幕发送者ID
    DM_id = []  # 弹幕是弹幕数据库中的第几条

    DM_text = []
    print("正在爬取第" + str(sets) + "期的弹幕...")
    for date in dates:
        url = 'https://api.bilibili.com/x/v1/dm/list.so?oid=' + cid
        response = requests.get(url=url, headers=headers)  # 返回文本信息
        response.encoding = 'utf-8'
        soup = BeautifulSoup(response.text, 'lxml')  # 建立soup对象
        all = soup.find_all("d")  # d是弹幕标签
        # print(all)
        for d in all:
            # 弹幕数据
            dm_data.append(str(d.get("p")).split(","))  # p是标签
            # 弹幕本体
            dm_text.append(d.get_text())
    print("第" + str(sets) + "集" + " " + str(date) + "数据爬取完毕!")

    # 分别把数据存进这几个数组
    for i in dm_data:
        DM_time.append(i[0])
        DM_mode.append(i[1])
        DM_font.append(i[2])
        DM_color.append(i[3])
        DM_realTime.append(i[4])
        DM_pool.append(i[5])
        DM_userID.append(i[6])
        DM_id.append(i[7])

    for i in dm_text:
        DM_text.append(i)

    #  利用pandas进行csv文件的写入
    dt = {"DM_time": DM_time, "DM_mode": DM_mode, "DM_font": DM_font, "DM_color": DM_color,
          "DM_realTime": DM_realTime, "DM_pool": DM_pool, "DM_userID": DM_userID, "DM_id": DM_id, "DM_text": DM_text}

    d = pd.DataFrame(dt)

    d.to_csv('./Danmu/Danmu-' + str(sets) + '.csv', encoding='utf-8')  # 存储弹幕信息
    print("已将弹幕放入到Danmu-" + str(sets) + ".csv文件中")
    sets -= 1

    # 每抓完一个网页休眠7秒
    print("缓冲中...")
    time.sleep(5)

print("已将罗翔读评论的第①期到第①⑤期的弹幕爬取完毕")

第四步,开始作图

主要功能:

import matplotlib.pyplot as plt
import matplotlib
import pandas as pd
import os
from wordcloud import WordCloud
import jieba

anime_name = "罗翔读评论"
file_dir = "./Danmu/"
# 获取文件名
files = [files for root, dirs, files in os.walk(file_dir)]
print(files)


# 去重
def duplicate(files):
    for file in files:
        print(file)
        data_df = pd.DataFrame()
        df = pd.read_csv(file_dir + file, encoding="utf-8-sig", index_col=0, engine='python')
        df = pd.concat([data_df, df])

        # 开始去重
        data = df.drop_duplicates(subset=['DM_id'], keep='first')
        data.to_csv(file_dir + file, encoding='utf-8-sig', index=True, index_label="")
    print("去重完毕")


# 每一期弹幕总数的变化折线图
def danmuSumPlot(files):
    print("弹幕总数变化图绘制中...")
    list1 = ['1', '2', '3', '4',
             '5', '6', '7', '8',
             '9', '10', '11', '12',
             '13', '14', '15']
    data_sum = []
    for file in files:
        data = pd.read_csv(file_dir + file, encoding="utf-8-sig", index_col=0)
        data_sum.append(len(data))

    matplotlib.rcParams["font.family"] = "SimHei"
    plt.plot(list1, data_sum, "m", ':')
    plt.ylabel("弹幕数量")
    plt.xlabel("《罗翔读评论》期数")
    plt.title("每一期弹幕总数的变化图")
    plt.savefig('./Analysis/弹幕分析图片/弹幕总数变化图', dpi=800)
    plt.show()
    print("绘制完毕")


# 发弹幕总数TOP10的用户柱状图
def danmuUserTopBarh(files):
    print("弹幕TOP20用户图绘制中...")
    datas = []
    for file in files:
        datas.append(pd.read_csv(file_dir + file, encoding="utf-8", index_col=0))

    # 先合并全部csv文件,再进行统计
    data = pd.concat(datas)
    data = data.groupby('DM_userID').size().reset_index(name="count")
    data = data.sort_values("count", ascending=False)

    label = []  # y轴的值
    width = []  # 给出具体每个直方图的数值
    i = 0

    for item in data.values:
        if i < 20:
            label.append(item[0])
            width.append(item[1])
            i += 1
        else:
            break

    matplotlib.rcParams["font.family"] = "SimHei"
    y = [20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]  # 给出在y轴上的位置
    plt.barh(y=y, width=width, tick_label=label)  # 绘制水平直方图
    plt.ylabel("用户ID")
    plt.xlabel("弹幕数")
    plt.title("发弹幕总数TOP20的用户柱状图")
    plt.subplots_adjust(left=0.22)  # 控制图片左边的间隔  避免显示不全
    plt.savefig('./Analysis/弹幕分析图片/TOP20柱状图', dpi=600)
    print("绘制完毕")


# 每期弹幕密度变化图
def danmuDensityChange(files):
    print("弹幕密度变化图绘制中...")
    sets = 1
    for file in files:
        data = pd.read_csv(file_dir + file, encoding="utf-8-sig", index_col=0)
        data = data.sort_values("DM_time")

        # 先对弹幕发送时间进行取整
        data['DM_time'] = [int(item) for item in data.DM_time]
        data = data.groupby('DM_time').size().reset_index(name="counted")

        list2 = [item for item in data.DM_time]
        data_sum = [item for item in data.counted]
        matplotlib.rcParams["font.family"] = "SimHei"
        plt.plot(list2, data_sum, "c")
        plt.ylabel("弹幕数量")
        plt.xlabel("视频时间轴/(秒)")
        plt.title(str(sets) + "期弹幕密度变化图")
        plt.savefig("./Analysis/弹幕密度变化/" + str(sets) + '期弹幕密度变化图', dpi=600)
        sets += 1
    print("绘制完毕")


# 每期的弹幕词云,词云已经用jieba库进行去词了
def danmuWordCloud(files):
    print("弹幕词云绘制中...")
    sets = 1
    for file in files:
        data = pd.read_csv(file_dir + file, encoding="utf-8-sig", index_col=0)
        # 先把全部弹幕信息写成一个字符串,再调用方法
        words = ''
        for item in data.DM_text:
            words += item

        words = " ".join(jieba.cut(words))
        # 这个scale参数是画布大小参数,也就是调整分辨率的,10代表是原来的10倍大小,越高分辨率越高
        wd = WordCloud(font_path='simhei.ttf',
                       max_words=80,
                       background_color='white',
                       min_font_size=5,
                       width=1920,
                       height=1080,
                       scale=10).generate(words)
        plt.imshow(wd)
        plt.axis("off")
        wd.to_file("./Analysis/词云/第" + str(sets) + "期词云.jpg")
        sets += 1
    print("绘制完毕")


# 每期的弹幕词云(jieba去词以后-带图片背景)
def danmuWordCloud_Img(files):
    print("弹幕词云绘制中...")
    # df = pd.DataFrame()
    # jieba.load_userdict("./Tools/" + anime_name + "词汇.txt")
    sets = 1
    sets_s = 1
    for file in files:
        data = pd.read_csv(file_dir + file, encoding="utf-8", index_col=0)
        # 先把全部弹幕信息写成一个字符串,再调用方法
        words = ''

        for item in data.DM_text:
            words += str(item)
        # print(words)

        with open('./自定义文件/stopwords.txt', 'r+', encoding='utf-8') as fp:
            stopwords = fp.read().split('\n')  # 将停用词词典的每一行停用词作为列表中的一个元素

        word_list = []  # 用于存储过滤停用词后的分词结果'
        seg_list = jieba.cut(words)
        for seg in seg_list:
            if seg not in stopwords:
                word_list.append(seg)

        words = " ".join(word_list)

        mask = plt.imread(
            './Analysis/maskImages/' + anime_name + '.jpg')  # 读取图片作为词云图轮廓
        # if sets == 15:
        #     sets = 1
        # 这个scale参数是画布大小参数,也就是调整分辨率的,10代表是原来的10倍大小,越高分辨率越高
        wd = WordCloud(font_path='simhei.ttf',
                       max_words=800,
                       background_color='white',
                       min_font_size=1,
                       # width=1920,
                       # height=1080,
                       mask=mask,
                       scale=5
                       ).generate(words)
        plt.imshow(wd)
        plt.axis("off")
        wd.to_file("./Analysis/带形状词云/" + anime_name + "第" + str(sets_s) + "集词云_Image.jpg")
        # sets += 1
        sets_s += 1
    print("绘制完毕")


# 弹幕颜色直方图
def DM_color(files):
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False

    for file in files:
        data_df = pd.DataFrame()
        data = pd.read_csv(file_dir + file)  # 读取数据文件
        data = pd.concat([data_df, data])  # 拼接数据

    data_color = data['DM_color'].value_counts()  # 统计用户发送的弹幕使用同一种颜色的数量
    data_color = data_color.head(7)  # 选出前七种颜色

    favorite_color = []  # 弹幕颜色的十六进制颜色码

    # 将爬取到的十进制颜色码转换为十六进制颜色码
    for a_color in data_color.index:
        temp = hex(a_color)
        temp = '#' + temp[2:].upper()

        while len(temp) < 7:
            temp = temp[0] + '0' + temp[1:]

        favorite_color.append(temp)

    fig, ax = plt.subplots()
    # 画柱状图,颜色为使用较多的弹幕颜色
    plt.bar([1, 2, 3, 4, 5, 6, 7], data_color.values, color=favorite_color)
    plt.title(anime_name + '弹幕颜色使用数量前七名')
    plt.xlabel('排名')
    plt.ylabel('使用该颜色的弹幕数量')
    # plt.show()

    fig.savefig(r'./Analysis/弹幕分析图片/' + anime_name + 'Color.png', transparent=True)  # 保存


if __name__ == '__main__':
    # # 去重
    duplicate(files[0])
    # 每一期弹幕总数的变化折线图
    danmuSumPlot(files[0])
    # 发弹幕总数TOP20的用户柱状图
    danmuUserTopBarh(files[0])
    # 弹幕颜色直方图
    DM_color(files[0])
    # 每期弹幕密度变化图
    danmuDensityChange(files[0])
    # 每期的弹幕词云
    danmuWordCloud(files[0])
    # 每期的弹幕词云(jieba去词以后-带图片背景)
    danmuWordCloud_Img(files[0])

这边就贴一张结果图片当实例吧

最后鸣谢这篇文章对我的帮助:(1条消息) python大作业——B站弹幕数据爬取与分析_lkx_icy的博客-CSDN博客_python爬取b站弹幕并进行数据分析

  • 3
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
Python爬虫获取B站弹幕数据分析可以分为以下几个步骤: 1. 获取B站视频的cid,可以通过视频页面的URL获取,例如视频页面的URL为:https://www.bilibili.com/video/BV1QK4y1d7dK,则cid为BV1QK4y1d7dK。 2. 使用B站提供的弹幕API获取弹幕数据,可以使用requests库发送请求,然后解析返回的XML数据,获取弹幕文本内容和发送时间等信息。 3. 将弹幕数据保存到本地文件或数据库中,可以使用csv、json、sqlite等格式进行存储。 4. 数据分析和可视化,可以使用pandas、matplotlib、seaborn等库进行数据分析和可视化,例如统计弹幕数量和分布、分析弹幕关键词等。 下面是一份获取B站视频弹幕并进行数据分析的示例代码: ```python import requests from bs4 import BeautifulSoup import xml.etree.ElementTree as ET import pandas as pd import matplotlib.pyplot as plt import seaborn as sns # 获取视频cid url = 'https://www.bilibili.com/video/BV1QK4y1d7dK' response = requests.get(url) soup = BeautifulSoup(response.text, 'html.parser') cid = soup.find('meta', {'itemprop': 'videoId'})['content'] # 获取弹幕数据 danmu_url = f'https://api.bilibili.com/x/v1/dm/list.so?oid={cid}' response = requests.get(danmu_url) xml_data = response.content.decode('utf-8-sig') xml_root = ET.fromstring(xml_data) danmu_list = [] for danmu in xml_root.iter('d'): danmu_attr = danmu.attrib['p'].split(',') danmu_list.append({ 'time': float(danmu_attr[0]), 'mode': int(danmu_attr[1]), 'color': int(danmu_attr[3]), 'text': danmu.text.strip() }) # 保存弹幕数据到csv文件中 df = pd.DataFrame(danmu_list) df.to_csv('danmu.csv', index=False, encoding='utf-8-sig') # 数据分析和可视化 df['datetime'] = pd.to_datetime(df['time'], unit='s') df['date'] = df['datetime'].dt.date df['hour'] = df['datetime'].dt.hour df['minute'] = df['datetime'].dt.minute df['second'] = df['datetime'].dt.second df['count'] = 1 # 统计弹幕数量和分布 danmu_count = df.groupby('date')['count'].sum() danmu_count.plot(kind='line', title='B站弹幕数量趋势', figsize=(8, 4)) plt.show() danmu_hour_count = df.groupby('hour')['count'].sum() danmu_hour_count.plot(kind='bar', title='B站弹幕小时分布', figsize=(8, 4)) plt.show() # 分析弹幕关键词 from jieba.analyse import extract_tags keywords = extract_tags(df['text'].str.cat(sep=' '), topK=20, withWeight=True) df_keywords = pd.DataFrame(keywords, columns=['keyword', 'weight']) sns.barplot(x='weight', y='keyword', data=df_keywords) plt.title('B站弹幕关键词分析') plt.show() ``` 以上代码可以获取B站视频的弹幕数据,并对弹幕数据进行数量和分布统计、关键词分析等操作,并使用pandas、matplotlib和seaborn等库进行数据分析和可视化。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Abandon-Lv

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值