python大作业代码及文档,python大作业有哪些题目

大家好,小编为大家解答python大作业设计一个纸牌类游戏的问题。很多人还不知道python大作业代码及文档,现在让我们一起来看看吧!

【超详细指北】python大作业!

​ 这是笔者最近写python大作业时写的一个实现过程笔记,也就是基本上可以说是本人从0开始上手的一个python练习。程序和本文档从 4.29-5.15日 总共历时17天 python自学有用吗。包含了大部分代码内容。

一、获取数据

(1)user-agent和cookie

user-agent

在这里插入图片描述

Cookie:

在这里插入图片描述

buvid3=11707BB8-8181-70C7-EBE1-FB1609F40FC370555infoc; i-wanna-go-back=-1; _uuid=F4221228-EF95-B7F10-49C1-F710CAC68D109F77140infoc; buvid4=E437889C-0A9F-DEF4-C164-E3F9F456407172347-022032622-MnLxL6Vqo8K/D8N1XzXHLQ%3D%3D; nostalgia_conf=-1; buvid_fp_plain=undefined; blackside_state=1; rpdid=|(J~J|R~m)Jm0J'uYR)Jm~JYR; CURRENT_BLACKGAP=0; hit-dyn-v2=1; LIVE_BUVID=AUTO4316488832212386; bp_article_offset_154100711=649151673891029000; SESSDATA=fbf8b924%2C1666235070%2C5c5a7%2A41; bili_jct=2f4e142aa58387a4ba58d6610a138881; DedeUserID=154100711; DedeUserID__ckMd5=4a5f601a3689140a; sid=7m78ki9o; CURRENT_QUALITY=0; fingerprint=a0d6414c1242c8cb9c9f7b4f70d4d671; b_ut=5; CURRENT_FNVAL=4048; bsource=search_baidu; b_lsid=AB9536C2_1807AC90CF7; _dfcaptcha=0f5ba157af594817171639f2996e0b43; PVID=1; innersign=1; buvid_fp=a0d6414c1242c8cb9c9f7b4f70d4d671; bp_video_offset_154100711=654934739730300900; fingerprint3=5ad9983134e17174abef4db7b440a5ab
(2)commentData类

​ 该类是获取某一视频的所有评论信息,包括一级评论、二级评论,获取了评论用户的基本信息和评论内容。在该类中,设置headersCookie防止反爬,此外还有一个fake_useragent库也可以防止反爬虫,在这里没有使用该库。

写在前面

​ 首先我们来分析一级评论:

一级评论:

在这里插入图片描述

在这里插入图片描述

​ 根据浏览器f12自带的调试中,我们查找存放评论内容的api。这里给出三个不同视频的评论接口:

三个网页的一级评论api及来源
https://api.bilibili.com/x/v2/reply/main?callback=jQuery17208590914915452643_1651207947683&jsonp=jsonp&next=0&type=1&oid=34491719&mode=3&plat=1&_=1651207949390
【https://www.bilibili.com/video/BV1ot411R7SM?spm_id_from=333.999.0.0】

https://api.bilibili.com/x/v2/reply/main?callback=jQuery33102399794496926384_1651209840924&jsonp=jsonp&next=0&type=1&oid=768445836&mode=3&plat=1&_=1651209840925
【https://www.bilibili.com/video/BV11r4y1J7cH?spm_id_from=333.999.0.0】

https://api.bilibili.com/x/v2/reply/main?callback=jQuery17203622673329462698_1651210156500&jsonp=jsonp&next=0&type=1&oid=721394418&mode=3&plat=1&_=1651210156936
【https://www.bilibili.com/video/BV1fQ4y1q7SB/?spm_id_from=333.788.recommend_more_video.16】
  1. https://api.bilibili.com/x/v2/reply/main?callback=jQuery17208590914915452643_1651207947683&jsonp=jsonp&next=0&type=1&oid=34491719&mode=3&plat=1&_=1651207949390

  2. https://api.bilibili.com/x/v2/reply/main?callback=jQuery33102399794496926384_1651209840924&jsonp=jsonp&next=0&type=1&oid=768445836&mode=3&plat=1&_=1651209840925

  3. https://api.bilibili.com/x/v2/reply/main?callback=jQuery17203622673329462698_1651210156500&jsonp=jsonp&next=0&type=1&oid=721394418&mode=3&plat=1&_=1651210156936

    可见在加粗部分是不同的

第一个api中:

https://api.bilibili.com/x/v2/reply/main?callback=jQuery17208590914915452643_1651207947683&jsonp=jsonp&next=0&type=1&oid=34491719&mode=3&plat=1&_=1651207949390

在这里插入图片描述

删除第一个和最后一个参数(因为我们不需要js请求,最后一个参数也没有什么影响),得到

一级评论:https://api.bilibili.com/x/v2/reply/main?jsonp=jsonp&next=0&type=1&oid=34491719&mode=3&plat=1

在这里插入图片描述

  • ​ next:翻页
  • ​ oid:视频编号(aid)
  • ​ mode:1、2表示按热度、时间排序; 0、3表示按热度排序,并显示评论和用户信息
二级评论:

​ 二级评论也就是视频评论的评论,也就是有人回复评论时的评论。

在这里插入图片描述

https://api.bilibili.com/x/v2/reply/reply?callback=jQuery17202729032535004876_1651213886637&jsonp=jsonp&pn=1&type=1&oid=34491719&ps=10&root=1426909940&_=1651213945276

同上删除首尾参数后得到:

二级评论:https://api.bilibili.com/x/v2/reply/reply?jsonp=jsonp&pn=1&type=1&oid=34491719&ps=10&root=1426909940

在这里插入图片描述

  • ​ pn:翻页
  • ​ oid:视频oid
  • ​ ps: 单页显示数量(最大为20)
  • ​ root:楼主的回复的rpid

视频的oid可通过视频BV号获取rpid可以通过一级评论获取(随后我们进行获取)

最后一页评论:

​ 我们自己根据一级评论的api,手动查找到最后一页评论,发现当没有评论时,data下的replies为null,机当前api中next的参数值为最后一页的页码,如果有评论时replies不为空。

因此我们在爬取所有评论时可以将replies是否为null作为循环退出条件。

https://api.bilibili.com/x/v2/reply/main?jsonp=jsonp&type=1&oid=34491719&mode=0&plat=1&next=28

在这里插入图片描述

在这里插入图片描述

1.构造函数init

初始化基本内容:

  • mid:up主的uid,传入参数
  • name:up主的姓名,传输参数
  • BV:爬取视频的BV号,传入参数
  • mode:排序方式(这里笔者所写的类中其实一直默认的0,也就是默认排序,其他自测):1、2表示按热度、时间排序; 0、3表示按热度排序,并显示评论和用户信息,传入参数
  • header:请求时的header,默认值,可自行更改
  • cookies:设置header和cookie防止网站反爬,传入参数
  • page:评论页数,通过爬取时根据api返回的replies是否为空进行判断是否爬取完毕,对self.page进行累加,来达到计算总共评论的总数量的目的。
  • BVName:视频名称,!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  • homeUrl:api的网址开头部分
  • oid:视频的id,通过**oid_get(self, BV)**函数返回oid值。
  • replyUrl:一级评论的api
  • rreplyUrl:二级评论的api
  • q:创建的队列,将content_get方法返回爬取内容并存入队列,通过csv_writeIn方法从q队列中进行取出存取,方便多线程工作,是一个生产着消费者模式。
  • count:当前评论楼数,指定主楼数,区别是评论还是评论的评论

因为在获取BVName和oid时,需要homeUrl,所以我们讲homeUrl放置在BVName和oid之前

    def __init__(self, mid, name, BV, mode, cookies):

        self.mid = mid          #up主的uid
        self.name = name        #up主的账号名称
        self.BV = BV            # BV:视频id号
        self.mode = mode        # mode:1、2表示按热度、时间排序; 0、3表示按热度排序,并显示评论和用户信息
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36 Edg/100.0.1185.50'
        }
        self.cookies = cookies  # 设置headers和Cookie防止反爬,还有一个fake_useragent库也可以用
        self.page = 0           # page:评论页数,出最后一页每页20条

        self.homeUrl = "https://www.bilibili.com/video/"
        self.BVName = self.BVName_get(self.BV)
        self.oid = self.oid_get(self.BV)

        #一级评论和二级评论
        self.replyUrl="https://api.bilibili.com/x/v2/reply/main?jsonp=jsonp&type=1&oid={oid}&mode={mode}&plat=1&next=".format(oid=self.oid,mode=mode)#next=0
        self.rreplyUrl = "https://api.bilibili.com/x/v2/reply/reply?jsonp=jsonp&type=1&oid={oid}&ps=20&root={root}&pn=".format(oid=self.oid, root="{root}")#pn=1

        self.q = queue.Queue()  # 用来存放爬取的数据,通过队列可以按顺序,使用多线程存入数据库或csv文件中
        # 这里我们用到了队列,好处在于,可以用多线程边爬边存,按顺序先进先出
        self.count = 1  # count变量指定主楼数,区别是评论还是评论的评论
2.获取视频oid和获取视频名称方法

​ 方法一:通过正则从response中选择以字符串aid开头的值并将其进行返回。

​ 方法二:通过BeautifulSoup4类获取视频名称,获取含有视频名称的标签,从而通过自带大string方法获取名称

(78条消息) Python中BeautifulSoup库的用法_阎_松的博客-CSDN博客_beautifulsoup库的作用

	# 获取视频 oid
    def oid_get(self, BV):
        # 请求视频页面
        response = requests.get(url=self.homeUrl + BV).text
        # 用正则表达式 通过视频 bv 号获取 oid
        oid = re.findall("\"aid\":([0-9]*),", response)[0]#寻找以字符串aid开头的值
        print("oid:" + oid)
        return oid

    def BVName_get(self,BV):
        # 请求视频页面
        response = requests.get(url=self.homeUrl + BV).text
        soup = BeautifulSoup(response, "html.parser", from_encoding="utf-8")
        nameResultSet = soup.find_all(attrs={'class': 'tit'})  # [<span class="tit">城市与山里的差距,真正体验过,我来告诉你!</span>]
        result = nameResultSet[0].string                       #城市与山里的差距,真正体验过,我来告诉你!
        print("BVName:" + result)
        return result
3.评论内容获取

​ 首先我们请求函数传递url,page(最大页面数,最终代码会删掉这个page参数,因为通过判断replies是否为空来获取所有页码的评论,就不需要指定获取页码的内容了),通过requests库请求数据,需要的数据都在data->replies里面,将该内容用一个列表保存。

在这里插入图片描述

在这里插入图片描述

​ 评论内容详细分析:其中是该视频的主要评论(也就是一级评论),其下有部分回复该评论的子评论,详细内容包含了评论的id、视频的id、时间戳、评论内容等等,其中主要信息为

  1. rpid:评论id
  2. oid:该视频的oid
  3. mid:账户的uid
  4. rcount :回复数
  5. ctime:时间戳
  6. like:点赞数
  7. member–>sign:用户标签,即用户的个性签名
  8. content–>message:评论内容
  9. replies:评论列表
  10. replies–>rpid:子评论的id
  11. replies–>level :用户等级

在这里插入图片描述

​ 获取一级评论的数据:

    #获取当前页面的评论
    def content_get(self, url, page):
        now = 0    # 当前页面
        while now<=page:
            print("page : <{now}>/<{page}>".format(now=now, page=page))
            response = requests.get(url=url+str(now), cookies=self.cookies, headers=self.headers, timeout=10).json()        # 把response解析为json格式,通过字典获取
            replies = response['data']['replies']     # 评论数据在data->replies 里面,每页有 20 条
            now += 1
            for reply in replies:                    # 遍历获取每一条,用reply_clean函数提取数据
                line = self.reply_clean(reply)
                self.count += 1

​ 因为一二级评论格式基本一致,所以将上面获取一级评论的数据的代码修改一下,增加复用性。

​ 这里新增了level_1来判断是否是一级评论,如果是则进行请求下一级,否则不请求。

​ 此外,这里将page参数进行了删除,通过之前分析的,通过判断replies是否为空来判断是否到达评论的最后一页。

    #数据获取:获取当前页面的评论
    def content_get(self, url, level_1=True):
        # level_1判断是否为一级评论。如果为二级评论,则不请求下一级评论(评论的评论)
        now = 1
        while True:
            if level_1:
                print("page : <{now}>".format(now=now))

            response = requests.get(url=url + str(now), cookies=self.cookies, headers=self.headers).json()
            print(url + str(now))
            replies = response['data']['replies']  # 评论数据在data->replies 里面,一共有 20 条

            if (replies == None)and(now == 1):
                #因为当next==0时和next==1时的评论内容是一样的,所以单独写出来一种情况:该视频没有任何评论
                self.page=0
                print("该页没有评论......")
                return
            elif replies == None:
                self.page = now - 1
                print("评论信息获取完成......")
                return
            elif replies != None:
                now += 1
                for reply in replies:
                    # 一级评论则去请求下一级评论
                    if level_1:
                        line = self.reply_clean(reply, self.count)
                        self.count += 1
                    else:
                        line = self.reply_clean(reply)
                    self.q.put(line)
                    # 这儿我们可以筛选一下,如果有二级评论,调用函数请求二级评论
                    if level_1 == True and line[-2] != 0:#如果是一级评论且 回复数 不为零 则去请求二级评论
                        self.content_get(url=self.rreplyUrl.format(root=str(line[-1])), level_1=False)  # 递归获取二级评论
4.数据清洗

​ 因为replies下的数据过多而且繁杂,而我们不需要这么多的数据,所以我们进行一下数据的“清洗”,只返回我们需要的数据信息。

​ 将评论时间的时间戳通过time库转换成正常格式。通过之前分析的含义,将需要的信息保存并且返回为列表类型。为了使程序更具用复用性,这里兼容清洗二级评论数据,增加count参数,默认为false,表示是否是二级评论。

​ 如果是二级评论,则返回数据第一个为"回复",否则为楼号。

​ 二级评论没有回复数rcount,三级评论都显示为 回复xxx @谁谁谁

    # 数据清洗,将我们需要的数据进行筛选返回
    def reply_clean(self, reply, count=False):
        # 这个函数可以爬一级评论也能爬二级评论
        # count 参数,看看是不是二级评论。
        name = reply['member']['uname']  # 名字
        sex = reply['member']['sex']    # 性别:男/女/保密
        mid = reply['member']['mid']    # 帐号的uid
        sign = reply['member']['sign']  # 个性签名
        rpid = reply['rpid']            # 评论的id,爬二级评论要用到
        rcount = reply['rcount']        # 回复数
        level = reply['member']['level_info']['current_level']  # 用户等级
        like = reply['like']            # 点赞数
        content = reply['content']['message'].replace("\n", "")  # 评论内容
        t = reply['ctime']              #时间戳
        timeArray = time.localtime(t)
        otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)  # 评论时间,时间戳转为标准时间格式,2022-05-05 19:15:14
        # 如果是二级评论,则返回数据第一个为"回复",否则为楼号
        # 二级评论没有回复数rcount,三级评论都显示为 回复xxx @谁谁谁
        if count:
            return [count, name, sex, level, mid, sign, otherStyleTime, content, like, rcount, rpid]
        else:
            return ["回复", name, sex, level, mid, sign, otherStyleTime, content, like, ' ', rpid]
5.存储评论内容

​ 将信息存放在dirname的文件夹下,在该文件夹下细分为up主自己的文件夹和视频的文件夹。每次写入一行数据,即将line的列表信息进行写入。不断从队列q中取出内容并保存。最后恢复到开始的工作目录。

完整代码:

    #csv文件保存数据
    def csv_writeIn(self, mid, name, BV, BVName ):
        dirname = '视频评论信息'
        begin = os.getcwd()  # 保存开始文件工作路径
        # 如果没有该文件夹则创建一个
        if not os.path.isdir(dirname):
            os.mkdir(dirname)
        os.chdir(dirname)  # 改变当前工作目录到指定的路径

        fileName = str(mid) + "-" + str(name)  # up主的文件夹:uid-name
        if not os.path.isdir(fileName):
            os.mkdir(fileName)
        os.chdir(fileName)

        fileName = str(BV) + "-" + str(BVName)  # BV视频的文件夹:BV-BVname
        if not os.path.isdir(fileName):
            os.mkdir(fileName)
        os.chdir(fileName)

        file = open("bilibili评论_" + BV + ".csv", "w", encoding="utf-8", newline="")
        f = csv.writer(file)
        line1 = ['楼层', '姓名', '性别', '等级', 'uid', '个性签名', '评论时间', '评论内容', '点赞数', '回复数', 'rpid']
        f.writerow(line1)
        file.flush()
        while True:
            try:
                line = self.q.get(timeout=10)
            except:
                break
            f.writerow(line)
            file.flush()
        file.close()

        os.chdir(begin)  # 恢复文件工作路径
6.commentData类代码
import os
import time
import requests
import re
import queue
import csv
from threading import Thread
from bs4 import BeautifulSoup

#该类实现爬取保存一个视频的评论信息。
class commentData:
    #构造函数__init__,设置基础信息
    def __init__(self, mid, name, BV, mode, cookies):

        self.mid = mid          #up主的uid
        self.name = name        #up主的账号名称
        self.BV = BV            # BV:视频id号
        self.mode = mode        # mode:1、2表示按热度、时间排序; 0、3表示按热度排序,并显示评论和用户信息
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36 Edg/100.0.1185.50'
        }
        self.cookies = cookies  # 设置headers和Cookie防止反爬,还有一个fake_useragent库也可以用
        self.page = 0           # page:评论页数,出最后一页每页20条

        self.homeUrl = "https://www.bilibili.com/video/"
        self.BVName = self.BVName_get(self.BV)
        self.oid = self.oid_get(self.BV)

        #一级评论和二级评论
        self.replyUrl="https://api.bilibili.com/x/v2/reply/main?jsonp=jsonp&type=1&oid={oid}&mode={mode}&plat=1&next=".format(oid=self.oid,mode=mode)#next=0
        self.rreplyUrl = "https://api.bilibili.com/x/v2/reply/reply?jsonp=jsonp&type=1&oid={oid}&ps=20&root={root}&pn=".format(oid=self.oid, root="{root}")#pn=1

        self.q = queue.Queue()  # 用来存放爬取的数据,通过队列可以按顺序,使用多线程存入数据库或csv文件中
        # 这里我们用到了队列,好处在于,可以用多线程边爬边存,按顺序先进先出
        self.count = 1  # count变量指定主楼数,区别是评论还是评论的评论

    # 获取视频 oid
    def oid_get(self, BV):
        # 请求视频页面
        response = requests.get(url=self.homeUrl + BV).text
        # 用正则表达式 通过视频 bv 号获取 oid
        oid = re.findall("\"aid\":([0-9]*),", response)[0]#寻找以字符串aid开头的值
        print("oid:" + oid)
        return oid

    def BVName_get(self,BV):
        # 请求视频页面
        response = requests.get(url=self.homeUrl + BV).text
        soup = BeautifulSoup(response, "html.parser", from_encoding="utf-8")
        nameResultSet = soup.find_all(attrs={'class': 'tit'})  # [<span class="tit">城市与山里的差距,真正体验过,我来告诉你!</span>]
        BVName = nameResultSet[0].string                       #城市与山里的差距,真正体验过,我来告诉你!
        print("BVName:" + BVName)
        return BVName

    #数据获取:获取当前页面的评论
    def content_get(self, url, level_1=True):
        # level_1判断是否为一级评论。如果为二级评论,则不请求下一级评论(评论的评论)
        now = 1
        while True:
            if level_1:
                print("page : <{now}>".format(now=now))

            response = requests.get(url=url + str(now), cookies=self.cookies, headers=self.headers).json()
            print(url + str(now))
            replies = response['data']['replies']  # 评论数据在data->replies 里面,一共有 20 条

            if (replies == None)and(now == 1):
                #因为当next==0时和next==1时的评论内容是一样的,所以单独写出来一种情况:该视频没有任何评论
                self.page=0
                print("该页没有评论......")
                return
            elif replies == None:
                self.page = now - 1
                print("评论信息获取完成......")
                return
            elif replies != None:
                now += 1
                for reply in replies:
                    # 一级评论则去请求下一级评论
                    if level_1:
                        line = self.reply_clean(reply, self.count)
                        self.count += 1
                    else:
                        line = self.reply_clean(reply)
                    self.q.put(line)
                    # 这儿我们可以筛选一下,如果有二级评论,调用函数请求二级评论
                    if level_1 == True and line[-2] != 0:#如果是一级评论且 回复数 不为零 则去请求二级评论
                        self.content_get(url=self.rreplyUrl.format(root=str(line[-1])), level_1=False)  # 递归获取二级评论


    # 数据清洗,将我们需要的数据进行筛选返回
    def reply_clean(self, reply, count=False):
        # 这个函数可以爬一级评论也能爬二级评论
        # count 参数,看看是不是二级评论。
        name = reply['member']['uname']  # 名字
        sex = reply['member']['sex']    # 性别:男/女/保密
        mid = reply['member']['mid']    # 帐号的uid
        sign = reply['member']['sign']  # 个性签名
        rpid = reply['rpid']            # 评论的id,爬二级评论要用到
        rcount = reply['rcount']        # 回复数
        level = reply['member']['level_info']['current_level']  # 用户等级
        like = reply['like']            # 点赞数
        content = reply['content']['message'].replace("\n", "")  # 评论内容
        t = reply['ctime']              #时间戳
        timeArray = time.localtime(t)
        otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)  # 评论时间,时间戳转为标准时间格式,2022-05-05 19:15:14
        # 如果是二级评论,则返回数据第一个为"回复",否则为楼号
        # 二级评论没有回复数rcount,三级评论都显示为 回复xxx @谁谁谁
        if count:
            return [count, name, sex, level, mid, sign, otherStyleTime, content, like, rcount, rpid]
        else:
            return ["回复", name, sex, level, mid, sign, otherStyleTime, content, like, ' ', rpid]

    #csv文件保存数据
    def csv_writeIn(self, mid, name, BV, BVName ):
        dirname = '视频评论信息'
        begin = os.getcwd()  # 保存开始文件工作路径
        # 如果没有该文件夹则创建一个
        if not os.path.isdir(dirname):
            os.mkdir(dirname)
        os.chdir(dirname)  # 改变当前工作目录到指定的路径

        fileName = str(mid) + "-" + str(name)  # up主的文件夹:uid-name
        if not os.path.isdir(fileName):
            os.mkdir(fileName)
        os.chdir(fileName)

        fileName = str(BV) + "-" + str(BVName)  # BV视频的文件夹:BV-BVname
        if not os.path.isdir(fileName):
            os.mkdir(fileName)
        os.chdir(fileName)

        file = open("bilibili评论_" + BV + ".csv", "w", encoding="utf-8", newline="")
        f = csv.writer(file)
        line1 = ['楼层', '姓名', '性别', '等级', 'uid', '个性签名', '评论时间', '评论内容', '点赞数', '回复数', 'rpid']
        f.writerow(line1)
        file.flush()
        while True:
            try:
                line = self.q.get(timeout=10)
            except:
                break
            f.writerow(line)
            file.flush()
        file.close()

        os.chdir(begin)  # 恢复文件工作路径

    def main(self):
        #创建队列,方便多线程程序进行
        T = []
        T.append(Thread(target=self.content_get, args=(self.replyUrl, )))
        T.append(Thread(target=self.csv_writeIn, args=(self.mid,self.name,self.BV,self.BVName)))

        print("开始爬取...")
        for t in T:
            t.start()
        for t in T:
            t.join()

if __name__ == '__main__':
    cookie = "buvid3=11707BB8-8181-70C7-EBE1-FB1609F40FC370555infoc; i-wanna-go-back=-1; _uuid=F4221228-EF95-B7F10-49C1-F710CAC68D109F77140infoc; buvid4=E437889C-0A9F-DEF4-C164-E3F9F456407172347-022032622-MnLxL6Vqo8K/D8N1XzXHLQ%3D%3D; nostalgia_conf=-1; buvid_fp_plain=undefined; blackside_state=1; rpdid=|(J~J|R~m)Jm0J'uYR)Jm~JYR; CURRENT_BLACKGAP=0; hit-dyn-v2=1; LIVE_BUVID=AUTO4316488832212386; bp_article_offset_154100711=649151673891029000; CURRENT_QUALITY=0; b_ut=5; fingerprint3=5ad9983134e17174abef4db7b440a5ab; CURRENT_FNVAL=4048; PVID=1; SESSDATA=da01081b%2C1667737162%2C066fd%2A51; bili_jct=e257a396d8d258b042d32a8aa9494f9e; DedeUserID=154100711; DedeUserID__ckMd5=4a5f601a3689140a; sid=ldg719nz; fingerprint=19c41c196550f8268e8c94867b19f6d8; buvid_fp=19c41c196550f8268e8c94867b19f6d8; innersign=1; bp_video_offset_154100711=659031889417338900; b_lsid=2A8FAA105_180B3D96C31"
    cookies = {}
    for c in cookie.split(";"):
        b = c.split("=")
        cookies[b[0]] = b[1]

    commentData = commentData(382193067, '巫托邦', 'BV1344y1u7K8', 0, cookies)
    commentData.main()
"""
==========================
@auther:JingDe
@Date:2022/4/29 14:53
@email:
@IDE:PyCharm
==========================
"""

运行该类,结果示意图为:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

(3)upData_.py文件

​ 写在前面,因为需要每隔一段时间爬取一次up主的信息,因此我们最好把程序一直放置后台运行。这里,笔者是把需要一直运行的程序放置在安装好Linux系统的树莓派中,让树莓派24h不间断运行,每隔一段时间进行爬取一次数据。这里是获取up主的基本数据,我们把upData类timer类写在一个文件中,运行该文件,通过24h每隔一小时爬取一次up主的信息并保存。

upData类

​ 这个类是用来获取up主粉丝数量基本相关信息。

1.up主粉丝数,关注数,uid

在这里插入图片描述

在这里插入图片描述

  • follower: 粉丝数(1253342)
  • following: up主关注数(6)
  • mid: up主uid(382193067)

我们在up主的主页中,可以看到粉丝数量等基本信息。通过f12查找相关api中预览内容,发现粉丝数,关注数,mid的接口如下:

https://api.bilibili.com/x/relation/stat?vmid=382193067&jsonp=jsonp

去掉mid(up主的uid)后即为接口:

https://api.bilibili.com/x/relation/stat?vmid=######&jsonp=jsonp
2.点赞数,播放数,阅读数

在这里插入图片描述

  • archive:view:播放数 (104483845)
  • article:view:阅读数(0)
  • likes: 点赞数:(10817191)

同上,我们预览相关接口的内容可以找到点赞数,播放数,阅读数的api接口如下:

https://api.bilibili.com/x/space/upstat?mid=382193067&jsonp=jsonp

去掉mid后即为接口:

https://api.bilibili.com/x/space/upstat?mid=#####&jsonp=jsonp

得到获得的两个api:

  • up主粉丝数,关注数,uid

    https://api.bilibili.com/x/relation/stat?vmid=######&jsonp=jsonp
    
  • 点赞数,播放数,阅读数

    https://api.bilibili.com/x/space/upstat?mid=#####&jsonp=jsonp
    

    创建好工作文件夹,即准备把up主信息存放的目的文件。

​ 在构造函数init里面进行 初始化headers,cookie。

​ 通过up主的uidname进行数据获取。定义好之前分析的链接,进行请求,将请求结果保存在response1和response2中,然后选择我们需要的信息进行返回一个数据列表。

​ 获取到数据后,我们讲其存放到本地,保存为csv格式。首先创建一个up主信息文件夹,然后在该文件夹下寻找需存放up主的文件夹,如果存在,则在其后进行追加即可,若不存在,则创建再添加。保存成功后讲其信息进行控制台输出

3.updata类为:
import os
import time
import requests
import csv

class upData:
    #构造函数__init__,设置基础信息
    def __init__(self, cookies):
        # 设置headers和Cookie防止反爬,还有一个fake_useragent库也可以用
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36 Edg/100.0.1185.50'
        }
        self.cookies = cookies

    #数据获取:
    def content_get(self, mid, name):
        # mid:up主的uid
        # name:up主的名称
        mid = str(mid)
        name = str(name)
        url1 = "https://api.bilibili.com/x/relation/stat?vmid=" + mid + "&jsonp=jsonp"  # up主粉丝数,关注数,阅读数
        url2 = "https://api.bilibili.com/x/space/upstat?mid=" + mid + "&jsonp=jsonp"  # 点赞数,播放数,阅读数
        response1 = requests.get(url=url1, cookies=self.cookies, headers=self.headers).json()#{'code': 0, 'message': '0', 'ttl': 1, 'data': {'mid': 382193067, 'following': 6, 'whisper': 0, 'black': 0, 'follower': 1253218}}
        response2 = requests.get(url=url2, cookies=self.cookies, headers=self.headers).json()
        for i in response1:
            data1 = response1['data']
        for j in response2:
            data2 = response2['data']
        uid = mid                               #up的uid
        upName = name                           #up的名称
        follower = data1['follower']            #粉丝数
        following = data1['following']          #关注数
        likes = data2['likes']                  #点赞数
        archive = data2['archive']['view']      #播放数
        article = data2['article']['view']      #阅读数
        ctime=time.time()                       #时间戳
        data=[ctime, uid, upName, follower, following, likes, archive, article]
        return data

    #csv文件保存数据
    def csv_writeIn(self, mid, name):
        dirname = 'up主信息'
        flag = False#标志是否写表头,默认不写(默认没有该up主的文件夹,即没有他的信息)
        begin=os.getcwd()#保存开始文件工作路径
        # 如果没有该文件夹则创建一个
        if not os.path.isdir(dirname):
            os.mkdir(dirname)
        os.chdir(dirname)  # 改变当前工作目录到指定的路径
        fileName = str(mid) + "-" + str(name)  # up主的文件夹:uid+name
        if not os.path.isdir(fileName):
            flag = True
            os.mkdir(fileName)
        os.chdir(fileName)
        file = open(fileName + ".csv", "a", encoding="utf-8", newline="")
        f = csv.writer(file)
        if(flag):
            line1 = ['时间', 'uid帐号', 'up主名称', '粉丝数', '关注数', '点赞数', '播放数', '阅读数']
            f.writerow(line1)
            file.flush()
        data=self.content_get(mid, name)
        f.writerow(data)
        file.flush()
        file.close()

        timeArray = time.localtime(data[0])
        print(time.strftime("%Y-%m-%d %H:%M:%S", timeArray) + ":该up基本信息如下......")
        print("uid:%s up主名称:%s 粉丝数:%d 关注数:%d 点赞数:%d 播放数:%d 阅读数:%d"%(data[1],data[2],data[3],data[4],data[5],data[6],data[7]))
        print("保存完毕......")
        os.chdir(begin)#恢复文件工作路径

    def main(self, mid, name):
        print("开始获取up主基本信息......")
        self.csv_writeIn(mid, name)

# if __name__ == '__main__':
#     cookie = "buvid3=11707BB8-8181-70C7-EBE1-FB1609F40FC370555infoc; i-wanna-go-back=-1; _uuid=F4221228-EF95-B7F10-49C1-F710CAC68D109F77140infoc; buvid4=E437889C-0A9F-DEF4-C164-E3F9F456407172347-022032622-MnLxL6Vqo8K/D8N1XzXHLQ%3D%3D; nostalgia_conf=-1; buvid_fp_plain=undefined; blackside_state=1; rpdid=|(J~J|R~m)Jm0J'uYR)Jm~JYR; CURRENT_BLACKGAP=0; hit-dyn-v2=1; LIVE_BUVID=AUTO4316488832212386; bp_article_offset_154100711=649151673891029000; SESSDATA=fbf8b924%2C1666235070%2C5c5a7%2A41; bili_jct=2f4e142aa58387a4ba58d6610a138881; DedeUserID=154100711; DedeUserID__ckMd5=4a5f601a3689140a; sid=7m78ki9o; CURRENT_QUALITY=0; fingerprint=a0d6414c1242c8cb9c9f7b4f70d4d671; b_ut=5; CURRENT_FNVAL=4048; bsource=search_baidu; b_lsid=AB9536C2_1807AC90CF7; _dfcaptcha=0f5ba157af594817171639f2996e0b43; PVID=1; innersign=1; buvid_fp=a0d6414c1242c8cb9c9f7b4f70d4d671; bp_video_offset_154100711=654934739730300900; fingerprint3=5ad9983134e17174abef4db7b440a5ab"
#     cookies = {}
#     for c in cookie.split(";"):
#         b = c.split("=")
#         cookies[b[0]] = b[1]    #b0和b1分别是cookie的关键字和值,也就是将cookie转换为字典类型
#     bilibili = upData(cookies)
#     bilibili.main(382193067,'巫托邦')
#     bilibili.main(431313625, '小蓝和他的朋友日常号')
#     bilibili.main(627888730, '星有野')
#     bilibili.main(946974, '影视飓风')
#     bilibili.main(163637592, '老师好我叫何同学')
MyTimer类

[(78条消息) Python:录记个做,写写便随_Ambitioner_c的博客-CSDN博客](https://blog.csdn.net/qq_41297934/article/details/105371870?ops_request_misc=&request_id=&biz_id=102&utm_term=Python 实现某个功能每隔一段时间被执行一次的功能方法&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-8-105371870.142v9pc_search_result_control_group,157v4control&spm=1018.2226.3001.4187)

(78条消息) Python 实现某个功能每隔一段时间被执行一次的功能_独一无二的小个性的博客-CSDN博客_python 每隔一段时间

[(78条消息) Python实现定时任务的几种方法_从流域到海域的博客-CSDN博客_python定时任务的实现方式](https://blog.csdn.net/Solo95/article/details/122026111?ops_request_misc=&request_id=&biz_id=102&utm_term=Python 实现某个功能每隔一段时间被执行一次的功能方法&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-5-122026111.142v9pc_search_result_control_group,157v4control&spm=1018.2226.3001.4187)

这是一个timer封装的定时器类,可以每隔一段时间自动执行。以下是需要注意的参数信息,和需要运行的方法。

tmr = MyTimer( start, 60*60, aa.hello, [ "owenliu", 18 ] )
#start 为当前时间,
#60*60 为代码循环周期(这里为 1h),
#aa.hello 为回调函数,
#["owenliu", 18] 为回调函数的参数
def hello(name, age):
        print("[%s]\thello %s: %d\n" % (datetime.now().strftime("%Y%m%d %H:%M:%S"), name, age))
Mytimer类内容:
# -*- coding: utf-8 -*-
# ==================================================
# 对 Timer 做以下再封装的目的是:当某个功能需要每隔一段时间被
# 执行一次的时候,不需要在回调函数里对 Timer 做重新安装启动
# ==================================================
__author__ = 'liujiaxing'
 
from threading import Timer
from datetime import datetime

class MyTimer(object):
 
    def __init__(self, start_time, interval, callback_proc, args=None, kwargs=None):
        self.__timer = None
        self.__start_time = start_time
        self.__interval = interval
        self.__callback_pro = callback_proc
        self.__args = args if args is not None else []
        self.__kwargs = kwargs if kwargs is not None else {}
 
    def exec_callback(self, args=None, kwargs=None):
        self.__callback_pro(*self.__args, **self.__kwargs)
        self.__timer = Timer(self.__interval, self.exec_callback)
        self.__timer.start()
 
    def start(self):
        interval = self.__interval - (datetime.now().timestamp() - self.__start_time.timestamp())
        print(interval)
        self.__timer = Timer(interval, self.exec_callback)
        self.__timer.start()
 
    def cancel(self):
        self.__timer.cancel()
        self.__timer = None

class AA:
    @staticmethod
    def hello(name, age):
        print("[%s]\thello %s: %d\n" % (datetime.now().strftime("%Y%m%d %H:%M:%S"), name, age))
 
if __name__ == "__main__":
    aa = AA()
    start = datetime.now().replace(minute=3, second=0, microsecond=0)
    tmr = MyTimer(start, 60 * 60, aa.hello, ["owenliu", 18])
    tmr.start()
    tmr.cancel()

修改循环时间,即MyTimer的第二个参数。

​ upData类和MyTimer类完成之后,我们讲两个类合并在一起,写到一个文件中并将其在树莓派进行“挂机”执行,值得注意的是,因为我们的cookie是写“死”的,事实上这个cookie每隔一段时间就会过期,需要更新这里给出下次参考链接来自适应更新cookie,笔者这里因为首要目标是完成python大作业,暂时没有完善这个功能,需要的朋友可自行研究。

(78条消息) python session保持cookie_python接口自动化测试八:更新Cookies、session保持会话_冷君聊大片的博客-CSDN博客

​ 回到正题,我们将两个类合并在一起并且命名为auto_.py文件,其实把这两个分开也行,只不过笔者在树莓派运行的时候,因为环境的差异,自己写的模块在import的时候会有问题,因为时间原因,最简单直接的办法就是将这两个合并在一起,事实上我也是这样做的。

auto_.py的内容:
# encoding: utf-8
import os
import time
from threading import Timer
from datetime import datetime

import requests
import csv

class upData:
    #构造函数__init__,设置基础信息
    def __init__(self, cookies):
        # 设置headers和Cookie防止反爬,还有一个fake_useragent库也可以用
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36 Edg/101.0.1210.39'
        }
        self.cookies = cookies

    #数据获取:
    def content_get(self, mid, name):
        # mid:up主的uid
        # name:up主的名称
        mid = str(mid)
        name = str(name)
        url1 = "https://api.bilibili.com/x/relation/stat?vmid=" + mid + "&jsonp=jsonp"  # up主粉丝数,关注数,阅读数
        url2 = "https://api.bilibili.com/x/space/upstat?mid=" + mid + "&jsonp=jsonp"  # 点赞数,播放数,阅读数
        response1 = requests.get(url=url1, cookies=self.cookies, headers=self.headers).json()#{'code': 0, 'message': '0', 'ttl': 1, 'data': {'mid': 382193067, 'following': 6, 'whisper': 0, 'black': 0, 'follower': 1253218}}
        response2 = requests.get(url=url2, cookies=self.cookies, headers=self.headers).json()
        for i in response1:
            data1 = response1['data']
        for j in response2:
            data2 = response2['data']
        uid = mid                               #up的uid
        upName = name                           #up的名称
        follower = data1['follower']            #粉丝数
        following = data1['following']          #关注数
        likes = data2['likes']                  #点赞数
        archive = data2['archive']['view']      #播放数
        article = data2['article']['view']      #阅读数
        ctime=time.time()                       #时间戳
        data=[ctime, uid, upName, follower, following, likes, archive, article]
        return data

    #csv文件保存数据
    def csv_writeIn(self, mid, name):
        dirname = 'up主信息'
        flag = False#标志是否写表头,默认不写(默认没有该up主的文件夹,即没有他的信息)
        begin=os.getcwd()#保存开始文件工作路径
        # 如果没有该文件夹则创建一个
        if not os.path.isdir(dirname):
            os.mkdir(dirname)
        os.chdir(dirname)  # 改变当前工作目录到指定的路径
        fileName = str(mid) + "-" + str(name)  # up主的文件夹:uid+name
        if not os.path.isdir(fileName):
            flag = True
            os.mkdir(fileName)
        os.chdir(fileName)
        file = open(fileName + ".csv", "a", encoding="utf-8", newline="")
        f = csv.writer(file)
        if(flag):
            line1 = ['时间', 'uid帐号', 'up主名称', '粉丝数', '关注数', '点赞数', '播放数', '阅读数']
            f.writerow(line1)
            file.flush()
        data=self.content_get(mid, name)
        f.writerow(data)
        file.flush()
        file.close()

        timeArray = time.localtime(data[0])
        print(time.strftime("%Y-%m-%d %H:%M:%S", timeArray) + ":该up基本信息如下......")
        print("uid:%s up主名称:%s 粉丝数:%d 关注数:%d 点赞数:%d 播放数:%d 阅读数:%d"%(data[1],data[2],data[3],data[4],data[5],data[6],data[7]))
        print("保存完毕......")
        os.chdir(begin)#恢复文件工作路径

    def main(self, mid, name):
        print("开始获取up主基本信息......")
        self.csv_writeIn(mid, name)

class MyTimer(object):

    def __init__(self, start_time, interval, callback_proc, args=None, kwargs=None):
        self.__timer = None
        self.__start_time = start_time
        self.__interval = interval
        self.__callback_pro = callback_proc
        self.__args = args if args is not None else []
        self.__kwargs = kwargs if kwargs is not None else {}

    def exec_callback(self, args=None, kwargs=None):
        self.__callback_pro(*self.__args, **self.__kwargs)
        self.__timer = Timer(self.__interval, self.exec_callback)
        self.__timer.start()

    def start(self):
        interval = self.__interval - (datetime.now().timestamp() - self.__start_time.timestamp())
        # print(interval)
        self.__timer = Timer(interval, self.exec_callback)
        self.__timer.start()

    def cancel(self):
        self.__timer.cancel()
        self.__timer = None

if __name__ == "__main__":
    cookie = "buvid3=11707BB8-8181-70C7-EBE1-FB1609F40FC370555infoc; i-wanna-go-back=-1; _uuid=F4221228-EF95-B7F10-49C1-F710CAC68D109F77140infoc; buvid4=E437889C-0A9F-DEF4-C164-E3F9F456407172347-022032622-MnLxL6Vqo8K/D8N1XzXHLQ%3D%3D; nostalgia_conf=-1; buvid_fp_plain=undefined; blackside_state=1; rpdid=|(J~J|R~m)Jm0J'uYR)Jm~JYR; CURRENT_BLACKGAP=0; hit-dyn-v2=1; LIVE_BUVID=AUTO4316488832212386; bp_article_offset_154100711=649151673891029000; CURRENT_QUALITY=0; b_ut=5; fingerprint3=5ad9983134e17174abef4db7b440a5ab; CURRENT_FNVAL=4048; bp_video_offset_154100711=658555976981413900; PVID=1; fingerprint=cade2120d2a48e2de0dadfee319a247e; buvid_fp=19c41c196550f8268e8c94867b19f6d8; SESSDATA=da01081b%2C1667737162%2C066fd%2A51; bili_jct=e257a396d8d258b042d32a8aa9494f9e; DedeUserID=154100711; DedeUserID__ckMd5=4a5f601a3689140a; sid=ldg719nz; b_lsid=F8833C51_180AE162A19"
    cookies = {}
    for c in cookie.split(";"):
        b = c.split("=")
        cookies[b[0]] = b[1]  # b0和b1分别是cookie的关键字和值,也就是将cookie转换为字典类型
    bilibili1 = upData(cookies)

    start = datetime.now().replace(minute=3, second=0, microsecond=0)
    # start 为当前时间,
    # 60*60 为代码循环周期(这里为 1h),
    # bilibili1.main 为回调函数,
    # ["owenliu", 18] 为回调函数的参数
    tmr1 = MyTimer(start, 60 * 60, bilibili1.main, [382193067, '巫托邦'])
    # tmr2 = MyTimer(start, 20 * 1, bilibili2.main, [431313625, '小蓝和他的朋友日常号'])
    # tmr3 = MyTimer(start, 20 * 1, bilibili3.main, [627888730, '星有野'])
    # tmr4 = MyTimer(start, 20 * 1, bilibili4.main, [946974, '影视飓风'])
    # tmr5 = MyTimer(start, 20 * 1, bilibili5.main, [163637592, '老师好我叫何同学'])
    tmr1.start()
    flag = input("\n输入\"exit\"停止执行......\n")
    if flag == 'exit':
        tmr1.cancel()

Linux环境中该auto_.py文件运行结果如下:

在这里插入图片描述

在这里插入图片描述

(4)videoDataDetection_.py文件
视频总播放数和历史累计弹幕数

在这里插入图片描述

查看网页元素和代码,发现播放数就在class为"video-data"的标签下,我们通过BeautifulSoup库对网页进行解析,通过find_all对class为"video-data"进行筛选,找到该标签,并返回一个集合

		#[
        # <div class="video-data">
        # <span class="view" title="总播放数1383293">138.3万播放 · </span>
        # <span class="dm" title="历史累计弹幕数3877">总弹幕数3877</span>
        # <span>2022-05-04 11:30:00</span>
        # <!-- -->
        # </div>
        # ]

该集合元素个数为1,且该返回类型为**<class ‘bs4.element.ResultSet’>,该集合元素第一个的类型为bs4.element.ResultSet[0]**

在这里插入图片描述

在debug中通过查看Total_playback_barrage_Set[0]的内容,发现所包含的标签就在contents属性中,而且该属性是一个list列表,在该列表下前两个元素分别是总播放数和总弹幕数,在第一个孩子Tag中,它的attrs属性值就包含了我们需要播放数。因此分别通过选择这两个值便可得到播放总数和历史弹幕总数。

在这里插入图片描述

获取到attrs的值后再通过字符串选择,保留数值即可。

		#视频名称
        responsePrototype = requests.get(url=self.homeUrl + BV)
        response = responsePrototype.text
        soup = BeautifulSoup(response, "html.parser", from_encoding="utf-8")
        nameResultSet = soup.find_all(attrs={'class': 'tit'})  # [<span class="tit">城市与山里的差距,真正体验过,我来告诉你!</span>]
        self.BVName = nameResultSet[0].string  # 城市与山里的差距,真正体验过,我来告诉你!
        print("BVName:" + self.BVName)
        print(responsePrototype.cookies)

        #总播放数和历史评论数
        Total_playback_barrage_Set=soup.find_all(attrs={'class':'video-data'})
        list = Total_playback_barrage_Set[0].contents
        total_playback = list[0].attrs['title'][4:]     #总播放数
        total_barrage = list[1].attrs['title'][7:]      #总弹幕数
        print("总播放数:" + total_playback)
        print("历史累计弹幕数:" +total_barrage)
点赞、投币、收藏、转发

查看网页源码,发现数据都在class为"ops"下的div中,通过find_all方法获取筛选后的结果,分析获取相关数据进行筛选。

在这里插入图片描述

在这里插入图片描述

​ 但是因为一旦当投币数到达几万(收藏数,转发数也是一样),投币数就不进行显示了,所以这需要找api。经过f12进行筛选网络请求,但是并没有找到理想中的接口。上网查询相关信息,发现接口为:

https://api.bilibili.com/x/web-interface/view?bvid=########

(78条消息) 哔哩哔哩视频播放量、点赞量、评论、收藏、投币与转发信息定时爬虫_Mark_Lee131的博客-CSDN博客

在其后追加BV号即可,浏览器请求发现,data下的stat即为视频基本信息。

在这里插入图片描述

  • ​ “aid”: 981244086(视频aid)
  • ​ “view”: 1433541(总播放数)
  • ​ “danmaku”: 3919(弹幕库数)
  • ​ “reply”: 1790(评论数)
  • ​ “favorite”: 4909(搜藏数)
  • ​ “coin”: 21329(投币数)
  • ​ “share”: 902(分享数)
  • ​ “like”: 52819(点赞数)
  • ​ “dislike”: 0

在这里插入图片描述

在这里插入图片描述

​ 到这里发现,其实一开始就可以通过这个接口进行选择想要的数据,然后进行保存。但笔者这里前面的代码已经写好,而且也是锻炼了爬取网页内容的能力。这里笔者就选择了剩余没有爬取到的数据进行使用,即点赞数、投币数、收藏数,转发数。将request返回的值进行筛选保存。

#点赞、投币、收藏、转发
        Like_coin_collect_forward_URL = 'https://api.bilibili.com/x/web-interface/view?bvid=' + BV
        response2 = requests.get(url=Like_coin_collect_forward_URL, cookies=self.cookies, headers=self.headers).json()
        dict = response2['data']['stat']
        like = dict['like']         #喜欢数
        coin = dict['coin']         #投币数
        favorite = dict['favorite'] #收藏数
        share = dict['share']       #分项数
        print('喜欢:'+ str(like))
        print('投币:'+ str(coin))
        print('收藏:'+ str(favorite))
        print('分享'+ str(share))

因为要监测视频数据,需要每隔一段时间自动执行一次,因此我们再次引入之前的MyTimer类,编写脚本。

videoDataDetection_.py代码:
# encoding: utf-8

#视频数据监测脚本:每隔一段时间查询一次视频数据:
# 总播放数、历史累计弹幕数
# 点赞、投币、收藏、转发
import os
import time
import requests
import csv
from bs4 import BeautifulSoup

from threading import Timer
from datetime import datetime

#该类实现爬取保存一个视频的评论信息。
class videoDataVariety:
    #构造函数__init__,设置基础信息
    def __init__(self, mid, name, BV, cookies):

        self.mid = mid          #up主的uid
        self.name = name        #up主的账号名称
        self.BV = BV            # BV:视频id号
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36 Edg/100.0.1185.50'
        }
        self.cookies = cookies  # 设置headers和Cookie防止反爬,还有一个fake_useragent库也可以用

        self.homeUrl = "https://www.bilibili.com/video/"
        self.BVName='(默认视频名称)'

    def videoData_content_get(self, BV):
        #获取视频基本数据,顺便设置self.BVName
        timeNow=time.time()
        timeArray = time.localtime(timeNow)  # 如果有浮点型的时间戳,则可以写在括号内
        otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)  # 评论时间,时间戳转为标准时间格式,2022-05-12 00:46:12

        #视频名称
        responsePrototype = requests.get(url=self.homeUrl + BV)
        response = responsePrototype.text
        soup = BeautifulSoup(response, "html.parser", from_encoding="utf-8")
        nameResultSet = soup.find_all(attrs={'class': 'tit'})  # [<span class="tit">城市与山里的差距,真正体验过,我来告诉你!</span>]
        self.BVName = nameResultSet[0].string  # 城市与山里的差距,真正体验过,我来告诉你!]
        #[
        # <div class="video-data">
        # <span class="view" title="总播放数1383293">138 .3万播放 · </span>
        # <span class="dm" title="历史累计弹幕数3877">总弹幕数3877</span>
        # <span>2022-05-04 11:30:00</span>
        # <!-- -->
        # </div>
        # ]

        #总播放数和历史评论数
        Total_playback_barrage_Set=soup.find_all(attrs={'class':'video-data'})
        list = Total_playback_barrage_Set[0].contents
        total_playback = list[0].attrs['title'][4:]     #总播放数
        total_barrage = list[1].attrs['title'][7:]      #总弹幕数

        #点赞、投币、收藏、转发
        Like_coin_collect_forward_URL = 'https://api.bilibili.com/x/web-interface/view?bvid=' + BV
        response2 = requests.get(url=Like_coin_collect_forward_URL, cookies=self.cookies, headers=self.headers).json()
        dict = response2['data']['stat']
        like = dict['like']         #点赞数
        coin = dict['coin']         #投币数
        favorite = dict['favorite'] #收藏数
        share = dict['share']       #转发数

        #日志信息
        log_str = '时间:' + otherStyleTime + ' 视频名称:《' + self.BVName + \
                  "》 总播放数:[" + total_playback + "] 历史累计弹幕数:[" +total_barrage + \
                  '] 点赞:['+ str(like) +'] 投币:['+ str(coin) + '] 收藏:['+ str(favorite) + '] 转发['+ str(share)+']'
        print(log_str)

        return [timeNow, int(total_playback), int(total_barrage), like, coin, favorite, share]   #[1652415252.7468505, 1488493, 3953, 53458, 21430, 4954, 918]

    def csv_writeIn(self, mid, name, BV, BVName, line):
        writeTableHead = False  #默认不写表头
        dirname = '视频数据变化分析'
        begin = os.getcwd()  # 保存开始文件工作路径
        # 如果没有该文件夹则创建一个
        if not os.path.isdir(dirname):
            os.mkdir(dirname)
        os.chdir(dirname)  # 改变当前工作目录到指定的路径

        fileName = str(mid) + "-" + str(name)  # up主的文件夹:uid-name
        if not os.path.isdir(fileName):
            os.mkdir(fileName)
        os.chdir(fileName)

        fileName = str(BV) + "-" + str(BVName)  # BV视频的文件夹:BV-BVname
        if not os.path.isdir(fileName):
            os.mkdir(fileName)
            writeTableHead = True
        os.chdir(fileName)

        # 如果没有该视频文件则创建文件并写表头
        file = open("bilibili视频监测数据_" + BV + ".csv", "a", encoding="utf-8", newline="")
        f = csv.writer(file)
        if writeTableHead:
            line1 = ['时间', '总播放数', '历史累计弹幕数', '点赞', '投币', '收藏', '转发']
            f.writerow(line1)
            file.flush()
        f.writerow(line)
        file.flush()
        file.close()

        os.chdir(begin)  # 恢复文件工作路径

    def main(self):

        line = self.videoData_content_get(self.BV)
        self.csv_writeIn(self.mid, self.name, self.BV, self.BVName, line)

class MyTimer(object):

    def __init__(self, start_time, interval, callback_proc, args=None, kwargs=None):
        self.__timer = None
        self.__start_time = start_time
        self.__interval = interval
        self.__callback_pro = callback_proc
        self.__args = args if args is not None else []
        self.__kwargs = kwargs if kwargs is not None else {}

    def exec_callback(self, args=None, kwargs=None):
        self.__callback_pro(*self.__args, **self.__kwargs)
        self.__timer = Timer(self.__interval, self.exec_callback)
        self.__timer.start()

    def start(self):
        interval = self.__interval - (datetime.now().timestamp() - self.__start_time.timestamp())
        # print(interval)
        self.__timer = Timer(interval, self.exec_callback)
        self.__timer.start()

    def cancel(self):
        self.__timer.cancel()
        self.__timer = None


if __name__ == '__main__':
    cookie = "buvid3=11707BB8-8181-70C7-EBE1-FB1609F40FC370555infoc; i-wanna-go-back=-1; _uuid=F4221228-EF95-B7F10-49C1-F710CAC68D109F77140infoc; buvid4=E437889C-0A9F-DEF4-C164-E3F9F456407172347-022032622-MnLxL6Vqo8K/D8N1XzXHLQ%3D%3D; nostalgia_conf=-1; buvid_fp_plain=undefined; blackside_state=1; rpdid=|(J~J|R~m)Jm0J'uYR)Jm~JYR; CURRENT_BLACKGAP=0; hit-dyn-v2=1; LIVE_BUVID=AUTO4316488832212386; bp_article_offset_154100711=649151673891029000; CURRENT_QUALITY=0; b_ut=5; fingerprint3=5ad9983134e17174abef4db7b440a5ab; SESSDATA=da01081b%2C1667737162%2C066fd%2A51; bili_jct=e257a396d8d258b042d32a8aa9494f9e; DedeUserID=154100711; DedeUserID__ckMd5=4a5f601a3689140a; sid=ldg719nz; fingerprint=19c41c196550f8268e8c94867b19f6d8; buvid_fp=19c41c196550f8268e8c94867b19f6d8; innersign=1; b_lsid=98AB9BF5_180B85A99EC; CURRENT_FNVAL=4048; bp_video_offset_154100711=659382028029919200; PVID=4"
    cookies = {}
    for c in cookie.split(";"):
        b = c.split("=")
        cookies[b[0]] = b[1]

    videoDataVariety = videoDataVariety(382193067, '巫托邦', 'BV1344y1u7K8', cookies)

    start = datetime.now().replace(minute=3, second=0, microsecond=0)

    # tmr1 = MyTimer(start, 10 * 1, videoDataVariety.main, [])  # 每10s查询一次数据
    tmr1 = MyTimer(start, 60 * 30, videoDataVariety.main, [])   #每半个小时查询一次数据
    tmr1.start()
    flag = input("\n输入\"exit\"停止执行......\n")
    if flag == 'exit':
        tmr1.cancel()

二、数据可视化

​ 这里除了词云图外,其他主要用了pyecharts库,这个库可以自动生成html文件,在网页中生成一个想要的图表,可交互性强,种类丰富。

快速开始 - pyecharts - A Python Echarts Plotting Library built with love.

(1)词云图

​ 评论区关键字词云图。

(79条消息) python词云图详细教程_全宇宙最最帅气的哆啦A梦小怪兽的博客-CSDN博客_python词云图

1)安装jupyter notebook

打开命令行输入:

pip install jupyter notebook
2)装必要的库
  1. wordcloud库

    打开网站:https://www.lfd.uci.edu/~gohlke/pythonlibs/

    (图片来自上述博客)

    在这里插入图片描述

    命令行进入安装包所在位置,pip安装

    pip install wordcloud-1.8.1-cp39-cp39-win_amd64.whl
    

在这里插入图片描述

  1. jieba库

    pip install jieba
    
  2. pandas库

    pip install pandas
    
3)csv获取评论列数据

​ 读取文件后根据csv操作读取列数据并进行返回。

column_content_get代码
import csv
	def column_content_get(self,mid, Name, BV, BVName, column):
        #column   第几列
        url = f'./视频评论信息/{mid}-{Name}/{BV}-{BVName}/bilibili评论_{BV}.csv'
        with open(url, mode='r', encoding='utf-8') as f:
            reader = csv.reader(f)
            column = [row[column] for row in reader]
            return column
4)生成词云图

读取数据后,设置png格式背景图片,设置停用词(过滤词),生成图片。

在这里插入图片描述

5)词语图代码

记得加上column_content_get()代码。

import wordcloud as wc
from PIL import Image
import numpy as np

	def ci_Yun_Tu(self):
        returnValue = self.column_content_get(self.mid, self.Name, self.BV, self.BVName, 7) #在文件中,评论所在列为第8列,下标为7
        text = " ".join(returnValue)
        # 设置背景形状图片
        mask = np.array(Image.open("./fivestar.png"))
        # 设置停用词
        stopwords = set()
        content = [line.strip() for line in open('./stopwords.txt', 'r',enconding='utf-8').readlines()]
        stopwords.update(content)
        # 画图
        word_cloud = wc.WordCloud(scale=10, font_path="C:\Windows\Fonts\msyh.ttc", mask=mask, stopwords=stopwords,
                                  background_color="white")  # 大小、字体、背景形状停用词、背景颜色
        word_cloud.generate(text)
        word_cloud.to_file("词云图-{}-{}.png".format(self.Name, self.BVName))  # 绘制到一个图片里
(2)饼图(环状图)

(79条消息) 【Pyecharts-学习笔记系列之Pie(三)】_浪花卷起千堆雪的博客-CSDN博客

Pie - Pie_radius - Document (pyecharts.org)

1)用户性别分类饼状图

​ 视频评论区用户性别分类饼状图。

​ 用column_content_get()方法读取到视频评论信息中下标为2的数据,也就是视频的评论区中用户的性别分类情况,其中有男、女、保密三种,将读取到的数据进行统计并放置inner_data_pair变量中,通过调用Pie()的方法并设置好环状图的半径,颜色,数据表的标题,图例位置等等。最后进行输出html文件。

​ 在练习过程中,发现有的视频名称如果末尾有 (三个英文句号)的话,在视频评论信息视频数据变化分析文件保存过程中(即commentData类和videoDataDetection_.py文件),Windows会将这三个标点符号进行忽略掉,比如何同学的这个视频。因此,我们修改了之前的代码,直接在给BVName赋值的时候,把“《》”加上即可。这里笔者没有再返回去修改本文之前那两个文件的代码内容,如有需要请自行修改。

在这里插入图片描述

在这里插入图片描述

环状图结果示意图:

在这里插入图片描述

2)饼图代码

记得加上column_content_get()代码

from pyecharts import options as opts
from pyecharts.charts import Pie

	#视频评论区用户性别分类饼状图
    def bing_tu(self):
        series_nameValue = self.BVName + '评论区用户'                     #鼠标悬浮在图上的提示文字
        sexColumn = self.column_content_get(self.mid, self.Name, self.BV, self.BVName, 2)
        maleNum = 0
        femaleNum = 0
        unknownNum = 0
        for i in sexColumn:
            if (i == '男'): maleNum += 1
            elif (i == '女'): femaleNum += 1
            elif (i == '保密'): unknownNum += 1
        inner_x_data = ["男", "女", "保密"]        #分类
        inner_y_data = [maleNum, femaleNum, unknownNum]                    #分类对应的值
        inner_data_pair = [list(z) for z in zip(inner_x_data, inner_y_data)]    #值的“合集”
        outUrl = './输出库/' + str(self.mid) + '-' + self.Name + '-' + self.BV + '-' + self.BVName     #输出路径
        c = (
            Pie()
                .add(
                    series_nameValue,
                    inner_data_pair,
                    radius=["50%", "75%"],  # 调整半径
                )
                .set_colors(["#65a8d8", "#f8a3cf", "#9da5ad"])      #颜色
                .set_global_opts(
                    title_opts=opts.TitleOpts(title="性别构成情况"),  #标题
                    legend_opts=opts.LegendOpts(orient="vertical", pos_top="10%", pos_left="88%"),# 图例设置
                )
                .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))   # 设置标签
                .render(outUrl + ".html")       #保存输出
        )
(3)柱状图
1)评论区点赞TOP20柱状图

​ 视频评论区点赞top20柱状图。

​ 使用pandas库和和饼图同样的pyecharts库,Pandas的名称来自于面板数据(panel data),基于NumPy构建,提供了高级数据结构数据操作工具

​ 读取好csv文件后通过DataFrame设置好源数据,删除空值与重复值,然后根据文件中点赞数那一列降序排序,取前多少行,也就是点赞数最多的几条评论数据。在pyecharts中设置好下x轴、y轴的数据,以及标题,保存导出即可。

​ 示意图如下:

在这里插入图片描述

2)代码
import csv
from pyecharts import options as opts
from pyecharts.charts import Bar
import pandas as pd
#视频评论区点赞top柱状图
    def zhu_zhuang_tu(self, mid, Name, BV, BVName):
        TopNum = 20  # 点赞前20
        series_nameValue = self.BVName + '点赞TOP' + str(TopNum)
        inUrl = './视频评论信息/' + str(mid) + '-' + Name + '/' + BV + '-' + BVName + '/bilibili评论_' + BV + '.csv'  # 视频评论文件所在路径
        outUrl = './输出库/' + str(mid) + '-' + Name + '-' + BV + '-' + series_nameValue + '柱状图'  # 输出路径

        df = pd.DataFrame(pd.read_csv(inUrl))
        df.dropna()             #删除空值
        df.drop_duplicates()    #删除重复值

        df1 = df.sort_values(by=['点赞数'], ascending=False).head(TopNum)  #根据文件里的'点赞数'列降序排序,取前TopNum行

        c = (
            Bar()
                .add_xaxis(
                    df1['评论内容'].to_list()   # x轴是评论内容
                )
                .add_yaxis(
                    "点赞数",
                    df1["点赞数"].to_list(),
                    color='#87cff1'
                )
                .set_global_opts(
                title_opts=opts.TitleOpts(title = series_nameValue),    #设置标题
                datazoom_opts=[opts.DataZoomOpts(), opts.DataZoomOpts(type_="inside")],
            )
                .render(outUrl + ".html")       #保存输出
        )
        c
(4)漏斗图
1)用户等级分布漏斗图

​ 视频评论区用户等级分布漏斗图。

​ 读取视频评论区数据后,通过pandas库的value_counts方法统计词频,以降序排列,再通过pyecharts库进行绘制图形。

示意图:

在这里插入图片描述

2)代码
from pyecharts import options as opts
from pyecharts.charts import Funnel
import pandas as pd

#视频评论区用户等级分布漏斗图
def lou_dou_tu(self, mid, Name, BV, BVName):

    series_nameValue = self.BVName + '评论区用户等级分布'
    inUrl = './视频评论信息/' + str(mid) + '-' + Name + '/' + BV + '-' + BVName + '/bilibili评论_' + BV + '.csv'  # 视频评论文件所在路径
    outUrl = './输出库/' + str(mid) + '-' + Name + '-' + BV + '-' + series_nameValue + '漏斗图'  # 输出路径

    df = pd.DataFrame(pd.read_csv(inUrl))
    df.dropna()  # 删除空值
    df.drop_duplicates()  # 删除重复值
    grade = df['等级'].value_counts().sort_index(ascending=False)        #统计词频,降序
    gradeNumList = grade.to_list()  #等级数量    #[77, 160, 47, 16, 6]
    gradeList = grade.index  # 等级# Int64Index([6, 5, 4, 3, 2], dtype='int64')

    c = (
        Funnel()
            .add(
                "用户等级",
                [list(z) for z in zip(gradeList, gradeNumList)],
                label_opts=opts.LabelOpts(position="inside"),
            )
            .set_colors(["#f9b4ab", "#fdebd3", "#264e70", '#679186', '#bbd4ce', '#ebf1f4'])  # 颜色
            .set_global_opts(title_opts=opts.TitleOpts(title="Funnel-Label(inside)"))
            .render(outUrl + ".html")       #保存输出
    )
(5)折线图

Line - Stacked_line_chart - Document (pyecharts.org)

(80条消息) Echarts|Stacked Line Chart(折线图堆叠)Y轴数据不正确问题_craftsman2020的博客-CSDN博客_echarts 折线图stack

1)UP主数据变化折线图

​ 96h(自定义h)内up主数据变化折线图。

​ 根据之前所爬取的文件信息,有粉丝数、关注数、点赞数、播放数、和阅读数可供使用。时间为时间戳格式,截止目前,笔者这里的数据是之前放置在树莓派爬取到的数据,总共是96条信息,也就是4天的信息。因为数据较少,时间跨度较低,而不同种类数据值差异较大,所以所得的折线图可能没有直观给出变化趋势。

​ 在该方法中我们依旧用pandas库读取数据,再通过pyecharts库进行绘制图形。筛选列数据后分别加入图例中。

在这里插入图片描述

​ 示意图如下:

在这里插入图片描述

2)代码
from pyecharts.charts import Line
import pandas as pd
import time

    # 24h up主粉丝变化折线图
    def zhe_xian_tu(self, mid, Name):
        interval = 96  # 因为up主粉丝数据是没1h获取一次的,所以这里显示24小时up主的粉丝情况。
        titleName = str(mid) + '-' + Name + '-' + str(interval) + 'h基本数据变化'
        inUrl = './up主信息/' + str(mid) + '-' + Name + '/' + str(mid) + '-' + Name  + '.csv'  #up主信息文件所在路径
        outUrl = './输出库/' + titleName + '折线图' # 输出路径

        df = pd.DataFrame(pd.read_csv(inUrl))
        df.dropna()  # 删除空值
        df1 = df.tail(interval)
        x_data = df1['时间']
        y_data_fans_Num = df1['粉丝数']
        y_data_follow_Num = df1['关注数']
        y_data_like_Num = df1['点赞数']
        y_data_play_Num = df1['播放数']
        y_data_read_Num = df1['阅读数']

        xx_data = []    #2022年5月15日 10:09:22格式的时间作为x轴
        for t in x_data:
            timeArray = time.localtime(t)
            otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)  # 评论时间,时间戳转为标准时间格式,2022-05-05 19:15:14
            xx_data.append(otherStyleTime)

        (
            Line()
                .add_xaxis(xaxis_data=xx_data)
                .add_yaxis(
                    series_name="粉丝数",
                    y_axis=y_data_fans_Num,
                    label_opts=opts.LabelOpts(is_show=False),
                )
                .add_yaxis(
                    series_name="关注数",
                    y_axis=y_data_follow_Num,
                    label_opts=opts.LabelOpts(is_show=False),
                )
                .add_yaxis(
                    series_name="点赞数",
                    y_axis=y_data_like_Num,
                    label_opts=opts.LabelOpts(is_show=False),
                )
                .add_yaxis(
                    series_name="播放数",
                    y_axis=y_data_play_Num,
                    label_opts=opts.LabelOpts(is_show=False),
                )
                .add_yaxis(
                    series_name="阅读数",
                    y_axis=y_data_read_Num,
                    label_opts=opts.LabelOpts(is_show=False),
                )
                .set_global_opts(
                    title_opts=opts.TitleOpts(title=titleName),
                    tooltip_opts=opts.TooltipOpts(trigger="axis"),
                    yaxis_opts=opts.AxisOpts(
                        type_="value",
                        axistick_opts=opts.AxisTickOpts(is_show=True),
                        splitline_opts=opts.SplitLineOpts(is_show=True),
                    ),
                    xaxis_opts=opts.AxisOpts(type_="category", boundary_gap=False),
                    legend_opts=opts.LegendOpts(orient="vertical", pos_top="10%", pos_left="91%"),  # 图例设置
                )
                .render(outUrl + ".html")       #保存输出
        )
(6)平行坐标系
1)各视频数据平行坐标系图

同一个up,不同视频总播放数、历史累计弹幕数、点赞、投币、收藏、转发的可视化。

Document (pyecharts.org)

pyecharts 设置地图大小错误 - 简书 (jianshu.com)

​ 因为需要横评不同视频的数据,因此我们重新定义一个类differentVideoDataEvaluation,并且在该类下定义一个新方法ping_xing_zuo_biao_xi(),该方法通过接受一个list集合,其中就是同一个up不同视频的数据。例如

data = [
    [1760078, 4185, 57384, 22057, 5242, 979, 'BV1344y1u7K8\n《为了不洗碗,我把碗筷\n吃了!》'], 
    [137866, 968, 27086, 20657, 3424, 392, 'BV1WT4y1B7Df\n《为了打嗝,专门去盖了\n个打阁!》'], 
    [125085, 316, 23713, 10821, 2001, 190, 'BV1NT4y1k7qU\n《【美食杂交技术】油条\n与薯条的完美结合体!》'],
    [400429, 1649, 76976, 72977, 9390, 766, 'BV11r4y1J7cH\n《不搞钱,就搞笑!》']
]

​ 那么如何获取数据呢?我们调用之前写的**videoData_content_get()**方法,查看该方法,

def videoData_content_get(self, BV):
	......此处代码省略
    return [timeNow, int(total_playback), int(total_barrage), like, coin, favorite, share]   #[1652415252.7468505, 1488493, 3953, 53458, 21430, 4954, 918]

​ 发现返回的是一个列表,包含了爬取时间、总播放数、历史累计弹幕数、点赞数、投币数、收藏数和分享数。

​ 我们在此数据上新添加BV的信息和BVName的信息就可以了,并把不同的视频的list写在一个list里面,将其传给**ping_xing_zuo_biao_xi()**方法就好了。

#获取数据列表
videoDataVariety1 = videoDataVariety(382193067, '巫托邦', 'BV1344y1u7K8', cookies) #初始化
datalist1 = videoDataVariety1.videoData_content_get(videoDataVariety1.BV)# 调用之前写的方法返回
videoDataVariety2 = videoDataVariety(382193067, '巫托邦', 'BV1WT4y1B7Df', cookies)
datalist2 = videoDataVariety2.videoData_content_get(videoDataVariety2.BV)
videoDataVariety3 = videoDataVariety(382193067, '巫托邦', 'BV1NT4y1k7qU', cookies)
datalist3 = videoDataVariety3.videoData_content_get(videoDataVariety3.BV)
videoDataVariety4 = videoDataVariety(382193067, '巫托邦', 'BV11r4y1J7cH', cookies)
datalist4 = videoDataVariety4.videoData_content_get(videoDataVariety4.BV)
# 此处添加BV和BVName
datalist1.append(videoDataVariety1.BV)
datalist1.append(videoDataVariety1.BVName)
datalist2.append(videoDataVariety2.BV)
datalist2.append(videoDataVariety2.BVName)
datalist3.append(videoDataVariety3.BV)
datalist3.append(videoDataVariety3.BVName)
datalist4.append(videoDataVariety4.BV)
datalist4.append(videoDataVariety4.BVName)
dataList = [
    datalist1,
    datalist2,
    datalist3,
    datalist4
]

​ 在ping_xing_zuo_biao_xi()方法中,我们将list下中第一条视频信息中的第一列的时间戳设置为该平行坐标系的生成时间,并将其赋值给timeArray,设置好titleName和输出路径,通过pyecharts库进行绘制平行坐标系图即可。

​ 注意:由于BV和BVName组成的字符串太长,如果不进行换行,在最终输出的图标中最后一列也就是BV-BVName的坐标轴上的数据显示不全,因此我们需要在合理的位置上添加’\n’

data = []
different_BV_length = len(videoData_content_get_Return_newListList)  #总共有几个视频的横评
BVNameList = [] #BV-BVName构成的list
for i in range(different_BV_length):
    BVNameLength = len(videoData_content_get_Return_newListList[i][8])
    v1 = videoData_content_get_Return_newListList[i][8]
    if BVNameLength > 22:
        v1 = videoData_content_get_Return_newListList[i][8][0:11] + '\n' + \
             videoData_content_get_Return_newListList[i][8][11:22] + '\n' + \
             videoData_content_get_Return_newListList[i][8][22:]
    elif BVNameLength> 11:
        v1= videoData_content_get_Return_newListList[i][8][0:11] +  '\n' + \
            videoData_content_get_Return_newListList[i][8][11:]
    str = videoData_content_get_Return_newListList[i][7] + '\n' + v1
    BVNameList.append(str)
    data.append([])
    data[i].append(videoData_content_get_Return_newListList[i][1]) #总播放数
    data[i].append(videoData_content_get_Return_newListList[i][2]) #历史累计弹幕数
    data[i].append(videoData_content_get_Return_newListList[i][3]) #点赞
    data[i].append(videoData_content_get_Return_newListList[i][4]) #投币
    data[i].append(videoData_content_get_Return_newListList[i][5]) #收藏
    data[i].append(videoData_content_get_Return_newListList[i][6]) #分享
    data[i].append(str) #BV-BVName

示意图如下:

在这里插入图片描述

2)代码
from bilibiliVideoDataAnalysis.work import videoDataDetection_	#导入之前自己写的模块
from pyecharts import options as opts
from pyecharts.charts import Parallel
import time

#可视化(平行坐标系)
class differentVideoDataEvaluation:
    #不同视频的评测
    # 平行坐标系
    # 同一个up,不同视频总播放数、历史累计弹幕数、点赞、投币、收藏、转发的可视化
    def ping_xing_zuo_biao_xi(self, upname, videoData_content_get_Return_newListList):
        # videoData_content_get_ReturnListList:
        # 该参数是videoDataDetection_.py文件中videoDataVariety类videoData_content_get()方法的不同视频返回数据集合,
        # 并且新新添加了两列:BV、BVName
        # 例如[
        #     [1652599039.1057296, 1760078, 4185, 57384, 22057, 5242, 979, 'BV1344y1u7K8', '《为了不洗碗,我把碗筷吃了!》'],
        #     [1652599039.8925633, 137866, 968, 27086, 20657, 3424, 392, 'BV1WT4y1B7Df', '《为了打嗝,专门去盖了个打阁!》'],
        #     [1652599041.0497084, 125085, 316, 23713, 10821, 2001, 190, 'BV1NT4y1k7qU', '《【美食杂交技术】油条与薯条的完美结合体!》'],
        #     [1652599042.146411, 400429, 1649, 76976, 72977, 9390, 766, 'BV11r4y1J7cH', '《不搞钱,就搞笑!》']
        # ]

        data = []
        different_BV_length = len(videoData_content_get_Return_newListList)  #总共有几个视频的横评
        BVNameList = [] #BV-BVName构成的list
        for i in range(different_BV_length):
            BVNameLength = len(videoData_content_get_Return_newListList[i][8])
            v1 = videoData_content_get_Return_newListList[i][8]
            if BVNameLength > 22:
                v1 = videoData_content_get_Return_newListList[i][8][0:11] + '\n' + \
                     videoData_content_get_Return_newListList[i][8][11:22] + '\n' + \
                     videoData_content_get_Return_newListList[i][8][22:]
            elif BVNameLength> 11:
                v1= videoData_content_get_Return_newListList[i][8][0:11] +  '\n' + \
                    videoData_content_get_Return_newListList[i][8][11:]
            str = videoData_content_get_Return_newListList[i][7] + '\n' + v1
            BVNameList.append(str)
            data.append([])
            data[i].append(videoData_content_get_Return_newListList[i][1]) #总播放数
            data[i].append(videoData_content_get_Return_newListList[i][2]) #历史累计弹幕数
            data[i].append(videoData_content_get_Return_newListList[i][3]) #点赞
            data[i].append(videoData_content_get_Return_newListList[i][4]) #投币
            data[i].append(videoData_content_get_Return_newListList[i][5]) #收藏
            data[i].append(videoData_content_get_Return_newListList[i][6]) #分享
            data[i].append(str) #BV-BVName

        timeArray = time.localtime(videoData_content_get_Return_newListList[0][0])      #获取横评视频时间
        genertTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)  # 生成时间,时间戳转为标准时间格式,2022-05-05 19:15:14

        titleName = genertTime + '-' + upname + "不同视频横评"
        outUrl = './输出库/' + upname + '-不同视频横评平行坐标图'  # 输出路径

        c = (
            Parallel(init_opts=opts.InitOpts(width="1200px", height="700px",page_title=titleName))
                .add_schema(
                [
                    opts.ParallelAxisOpts(dim=0, name='总播放数'),
                    opts.ParallelAxisOpts(dim=1, name='历史累计弹幕数'),
                    opts.ParallelAxisOpts(dim=2, name='点赞'),
                    opts.ParallelAxisOpts(dim=3, name="投币"),
                    opts.ParallelAxisOpts(dim=4, name="收藏"),
                    opts.ParallelAxisOpts(dim=5, name="转发"),
                    opts.ParallelAxisOpts(
                        dim=6,
                        name="BV-视频名称",
                        type_="category",
                        data=BVNameList,
                    ),
                ]
            )
                .add(
                    upname,
                    data,
                )
                .set_global_opts(
                    title_opts=opts.TitleOpts(title = titleName),
                )  #设置全局配置项
                .render(outUrl + ".html")       #保存输出
        )
        c
(7)visualization文件代码
#可视化
import csv
import os
import wordcloud as wc
from PIL import Image
import numpy as np
from pyecharts import options as opts
from pyecharts.charts import Bar, Pie, Funnel, Line
import pandas as pd
import time
from pyecharts.charts import Parallel
from bilibiliVideoDataAnalysis.work.videoDataDetection_ import videoDataVariety

#可视化(词云图、饼图、柱状图、漏斗图、折线图)
class visualization:

    def __init__(self, mid, Name, BV, BVName):
        self.mid = mid
        self.Name = Name
        self.BV = BV
        self.BVName = BVName

    # 评论词云图
    def ci_Yun_Tu(self):

        dirname = '输出库'
        begin = os.getcwd()  # 保存开始文件工作路径

        returnValue = self.column_content_get(self.mid, self.Name, self.BV, self.BVName, 7) #在文件中,评论所在列为第8列,下标为7
        text = " ".join(returnValue)
        # 设置背景形状图片
        mask = np.array(Image.open("./素材库/fivestar.png"))
        # 设置停用词
        stopwords = set()
        content = [line.strip() for line in open('./素材库/stopwords.txt', 'r',encoding='utf-8').readlines()]
        stopwords.update(content)

        # 画图
        word_cloud = wc.WordCloud(scale=10, font_path="C:/Windows/Fonts/msyh.ttc", mask=mask, stopwords=stopwords,
                                  background_color="white")  # 大小、字体、背景形状停用词、背景颜色
        word_cloud.generate(text)
        # 如果没有该文件夹则创建一个
        if not os.path.isdir(dirname):
            os.mkdir(dirname)
        os.chdir(dirname)  # 改变当前工作目录到指定的路径
        word_cloud.to_file("词云图-{}-{}.png".format(self.Name, self.BVName))  # 绘制到一个图片里
        os.chdir(begin)  # 恢复文件工作路径

    #视频评论区用户性别分类饼状图
    def bing_tu(self):
        series_nameValue = self.BVName + '评论区用户'                     #鼠标悬浮在图上的提示文字
        sexColumn = self.column_content_get(self.mid, self.Name, self.BV, self.BVName, 2)
        maleNum = 0
        femaleNum = 0
        unknownNum = 0
        for i in sexColumn:
            if (i == '男'): maleNum += 1
            elif (i == '女'): femaleNum += 1
            elif (i == '保密'): unknownNum += 1
        inner_x_data = ["男", "女", "保密"]        #分类
        inner_y_data = [maleNum, femaleNum, unknownNum]                    #分类对应的值
        inner_data_pair = [list(z) for z in zip(inner_x_data, inner_y_data)]    #值的“合集”
        outUrl = './输出库/' + str(self.mid) + '-' + self.Name + '-' + self.BV + '-' + series_nameValue + '环状图'     #输出路径
        c = (
            Pie()
                .add(
                    series_nameValue,
                    inner_data_pair,
                    radius=["50%", "75%"],  # 调整半径
                )
                .set_colors(["#65a8d8", "#f8a3cf", "#9da5ad"])      #颜色
                .set_global_opts(
                    title_opts=opts.TitleOpts(title="性别构成情况"),  #标题
                    legend_opts=opts.LegendOpts(orient="vertical", pos_top="10%", pos_left="91%"),# 图例设置
                )
                .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))   # 设置标签
                .render(outUrl + ".html")       #保存输出
        )

    #视频评论区点赞top柱状图
    def zhu_zhuang_tu(self, mid, Name, BV, BVName):
        TopNum = 20  # 点赞前20
        series_nameValue = self.BVName + '点赞TOP' + str(TopNum)
        inUrl = './视频评论信息/' + str(mid) + '-' + Name + '/' + BV + '-' + BVName + '/bilibili评论_' + BV + '.csv'  # 视频评论文件所在路径
        outUrl = './输出库/' + str(mid) + '-' + Name + '-' + BV + '-' + series_nameValue + '柱状图'  # 输出路径

        df = pd.DataFrame(pd.read_csv(inUrl))
        df.dropna()             #删除空值
        df.drop_duplicates()    #删除重复值

        df1 = df.sort_values(by=['点赞数'], ascending=False).head(TopNum)  #根据文件里的'点赞数'列降序排序,取前TopNum行

        c = (
            Bar()
                .add_xaxis(
                    df1['评论内容'].to_list()   # x轴是评论内容
                )
                .add_yaxis(
                    "点赞数",
                    df1["点赞数"].to_list(),
                    color='#87cff1'
                )
                .set_global_opts(
                    title_opts=opts.TitleOpts(title = series_nameValue),    #设置标题
                    datazoom_opts=[opts.DataZoomOpts(), opts.DataZoomOpts(type_="inside")],
                    legend_opts=opts.LegendOpts(orient="vertical", pos_top="10%", pos_left="91%"),  # 图例设置
            )
                .render(outUrl + ".html")       #保存输出
        )
        c

    #视频评论区用户等级分布漏斗图
    def lou_dou_tu(self, mid, Name, BV, BVName):

        series_nameValue = BVName + '评论区用户等级分布'
        inUrl = './视频评论信息/' + str(mid) + '-' + Name + '/' + BV + '-' + BVName + '/bilibili评论_' + BV + '.csv'  # 视频评论文件所在路径
        outUrl = './输出库/' + str(mid) + '-' + Name + '-' + BV + '-' + series_nameValue + '漏斗图'  # 输出路径

        df = pd.DataFrame(pd.read_csv(inUrl))
        df.dropna()  # 删除空值
        df.drop_duplicates()  # 删除重复值
        grade = df['等级'].value_counts().sort_index(ascending=False)        #统计词频,降序
        gradeNumList = grade.to_list()  #等级数量    #[77, 160, 47, 16, 6]
        gradeList = grade.index  # 等级# Int64Index([6, 5, 4, 3, 2], dtype='int64')

        c = (
            Funnel()
                .add(
                    "用户等级",
                    [list(z) for z in zip(gradeList, gradeNumList)],
                    label_opts=opts.LabelOpts(position="inside"),
                )
                .set_colors(["#f9b4ab", "#fdebd3", "#264e70", '#679186', '#bbd4ce', '#ebf1f4'])  # 颜色
                .set_global_opts(
                    title_opts=opts.TitleOpts(title=series_nameValue),
                    legend_opts=opts.LegendOpts(orient="vertical", pos_top="10%", pos_left="91%"),  # 图例设置
                )
                .render(outUrl + ".html")       #保存输出
        )

    # 96h(自定义h)内up主本数据变化折线图
    def zhe_xian_tu(self, mid, Name):
        interval = 96  # 因为up主粉丝数据是没1h获取一次的,所以这里显示24小时up主的粉丝情况。
        titleName = str(mid) + '-' + Name + '-' + str(interval) + 'h基本数据变化'
        inUrl = './up主信息/' + str(mid) + '-' + Name + '/' + str(mid) + '-' + Name  + '.csv'  #up主信息文件所在路径
        outUrl = './输出库/' + titleName + '折线图' # 输出路径

        df = pd.DataFrame(pd.read_csv(inUrl))
        df.dropna()  # 删除空值
        df1 = df.tail(interval)
        x_data = df1['时间']
        y_data_fans_Num = df1['粉丝数']
        y_data_follow_Num = df1['关注数']
        y_data_like_Num = df1['点赞数']
        y_data_play_Num = df1['播放数']
        y_data_read_Num = df1['阅读数']

        xx_data = []    #2022年5月15日 10:09:22格式的时间作为x轴
        for t in x_data:
            timeArray = time.localtime(t)
            otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)  # 评论时间,时间戳转为标准时间格式,2022-05-05 19:15:14
            xx_data.append(otherStyleTime)

        (
            Line()
                .add_xaxis(xaxis_data=xx_data)
                .add_yaxis(
                    series_name="粉丝数",
                    y_axis=y_data_fans_Num,
                    label_opts=opts.LabelOpts(is_show=False),
                )
                .add_yaxis(
                    series_name="关注数",
                    y_axis=y_data_follow_Num,
                    label_opts=opts.LabelOpts(is_show=False),
                )
                .add_yaxis(
                    series_name="点赞数",
                    y_axis=y_data_like_Num,
                    label_opts=opts.LabelOpts(is_show=False),
                )
                .add_yaxis(
                    series_name="播放数",
                    y_axis=y_data_play_Num,
                    label_opts=opts.LabelOpts(is_show=False),
                )
                .add_yaxis(
                    series_name="阅读数",
                    y_axis=y_data_read_Num,
                    label_opts=opts.LabelOpts(is_show=False),
                )
                .set_global_opts(
                    title_opts=opts.TitleOpts(title=titleName),
                    tooltip_opts=opts.TooltipOpts(trigger="axis"),
                    yaxis_opts=opts.AxisOpts(
                        type_="value",
                        axistick_opts=opts.AxisTickOpts(is_show=True),
                        splitline_opts=opts.SplitLineOpts(is_show=True),
                    ),
                    xaxis_opts=opts.AxisOpts(type_="category", boundary_gap=False),
                    legend_opts=opts.LegendOpts(orient="vertical", pos_top="10%", pos_left="91%"),  # 图例设置
                )
                .render(outUrl + ".html")       #保存输出
        )

    #获取csv列数据
    def column_content_get(self,mid, Name, BV, BVName, column):
        #column   第几列
        url = f'./视频评论信息/{mid}-{Name}/{BV}-{BVName}/bilibili评论_{BV}.csv'
        with open(url, mode='r', encoding='utf-8') as f:
            reader = csv.reader(f)
            column = [row[column] for row in reader]
            return column

    def main(self):
        self.ci_Yun_Tu()
        self.bing_tu()
        self.zhu_zhuang_tu(self.mid, self.Name, self.BV, self.BVName)
        self.lou_dou_tu(self.mid, self.Name, self.BV, self.BVName)
        self.zhe_xian_tu(self.mid, self.Name )
        pass

#可视化(平行坐标系)
class differentVideoDataEvaluation:
    #不同视频的评测
    # 平行坐标系
    # 同一个up,不同视频总播放数、历史累计弹幕数、点赞、投币、收藏、转发的可视化
    def ping_xing_zuo_biao_xi(self, upname, videoData_content_get_Return_newListList):
        # videoData_content_get_ReturnListList:
        # 该参数是videoDataDetection_.py文件中videoDataVariety类videoData_content_get()方法的不同视频返回数据集合,
        # 并且新新添加了两列:BV、BVName
        # 例如[
        #     [1652599039.1057296, 1760078, 4185, 57384, 22057, 5242, 979, 'BV1344y1u7K8', '《为了不洗碗,我把碗筷吃了!》'],
        #     [1652599039.8925633, 137866, 968, 27086, 20657, 3424, 392, 'BV1WT4y1B7Df', '《为了打嗝,专门去盖了个打阁!》'],
        #     [1652599041.0497084, 125085, 316, 23713, 10821, 2001, 190, 'BV1NT4y1k7qU', '《【美食杂交技术】油条与薯条的完美结合体!》'],
        #     [1652599042.146411, 400429, 1649, 76976, 72977, 9390, 766, 'BV11r4y1J7cH', '《不搞钱,就搞笑!》']
        # ]

        data = []
        different_BV_length = len(videoData_content_get_Return_newListList)  #总共有几个视频的横评
        BVNameList = [] #BV-BVName构成的list
        for i in range(different_BV_length):
            BVNameLength = len(videoData_content_get_Return_newListList[i][8])
            v1 = videoData_content_get_Return_newListList[i][8]
            if BVNameLength > 22:
                v1 = videoData_content_get_Return_newListList[i][8][0:11] + '\n' + \
                     videoData_content_get_Return_newListList[i][8][11:22] + '\n' + \
                     videoData_content_get_Return_newListList[i][8][22:]
            elif BVNameLength> 11:
                v1= videoData_content_get_Return_newListList[i][8][0:11] +  '\n' + \
                    videoData_content_get_Return_newListList[i][8][11:]
            str = videoData_content_get_Return_newListList[i][7] + '\n' + v1
            BVNameList.append(str)
            data.append([])
            data[i].append(videoData_content_get_Return_newListList[i][1]) #总播放数
            data[i].append(videoData_content_get_Return_newListList[i][2]) #历史累计弹幕数
            data[i].append(videoData_content_get_Return_newListList[i][3]) #点赞
            data[i].append(videoData_content_get_Return_newListList[i][4]) #投币
            data[i].append(videoData_content_get_Return_newListList[i][5]) #收藏
            data[i].append(videoData_content_get_Return_newListList[i][6]) #分享
            data[i].append(str) #BV-BVName

        timeArray = time.localtime(videoData_content_get_Return_newListList[0][0])      #获取横评视频时间
        genertTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)  # 生成时间,时间戳转为标准时间格式,2022-05-05 19:15:14

        titleName = genertTime + '-' + upname + "不同视频横评"
        outUrl = './输出库/' + upname + '-不同视频横评平行坐标图'  # 输出路径

        c = (
            Parallel(init_opts=opts.InitOpts(width="1200px", height="700px",page_title=titleName))
                .add_schema(
                [
                    opts.ParallelAxisOpts(dim=0, name='总播放数'),
                    opts.ParallelAxisOpts(dim=1, name='历史累计弹幕数'),
                    opts.ParallelAxisOpts(dim=2, name='点赞'),
                    opts.ParallelAxisOpts(dim=3, name="投币"),
                    opts.ParallelAxisOpts(dim=4, name="收藏"),
                    opts.ParallelAxisOpts(dim=5, name="转发"),
                    opts.ParallelAxisOpts(
                        dim=6,
                        name="BV-视频名称",
                        type_="category",
                        data=BVNameList,
                    ),
                ]
            )
                .add(
                    upname,
                    data,
                )
                .set_global_opts(
                    title_opts=opts.TitleOpts(title = titleName),
                )  #设置全局配置项
                .render(outUrl + ".html")       #保存输出
        )
        c


if __name__ == '__main__':
    cookie = "buvid3=11707BB8-8181-70C7-EBE1-FB1609F40FC370555infoc; i-wanna-go-back=-1; _uuid=F4221228-EF95-B7F10-49C1-F710CAC68D109F77140infoc; buvid4=E437889C-0A9F-DEF4-C164-E3F9F456407172347-022032622-MnLxL6Vqo8K/D8N1XzXHLQ%3D%3D; nostalgia_conf=-1; buvid_fp_plain=undefined; blackside_state=1; rpdid=|(J~J|R~m)Jm0J'uYR)Jm~JYR; CURRENT_BLACKGAP=0; hit-dyn-v2=1; LIVE_BUVID=AUTO4316488832212386; CURRENT_QUALITY=0; b_ut=5; fingerprint3=5ad9983134e17174abef4db7b440a5ab; SESSDATA=da01081b%2C1667737162%2C066fd%2A51; bili_jct=e257a396d8d258b042d32a8aa9494f9e; DedeUserID=154100711; DedeUserID__ckMd5=4a5f601a3689140a; sid=ldg719nz; fingerprint=19c41c196550f8268e8c94867b19f6d8; buvid_fp=19c41c196550f8268e8c94867b19f6d8; CURRENT_FNVAL=4048; PVID=1; bp_video_offset_154100711=660026573772030000; b_lsid=51210F6A10_180C576256C; b_timer=%7B%22ffp%22%3A%7B%22333.788.fp.risk_11707BB8%22%3A%22180C59D0C11%22%2C%22333.999.fp.risk_11707BB8%22%3A%22180C59D142F%22%2C%22444.41.fp.risk_11707BB8%22%3A%22180C59D7218%22%7D%7D"
    cookies = {}
    for c in cookie.split(";"):
        b = c.split("=")
        cookies[b[0]] = b[1]

    visualization_type1 = visualization(382193067, '巫托邦', 'BV1344y1u7K8', '《为了不洗碗,我把碗筷吃了!》')
    visualization_type1.main()      #生成(词云图、饼图、柱状图、漏斗图、折线图)

    visualization_type2 = differentVideoDataEvaluation()    #创建平行坐标系类的对象

    #获取数据列表
    videoDataVariety1 = videoDataVariety(382193067, '巫托邦', 'BV1344y1u7K8', cookies) #初始化
    datalist1 = videoDataVariety1.videoData_content_get(videoDataVariety1.BV)# 调用之前写的方法返回
    videoDataVariety2 = videoDataVariety(382193067, '巫托邦', 'BV1WT4y1B7Df', cookies)
    datalist2 = videoDataVariety2.videoData_content_get(videoDataVariety2.BV)
    videoDataVariety3 = videoDataVariety(382193067, '巫托邦', 'BV1NT4y1k7qU', cookies)
    datalist3 = videoDataVariety3.videoData_content_get(videoDataVariety3.BV)
    videoDataVariety4 = videoDataVariety(382193067, '巫托邦', 'BV11r4y1J7cH', cookies)
    datalist4 = videoDataVariety4.videoData_content_get(videoDataVariety4.BV)
    # 此处添加BV和BVName
    datalist1.append(videoDataVariety1.BV)
    datalist1.append(videoDataVariety1.BVName)
    datalist2.append(videoDataVariety2.BV)
    datalist2.append(videoDataVariety2.BVName)
    datalist3.append(videoDataVariety3.BV)
    datalist3.append(videoDataVariety3.BVName)
    datalist4.append(videoDataVariety4.BV)
    datalist4.append(videoDataVariety4.BVName)
    dataList = [
        datalist1,
        datalist2,
        datalist3,
        datalist4
    ]

    visualization_type2.ping_xing_zuo_biao_xi(videoDataVariety1.name, dataList) #生成平行坐标系

"""
==========================
@auther:JingDe
@Date:2022/5/13 17:18
@email:
@IDE:PyCharm
==========================
"""

三、不足之处

  • 折线图平行坐标图的制作中,折线图所表示的up主的基本信息体现不够明晰,应该将这两个图所表示的内容进行互换,即用折线图来表示一个新发的视频的变化趋势,直接调用videoDataDetection_.py文件所获取的csv文件读取用pyechartsi制作数据表即可,理想状态下,会充分发挥折线图的优势;用平行坐标图来表示不同up主的基本信息横评,因为在大部分情况下,up主的基本信息已基本成型,在短时间内不会改变,用平行坐标图来体现的话更有优势。
  • 可能本文有些地方表述不清。
  • 有些类的方法写的不是很“优雅”,比如在最后differentVideoDataEvaluation类方面的实现过程中。

四、搁置项(可忽略)

这一部分就是刚开始做的无用功,对最终结果没有重要帮助,但是我又舍不得删,故放在最后,可跳过

1.获取User-Agent

登录目标网站:

2013年12月西安空气质量指数AQI_PM2.5日历史数据_中国空气质量在线监测分析平台历史数据 (aqistudy.cn)

在这里插入图片描述

查看数据元素

在这里插入图片描述

2.分析用户关注得所有up主的api

查找用户关注up主的api。根据api可以得到每次调用显示20个用户。

在这里插入图片描述

在这里插入图片描述

获的请求api:

https://api.bilibili.com/x/relation/followings?vmid=154100711&pn=1&ps=20&order=desc&order_type=attention&jsonp=jsonp&callback=__jp5
  • vimd=后的参数就是用户的mid号
  • pn=1指用户的关注的第一面用户

在这里插入图片描述

删去callback参数,vmid用户id,我们将pn放到后面方便调用时换页。

https://api.bilibili.com/x/relation/followings?vmid=2&pn=1&ps=20&order=desc&jsonp=jsonp&callback=__jp7
https://api.bilibili.com/x/relation/followings?vmid=######&ps=20&order=desc&jsonp=jsonp&pn=#

筛选需要的up主信息:

  • mid:up主uid
  • mtime:生日
  • desc:官方认证信息
  • sign:个性签名
  • uname:账号名称

在这里插入图片描述

五、参考资料

【1】 (108条消息) F12查看headers的含义_不熬夜的程序猿的博客-CSDN博客_f12查看header

【2】 (108条消息) python模块–BeautifulSoup4 和 lxml_黄佳俊、的博客-CSDN博客_beautifulsoup4 lxml

【3】 (109条消息) 【网络爬虫 | python】bilibili评论信息爬取(基础版)_竹一笔记的博客-CSDN博客_b站评论爬取

【4】 (109条消息) python:类基础_不怕猫的耗子A的博客-CSDN博客_python 类

【5】 一篇文章带你搞懂Python中的类 (baidu.com)

【6】 Python3 面向对象 | 菜鸟教程 (runoob.com)

【7】 【python爬虫】每天统计一遍up主粉丝数!-python黑洞网 (pythonheidong.com)

【8】[(78条消息) Python:录记个做,写写便随_Ambitioner_c的博客-CSDN博客](https://blog.csdn.net/qq_41297934/article/details/105371870?ops_request_misc=&request_id=&biz_id=102&utm_term=Python 实现某个功能每隔一段时间被执行一次的功能方法&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-8-105371870.142v9pc_search_result_control_group,157v4control&spm=1018.2226.3001.4187)

【9】[(78条消息) Python实现定时任务的几种方法_从流域到海域的博客-CSDN博客_python定时任务的实现方式](https://blog.csdn.net/Solo95/article/details/122026111?ops_request_misc=&request_id=&biz_id=102&utm_term=Python 实现某个功能每隔一段时间被执行一次的功能方法&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-5-122026111.142v9pc_search_result_control_group,157v4control&spm=1018.2226.3001.4187)

【10】 (77条消息) Python 实现某个功能每隔一段时间被执行一次的功能_独一无二的小个性的博客-CSDN博客_python 每隔一段时间

www.suphelp.cn/submit32

【11】https://www.bilibili.com/video/BV1ot411R7SM?spm_id_from=333.999.0.0

【12】https://www.bilibili.com/video/BV11r4y1J7cH?spm_id_from=333.999.0.0

【13】https://www.bilibili.com/video/BV1fQ4y1q7SB/?spm_id_from=333.788.recommend_more_video.16

【14】 (78条消息) python session保持cookie_python接口自动化测试八:更新Cookies、session保持会话_冷君聊大片的博客-CSDN博客

【15】 (78条消息) Python中BeautifulSoup库的用法_阎_松的博客-CSDN博客_beautifulsoup库的作用

【16】 (78条消息) 哔哩哔哩视频播放量、点赞量、评论、收藏、投币与转发信息定时爬虫_Mark_Lee131的博客-CSDN博客

【17】 (79条消息) python词云图详细教程_全宇宙最最帅气的哆啦A梦小怪兽的博客-CSDN博客_python词云图

【18】 (79条消息) Python将冰冰的第一条vlog并进行数据分析_北山啦的博客-CSDN博客_python爬取b站评论

【19】 (79条消息) 【Pyecharts-学习笔记系列之Pie(三)】_浪花卷起千堆雪的博客-CSDN博客

【20】 快速开始 - pyecharts - A Python Echarts Plotting Library built with love.

【21】 pyecharts 设置地图大小错误 - 简书 (jianshu.com)

【22】 Document (pyecharts.org)

【23】 60 种常用可视化图表,该怎么用?

m_id_from=333.788.recommend_more_video.16

【14】 (78条消息) python session保持cookie_python接口自动化测试八:更新Cookies、session保持会话_冷君聊大片的博客-CSDN博客

【15】 (78条消息) Python中BeautifulSoup库的用法_阎_松的博客-CSDN博客_beautifulsoup库的作用

【16】 (78条消息) 哔哩哔哩视频播放量、点赞量、评论、收藏、投币与转发信息定时爬虫_Mark_Lee131的博客-CSDN博客

【17】 (79条消息) python词云图详细教程_全宇宙最最帅气的哆啦A梦小怪兽的博客-CSDN博客_python词云图

【18】 (79条消息) Python将冰冰的第一条vlog并进行数据分析_北山啦的博客-CSDN博客_python爬取b站评论

【19】 (79条消息) 【Pyecharts-学习笔记系列之Pie(三)】_浪花卷起千堆雪的博客-CSDN博客

【20】 快速开始 - pyecharts - A Python Echarts Plotting Library built with love.

【21】 pyecharts 设置地图大小错误 - 简书 (jianshu.com)

【22】 Document (pyecharts.org)

【23】 60 种常用可视化图表,该怎么用?

chatgpt001
关注 关注
  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
python大作业代码文档,python期末大作业题目
阿发狗伪原创
10-02 1926
大家好,给大家分享一下python大作业代码文档,很多人还不知道这一点。2.每一列空格部分都使用前后两个值进行“线性插值”填空,以月为单位进行插值。将多幅图片组织为mp4格式视频,帧率设置为每秒12帧。1.每一列最前面的正整数之前全部设为0。先进行一些库的引入和常规操作。
python大作业有哪些题目,python大作业代码文档
chatgpt001的博客
08-08 906
传统的职工健康信息是通过纸质版进行记录的,管理员需要对每个职工的姓名包括健康体检的项目信息详细地写在纸上并保存好。但是纸质版的健康信息随着时间的流逝,可能会丢失,给管理员的查询带来不便。并且在纸质记录的过程中,可能会有不正常的情况出现。因此我们开发一款基于Django的职工健康档案管理系统,可以让管理员在线对职工体检的报告进行记录和收费,这样可以尽可能降低数据记录失误的可能性,并且可以最大程度地保存好健康数据,方便日后的查看。
交大python大作业
04-19
本人的程序设计通识课期末大作业代码和说明文档,欢迎大家参考,共同学习python
Python期末课程设计(大作业)—— 学生成绩管理系统
最新发布
Ccry_0218_的博客
07-12 478
Python期末课程设计(大作业)—— 学生成绩管理系统
python大作业代码文档,python大作业设计报告
神经网络爱好者
12-12 385
大家好,给大家分享一下python大作业代码文档,很多人还不知道这一点。下面详细解释一下。现在让我们来看看!
(附完整代码和实验报告)【python 大作业】实现一个聊天机器人,关键词双重查找,结合语境查找,爬虫查询,图形化界面,语音播报。
Timeseed的博客
06-09 6310
1.进行简单的日常生活聊天(首先根据输入寻找对应答案,如果未找到对应答案,就结合从百度百科上爬取下的内容进行回答) 2.关于具体电影和音乐话题的聊天(联系上文语境、实现关键词双重查找) 附加部分: 为机器人增加了语音播报功能 实现了一个简单的GUI,实现了交互功能.........
Python课程设计大作业基于Django的学生信息管理系统源码+数据库+文档说明.zip
05-20
Python课程设计大作业基于Django的学生信息管理系统源码+数据库+文档说明 班主任登录可以对学生进行管理, 同时可以查看图形化界面的人员分布信息,可以更客观形象的看出学生的比例人数等。 同时,班主任在平台上的任何操作,都会有相关信息发送到微信,进行实时监控。班主任登录可以对学生进行管理, 同时可以查看图形化界面的人员分布信息,可以更客观形象的看出学生的比例人数等。 同时,班主任在平台上的任何操作,都会有相关信息发送到微信,进行实时监控。班主任登录可以对学生进行管理, 同时可以查看图形化界面的人员分布信息,可以更客观形象的看出学生的比例人数等。 同时,班主任在平台上的任何操作,都会有相关信息发送到微信,进行实时监控。班主任登录可以对学生进行管理, 同时可以查看图形化界面的人员分布信息,可以更客观形象的看出学生的比例人数等。 同时,班主任在平台上的任何操作,都会有相关信息发送到微信,进行实时监控。班主任登录可以对学生进行管理, 同时可以查看图形化界面的人员分布信息,可以更客观形象的看出学生的比例人数等。 同时,班主任在平台上的任何操作,都会有相关信息发送到微信,进行实时监控。
python大作业仓库管理系统.7z
07-02
Python大作业仓库管理系统是一个基于Python编程语言开发的项目,旨在实现对仓库中物品的高效管理。这个系统可能包含了数据存储、库存跟踪、入库出库操作、物品查询以及各种报告生成等功能,是学习Python面向对象编程...
python大作业.zip
01-03
Python大作业.zip】是一个包含Python编程练习的压缩文件,主要关注的是Python语言的应用,特别是Python爬虫技术。这个大作业可能是一个课程项目或者自我提升的学习任务,旨在提高学生的实战能力,理解并掌握网络...
python学习日记day06作业
m0_64803077的博客
04-13 920
作业1:求第n项的斐波那契数列的值(n>=1) n = int(input('请输入项数: ')) ''' 斐波那契数列从第三项开始, 每一项都等于前两项之和 ''' def flibsOne(n): a=0 b=1 c=0 #用于存放前两项之和 i=1 print('斐波那契数列:') while i<=n : i+=1 print(a) c=a+b a=b b=c return print(flibsOne(n)) 作业
python大作业
12-21
上海交通大学程序设计思想与方法大作业啊。
Python数据分析大作业 4000+字 图文分析文档 销售分析 +完整python代码
07-16
根据某商场货物销售数据 利用python实现数据可视化 进行分析 代码2000+行 文档预览:https://blog.csdn.net/weixin_45741872/article/details/118787250
python大作业 五子棋 人人对战
12-17
python编写的五子棋人人对战。某学校的python课程大作业,需要用到graphics.py库(这里免积分下载:http://download.csdn.net/detail/w1135181854u/6730647),把graphics.py 放进python安装文件夹的lib文件夹里就好了。
python总练习
starmultiple的博客
05-22 991
1、提示用户从键盘上输入一个有效的年份,在屏幕上打印这个年份是否为闰年。 注:凡符合下面两个条件之一的年份是闰年。 (1)能被4整除但不能被100整除。 (2)能被400整除。 i=int(input('请输入一个有效年份')) if i%4==0& i%100 !=0&i%400==0: print(i,'年为闰年') else : print(i,'年不是闰年') 2、从键盘输入三个数,输出其最大者。 a=int(input('请输入第一个数')) b=
2020年Python期末大作业:编程与绘图实战
这个作业旨在检验学生对Python语言的基础掌握程度,包括输入处理、数据结构操作、异常处理以及图形绘制能力,同时也考察了他们的逻辑思维、问题解决和文档写作技巧。通过完成这些任务,学生能够巩固和提高他们在...
写文章

热门文章

  • python程序编程代码大全,python编程代码详解 48605
  • python小游戏代码200行左右,python编程小游戏代码 18169
  • 一朵玫瑰花的python代码,python玫瑰花代码讲解 15951
  • 论文重复引用同一参考文献吗【详细教程】 11746
  • 怎么下载python并安装3.9.0,python下载安装教程3.10.0 8979

分类专栏

  • 智能写作 21篇
  • AIGC 8篇
  • 人工智能 5篇
  • 论文降重 2篇
  • 软件 2篇
  • 神码ai 1篇
  • rfid 1篇
  • Python 9篇
  • 搜索引擎 1篇
  • ChatGPT 3篇

最新评论

  • js如何调用api接口数据,javascript调用api接口

    Ivy@HPC: 文章详细介绍了JS调用API接口的方法,步骤清晰,代码示例丰富,对初学者非常友好,实操性强。注意API的安全性和数据处理,是学习JS网络请求的好资料。

  • 笨办法学Python3 百度网盘,笨办法学python3进阶篇pdf

    xl_stone: 标题党

  • python节日祝福源代码,用python绘制祝福语

    Cikl1231: 为啥我弹不出窗口

  • 高中信息技术会考python操作题解题技巧

    bh6xqtd: 三月七好评

  • js如何调用api接口数据,javascript调用api接口

    M694: 谢谢大佬,收获很大

大家在看

  • 1034:计算三角形面积
  • 游戏服务器遇到攻击怎么办?什么是游戏盾? 755
  • 网络钓鱼!如何识别和防范这一隐秘威胁?
  • 2024.9最新:CUDA安装,pytorch库安装 1652
  • 【最新原创毕设】基于SpringBoot的乐山市景区旅游攻略网+49117(免费领源码)可做计算机毕业设计JAVA、PHP、爬虫、APP、小程序、C#、C++、python、数据可视化、大数据、文案 2102

最新文章

  • python数据分析 期末测验,python数据分析基础题库
  • 基于python的图表生成系统,python导入数据生成图表
  • python开源的etl工具,eclat算法python
2024
04月 4篇
03月 230篇
02月 191篇
01月 389篇
2023年1381篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值

玻璃钢生产厂家做玻璃钢雕塑排行榜玻璃钢铜像雕塑艺术玻璃钢雕塑一般多少钱宿迁玻璃钢雕塑厂家加工杭州河源玻璃钢动物雕塑商场美陈清洁注意事项福清玻璃钢雕塑哪里好防城港玻璃钢泡沫雕塑厂家玻璃钢龙虾雕塑价格玻璃钢动物马雕塑公司冀州玻璃钢长颈雕塑知名的玻璃钢花盆江苏商场主题创意商业美陈制作河北玻璃钢南瓜屋雕塑新疆玻璃钢雕塑制作金华玻璃钢广场雕塑厂家浙江商场主题创意商业美陈服务黔东南玻璃钢雕塑上饶玻璃钢头像雕塑景观园林玻璃钢雕塑厂家玻璃钢十二生肖头喷水雕塑汕尾玻璃钢雕塑制作厂家惠州玻璃钢雕塑款式齐全泸州玻璃钢雕塑制作厂家蚌埠景区玻璃钢雕塑市场上海商场创意商业美陈作品唐县玻璃钢铸铜雕塑武汉公园玻璃钢雕塑设计玻璃钢卡通动漫雕塑视频无锡商场商业美陈口碑推荐香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化