2021.11.8 python 爬虫教学B站爬取up全部视频弹幕,视频评论,视频信息代码讲解(最全、清晰易懂)

一.介绍

咳咳、、、这是我最近接到的一个单子,我心想干脆就放出来吧,网上几乎都是一些零零散散的教程,我来填补这类空缺!!!(啊!我被我这开源的精神给打动了,看完了点赞呗)

我们观察bilibili网站,看看我们要爬取的人,我这里选的是老番茄这位B站一哥。


 二.弹幕

通过视频,分析怎么获得Bv号

首先是弹幕这一块,我们打开“老番茄”的B站主页,如下图 所示: 第一点,我们首先要做的是把思路理清楚,先干什么,然后干什么,思路很重要!!!!

我们要爬取一个up主所有的弹幕,是不是要先知道他发不过哪些作品,作品的数量,作品的BV号,通过BV号我们可以确定一条视频,比如:

https://www.bilibili.com/video/BV1H4411q7Qhttps://www.bilibili.com/video/BV1H4411q7Qxhttps://www.bilibili.com/video/BV1H4411q7Q 这个老番茄的“【老番茄】烂俗笑话”这一条视频。

https://www.bilibili.com/video/BV1Nf4y177iX 这是老番茄的“【老番茄】史上最骚杀手(番外篇②)”

观察规律,video/后面的就是BV号,所以我们说可以通过BV号锁定到视频url。那好接下来就好办了。

问题:我们从哪里获得BV号呢?

观察网站,作为一位爬虫工程师,你需要的就是观察网站,从网站下手。

https://space.bilibili.com/546195/video 这是老番茄的个人主页的投稿视频

我们可以看到有很多的视频url(地址),我们打开抓包工具(F12),看看这些视频是通过哪些api得到的。

 

 

 

 我们发现网站是通过ajax来实现网站对数据的加载的,有同学会问,什么是ajax?我举一个很通俗易懂的例子给你听吧。就是网站不用换地址的情况下实现的数据加载,比如翻页,源地址并没有变,变的是内容。

 

 

 找到json数据,看看api是什么

https://api.bilibili.com/x/space/arc/search?mid=546195&ps=30&tid=0&pn=1&keyword=&order=pubdate&jsonp=jsonp

但是我们打不开是怎么回事?因为我们在博客园里打开肯定不行的!!!我们把它复制粘贴下来,到浏览器里打开。

 

 

我们观察api参数。

 

 

 参数有以下几种,观察一组肯定发现不了什么,我们抓取下一页的json数据进行对比

 

 

 

 我们观察看参数的变化,现在只有pn是变的,pn应该是页数,不确定?我们抓一下第三页的json数据

 

 

果然!!!!那么mid是什么呢?好眼熟哦。没错!他就是我们用户的id!打开老番茄主页,

 uid的值就是json参数里的mid!!!

那么确定他有多少页呢?看投稿页面啊呀,我的傻宝。

 

 

 

现在我们已经获取的参数信息,现在开始写代码。

 


获得BV号,作者名字,保存到电子表格中的代码部分

具体json分析我这里不浪费口舌了,能看到我的博文说明还是有点水平的

我们下面的代码中有一个名叫xlsxwriter的包,具体是什么用法我发几张图片给大家领悟一下,实在不懂的评论区dd我。

import xlsxwriter as xw
def xw_toExcel(data):  # xlsxwriter库储存数据到excel
    workbook = xw.Workbook('演示数据.xlsx')  # 创建工作簿
    worksheet1 = workbook.add_worksheet("sheet1")  # 创建子表
    worksheet1.activate()  # 激活表
    title = ['第一标题','第二标题']  # 设置表头
    worksheet1.write_row('A1', title)  # 从A1单元格开始写入表头
    i = 2  # 从第二行开始写入数据
    for j in range(len(data)):
        insertData = [data[j]["第一标题"],data[j]["第二标题"]]
        row = 'A' + str(i)
        worksheet1.write_row(row, insertData)
        i += 1
    workbook.close()  # 关闭表

data = [
    {'第一标题':'我从哪里来?','第二标题':'我诞生于星河'},
    {'第一标题':'人为什么会思考?','第二标题':'呃。。。。。'},
    {'第一标题':'巴拉巴拉巴拉?','第二标题':'啊吧啊吧啊吧'}
]

xw_toExcel(data)

结果就是这样: 

好了,是不是清晰易懂,实在不懂就把函数照套就行了。

然后就是总代码了。

import requests
import json
from time import sleep
import xlsxwriter as xw
#导入我们要的包
#现在进行headers封装
headers = {
    'user-agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36'
}

def get_video(mid,page): #定义一个函数 mid参数就是up主的uid,page参数就是页数
    all_Bv_list = []   #定义一个列表,把所有的url装进列表
    for i in range(1,page+1): #for循环主要是用来确定页数
        url = 'https://api.bilibili.com/x/space/arc/search?mid=' + str(mid) + '&ps=30&tid=0&pn=' + str(i) + '&keyword=&order=pubdate&jsonp=jsonp' 
           #我们用字符串拼接来组成url
        Session = requests.session()#下面进行简单的初始化
        sleep(1)
        json_data = Session.get(url,headers=headers).content#得到二进制数据
        json_data = str(json_data,'utf8') #utf-8格式对二进制数据分析
        json_data = json.loads(json_data)#封装json
        json_data = json_data['data']['list']['vlist']#获取json对应数据
        for BV in json_data:#遍历对应数据
            BV = BV['bvid']#得到BV号
            video_url = 'https://www.bilibili.com/video/' + BV #拼接
            all_Bv_list.append(video_url)# 添加列表
    return all_Bv_list #返回值

def get_video_title(mid,page):
    all_title = []
    for i in range(1, page + 1):
        url = 'https://api.bilibili.com/x/space/arc/search?mid=' + str(mid) + '&ps=30&tid=0&pn='+str(i)+'&keyword=&order=pubdate&jsonp=jsonp'
        Session_1 = requests.session()
        json_data = Session_1.get(url, headers=headers).content
        json_data = str(json_data, 'utf8')
        json_data = json.loads(json_data)
        json_data= json_data['data']['list']['vlist']
        for BV in json_data:
            title = BV['title']
            all_title.append(title)
    return all_title #跟上面一样,只不过我们要视频标题作为文件名


def get_video_up(mid):
    url = 'https://api.bilibili.com/x/space/arc/search?mid=' + str(mid) + '&ps=30&tid=0&pn=1&keyword=&order=pubdate&jsonp=jsonp'
    Session_1 = requests.session()
    json_data = Session_1.get(url,headers=headers).content
    json_data = str(json_data,'utf8')
    json_data = json.loads(json_data)
    json_data_author = json_data['data']['list']['vlist'][0]['author']
    return json_data_author #获得up主名字

def xw_toExcel(data):  # xlsxwriter库储存数据到excel
    workbook = xw.Workbook('弹幕数据.xlsx')  # 创建工作簿
    worksheet1 = workbook.add_worksheet("sheet1")  # 创建子表
    worksheet1.activate()  # 激活表
    title = ['up主','平台','视频标题','弹幕内容','第一参数','第二参数','第三参数','第四参数','第五参数','第六参数','第七参数','第八参数']  # 设置表头
    worksheet1.write_row('A1', title)  # 从A1单元格开始写入表头
    i = 2  # 从第二行开始写入数据
    for j in range(len(data)):
        insertData = [data[j]["up主"],data[j]["平台"],data[j]['视频标题'],data[j]['弹幕内容'],data[j]['第一参数'],data[j]['第二参数'],data[j]['第三参数'],data[j]['第四参数'],data[j]['第五参数'],data[j]['第六参数'],data[j]['第七参数'],data[j]['第八参数']]
        row = 'A' + str(i)
        worksheet1.write_row(row, insertData)
        i += 1
    workbook.close()  # 关闭表

遍历视频列表,获得弹幕

得到视频地址后我们就可以对每个视频url发起一个请求,看看每个网页中的弹幕是通过哪些api请求到的。

可是我对每个视频的url进行抓包,并没有得到对应的api,这是为什么呢?

随后我去了百度,发现了视频中的弹幕都是加载好的,然后放进视频里的,我想也对,要是不这么做的话,不得卡死。上网搜索的到官方的弹幕地址,

https://comment.bilibili.com/'+str(cid)+'.xml 类似

 那些数字的意思就是:

 我们给他爬下来

cid是通过视频网页源码获取的,我们可以用正则提取,response是指网页源码.

cid = re.findall("\"cid\":([0-9]*),", response)[0]

得到cid后我们就可以进行写代码了,我们把上面的代码写入一个py文件里,通过导入的方法调用函数

import requests
import time
import re
from lxml import etree
#导包
import all_url_list
mid = int(input('id:'))
page = int(input('页数:'))
urls = all_url_list.get_video(mid,page)
title = all_url_list.get_video_title(mid,page)
author = all_url_list.get_video_up(mid)
#调用方法
headers = {
    'user-agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36'
}
i = 0
Session = requests.session()
#封装
TextData = []
for url in urls:
    times = 0
    response = Session.get(url,headers=headers).text
    cid = re.findall("\"cid\":([0-9]*),", response)[0] #获得cid
    dm_url = 'https://comment.bilibili.com/'+str(cid)+'.xml'
    response_1 = Session.get(dm_url,headers=headers)
    xml = etree.fromstring(response_1.content)
    all_dm = xml.xpath("/i/d/text()")
    p = xml.xpath("/i/d/@p")
    # print(p[0].split(','))
    # break
    for dm in all_dm:#添加数据
        numbers = p[times].split(',')
        Data = {
            'up主':'None','平台':'None','视频标题':'None','弹幕内容':'None','第一参数':'None','第二参数':'None','第三参数':'None','第四参数':'None','第五参数':'None','第六参数':'None','第七参数':'None','第八参数':'None',
        }
        Data['up主'] = author
        Data['平台'] = 'B站'
        Data['视频标题'] = title[i]
        Data['弹幕内容'] = dm
        Data['第一参数'] = numbers[0]
        Data['第二参数'] = numbers[1]
        Data['第三参数'] = numbers[2]
        Data['第四参数'] = numbers[3]
        Data['第五参数'] = numbers[4]
        Data['第六参数'] = numbers[5]
        Data['第七参数'] = numbers[6]
        Data['第八参数'] = numbers[7]
        times += 1
        TextData.append(Data)    #循环写入,懂我意思吧
    i += 1
    print('第%d,ok!'% i )
all_url_list.xw_toExcel(data=TextData)#写入
print('全部视频数据下载完成')

 我们看看效果,因为我之前爬的是李子柒的,我就不浪费时间爬了

 看到有50w的弹幕,真可怕!

 二,爬取视频信息和爬取视频评论

好了,写下来就是我们的主菜啦!!!!

首先分析,没错还是分析分析,想清楚自己要从哪一步入手?

是不是从网站入手啊,然后打开我们的视频链接史上最离谱随机挑战!我们终于去老番茄家蹭饭了!!【第七期】_哔哩哔哩_bilibili

找到评论,看看是那个api请求到的。打开开发者工具,按照上面的操作步骤

​ 我们成功在js里找到我们对应的js数据,我们看看api地址是什么

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

 为什么还是访问失败,我们把一些参数去掉,因为我们不需要参数进行js处理。

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

可以发现我们访问成功了

接下来我们看看参数是什么 意思,

next应该是下一页的意思

那oid是什么呢?在哪里获取呢,我这里直接告诉大家,oid就是网站的aid,我们可以用re来直接获取。跟上面cid一样的

我们拿到json数据后进行解析,解析步骤我也不讲了,都应该懂吧。

下面导入全部代码,让大家一次看过瘾

import json
import requests
from lxml import etree
from time import sleep
import re
import xlsxwriter as xw
import random
from all_url_list import get_video,get_video_up
#还是导入我们的模块包
USER_AGENTS = [
        "Mozilla/5.0 (Windows; U; Windows NT 5.2) Gecko/2008070208 Firefox/3.0.1",
        "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
        "Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
        "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11",
        "Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
        "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)",
        "Opera/9.80 (Windows NT 5.1; U; zh-cn) Presto/2.9.168 Version/11.50",
        "Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0",
        "Mozilla/5.0 (Windows NT 5.2) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.122 Safari/534.30",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER",
        "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; LBBROWSER)",
        "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; 360SE)",
        "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 SE 2.X MetaSr 1.0",
        "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0)",
        "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2)",
        "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)",
        "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT)",
        "Mozilla/5.0 (Windows; U; Windows NT 5.2) Gecko/2008070208 Firefox/3.0.1",
        "Mozilla/5.0 (Windows; U; Windows NT 5.1) Gecko/20070309 Firefox/2.0.0.3",
        "Mozilla/5.0 (Windows; U; Windows NT 5.1) Gecko/20070803 Firefox/1.5.0.12 "
        ]
#进行UA伪装
mid = int(input('输入用户的UID账号:'))
page = int(input('用户一共有几页:'))
urls = get_video(mid,page) #获取视频一共的页数,跟爬取弹幕是一样的,因为导入的是同一个包
author = get_video_up(mid=mid)
TextData_vedio = []
TextData_comment = [] #定义两个列表来存视频信息和视频评论
#这是视频评论的
def xw_toExcel_comment(data):  # xlsxwriter库储存数据到excel
    workbook = xw.Workbook('总评论数据.xlsx')  # 创建工作簿
    worksheet1 = workbook.add_worksheet("sheet1")  # 创建子表
    worksheet1.activate()  # 激活表
    title = ['up主','平台','视频标题','用户','id','等级','性别','签名','评论']  # 设置表头
    worksheet1.write_row('A1', title)  # 从A1单元格开始写入表头
    i = 2  # 从第二行开始写入数据
    for j in range(len(data)):
        insertData = [data[j]["up主"],data[j]["平台"],data[j]['视频标题'], data[j]["用户"],data[j]['id'] ,data[j]["等级"],data[j]["性别"],data[j]["签名"],data[j]['评论']]
        row = 'A' + str(i)
        worksheet1.write_row(row, insertData)
        i += 1
    workbook.close()  # 关闭表
#这是视频信息的
def xw_toExcel_vedio(data):  # xlsxwriter库储存数据到excel
    workbook = xw.Workbook('总数据.xlsx')  # 创建工作簿
    worksheet1 = workbook.add_worksheet("sheet1")  # 创建子表
    worksheet1.activate()  # 激活表
    title = ['平台','视频标题','视频播放量','弹幕量','发布时间','点赞数量','硬币数量','收藏','转发']  # 设置表头
    worksheet1.write_row('A1', title)  # 从A1单元格开始写入表头
    i = 2  # 从第二行开始写入数据
    for j in range(len(data)):
        insertData = [data[j]["平台"],data[j]["视频标题"], data[j]["视频播放量"], data[j]["弹幕量"],data[j]["发布时间"],data[j]["点赞数目"],data[j]["硬币数量"],data[j]["收藏"],data[j]['转发']]
        row = 'A' + str(i)
        worksheet1.write_row(row, insertData)
        i += 1
    workbook.close()  # 关闭表
# 设置headers
headers = {
    'user-agent' : random.choice(USER_AGENTS),
}
Session = requests.session() #实例化对象
video_number = 0 #设置一个提醒操作,主要用来知道爬取到哪里了 
try:
    for url in urls: #将上面的url列表进行遍历
        number = 1 #申明
        #下面都属于解析
        response = Session.get(url=url,headers=headers).text #因为只有视频评论是动态加载的,所以这里我们可以直接爬取网页源代码
        tree = etree.HTML(response) 
        title = tree.xpath('//*[@id="viewbox_report"]/h1/@title')[0] #视频标题
        video_playback_volume = tree.xpath('//*[@id="viewbox_report"]/div/span[1]/text()')[0]#视频播放量
        Number_of_video_barrages = tree.xpath('//*[@id="viewbox_report"]/div/span[2]/text()')[0]#弹幕量
        video_time = tree.xpath('//*[@id="viewbox_report"]/div/span[3]/text()')[0]#发布时间
        video_likes = tree.xpath('//*[@id="arc_toolbar_report"]/div[1]/span[1]/@title')[0] #点赞数量
        video_coin = tree.xpath('//*[@id="arc_toolbar_report"]/div[1]/span[2]/text()')[0] #硬币数量
        video_collect = tree.xpath('//*[@id="arc_toolbar_report"]/div[1]/span[3]/text()')[0] #收藏
        video_forward = tree.xpath('//*[@id="arc_toolbar_report"]/div[1]/span[4]/text()')[0] #转发
        # #以上获得了视频信息的内容
        oid = re.findall("\"aid\":([0-9]*),", response)[0] #获得oid
        #定义一个字典
        vedio_Data = {
            '平台':'None','视频标题':'None', '视频播放量': 'None', '弹幕量':'None', '发布时间': 'None', '点赞数目': 'None', '硬币数量': 'None', '收藏': 'None', '转发': 'None'
        }
        vedio_Data['平台'] = 'B站'
        vedio_Data['视频标题'] = str(title)
        vedio_Data['视频播放量'] = str(video_playback_volume)
        vedio_Data['弹幕量'] = str(Number_of_video_barrages)
        vedio_Data['发布时间'] = str(video_time)
        vedio_Data['点赞数目'] = str(video_likes)
        vedio_Data['硬币数量'] = str(video_coin)
        vedio_Data['收藏'] = str(video_collect)
        vedio_Data['转发'] = str(video_forward)
        TextData_vedio.append(vedio_Data)#添加到列表
        #接下来一直爬视频评论了
        while True:
            comment_url = 'https://api.bilibili.com/x/v2/reply/main?jsonp=jsonp&next='+str(number)+'&type=1&oid='+str(oid)+'&mode=3&plat=1'
            number += 1
            json_data = Session.get(url=comment_url,headers=headers).content
            sleep(1)
            json_data = json.loads(json_data) #获得字典数据
            #下面进行数据分析
            replien = json_data['data']['replies']
            print('第%d页'%number)
            if replien != None: #因为当字典里replien等于Null时,我们的评论就爬完了,所以我们当replien不等于None是进行数据的持久化存储
                for data in replien:
                    data_comment = data['content']['message'] #评论内容
                    data_id = data['mid'] #用户的id
                    data_level = data['member']['level_info']['current_level']#等级
                    data_name = data['member']['uname'] #用户的名字
                    data_sex = data['member']['sex'] #用户的性别
                    data_sign = data['member']['sign']#用户的个性签名
                    # data_like = data['like']#评论点赞数
                    Data = {
                        'up主':'None', '平台':'None', '视频标题':'None', '用户':'None', 'id':'None','等级':'None', '性别':'None', '签名':'None', '评论':'None'
                    }
                    Data['up主'] = author
                    Data['平台'] = 'B站'
                    Data['视频标题'] = title
                    Data['用户'] = data_name
                    Data['id'] = data_id
                    Data['等级'] = data_level
                    Data['性别'] = data_sex
                    Data['签名'] = data_sign
                    Data['评论'] = data_comment
                    TextData_comment.append(Data)
            else: #当为None时我们就返回爬完了,爬取下一个视频
                video_number = video_number + 1
                print('第%d个视频爬取完毕'% video_number)
                break
#接下来就是调用函数保存啦!!
except:
    xw_toExcel_comment(TextData_comment)
    xw_toExcel_vedio(TextData_vedio) 
else:
    xw_toExcel_comment(TextData_comment)
    xw_toExcel_vedio(TextData_vedio)

接下来看看成果,因为我爬取得还是李子柒哈哈哈,单纯的懒因为是爬好的,效果

都差不多。 

接下来是评论信息,我这里是滇西小哥的

以上就是代码的全部了,希望你能喜欢! 

代码的使用

我们双击弹幕.py

输入up主的UID,和一共有多少页

好了,看看视频怎么使用

​ 也是和弹幕一样的使用流程,你学废了吗???

看完点个赞呗!!!

注意!!!本文章没有我的允许,禁止转载,如果被我找到,我会拿起法律武器进行维权!!!

注意!!!本文章没有我的允许,禁止转载,如果被我找到,我会拿起法律武器进行维权!!!

注意!!!本文章没有我的允许,禁止转载,如果被我找到,我会拿起法律武器进行维权!!!

注意!!!本文章没有我的允许,禁止转载,如果被我找到,我会拿起法律武器进行维权!!!

注意!!!本文章没有我的允许,禁止转载,如果被我找到,我会拿起法律武器进行维权!!!

注意!!!本文章没有我的允许,禁止转载,如果被我找到,我会拿起法律武器进行维权!!!

注意!!!本文章没有我的允许,禁止转载,如果被我找到,我会拿起法律武器进行维权!!!

posted @ 2021-11-08 22:27  lambda!!!  阅读(688)  评论(2)    收藏  举报