Python爬虫3:爬B站各种信息


[success]前面都是挺短的,想必大家都看的不过瘾吧?那么下面我们就让我们的爬虫前往我们的小破站,去看一下可以爬到什么吧![/success]

爬视频

我们先来爬一下B站的视频,刚开始我以为视频可能被藏在json数据里面,不过我找了半天也没有找到数据,后来我直接查看网页的源代码,没想到居然就在源代码里面,所以我们直接获取网页源代码,然后提取就行了。

不过如果直接获取是不能获取的,B站也有反爬机制,不过想绕过也很简单,伪装一下自己的header数据就行了。还有下载的时候我们需要修改一下refer请求,要不然会禁止访问,当然我们下的其实是m4s文件,这文件的视频和音频是分开的,所以我们需要自己后期把视频和音频文件合并即可。

那么废话不多说,直接贴代码:

#-*- coding:UTF-8 -*-
import requests,json
from urllib import parse
from bs4 import BeautifulSoup
from contextlib import closing
from multiprocessing import Process
class Bilibili(object):
    def __init__(self):
        self.target='https://www.bilibili.com/video/avreplace/'#视频地址
        self.av=''#av号
        self.video=[]#视频下载链接
        self.grade1=[]#视频画质等级
        self.grade2=[]#音频等级
        self.music=[]#音频地址
        self.name=''#视频名字
        self.headers={'Referer': 'https://www.bilibili.com/','User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}
        #上面是一些头部信息,用于伪装自己
    #获取视频的各种信息
    def getinfo(self):
        req=requests.get(url=self.target,headers=self.headers)
        bf=BeautifulSoup(req.text,'lxml')
        # self.name=bf.find_all('span',class_="tit")[0].text
        cont1=bf.find_all("script")#寻找我们需要的东西
        cont1=cont1[2].text.replace("window.__playinfo__=","")#把内容替换掉以便后面进行json数据解析
        cont1=json.loads(cont1)#json数据解析
        for each in cont1['data']['dash']['video']:#遍历json数据,获取视频下载地址
            self.grade1.append(each['id'])
            self.video.append(each['baseUrl'])
        for each in cont1['data']['dash']['audio']:#获取音频下载地址
            self.grade2.append(each['id'])
            self.music.append(each['baseUrl'])
    def start(self):
        while True:
            s=input("请输入AV号(只用输数字):")
            self.av=s
            self.target=self.target.replace('replace',s)
            req=requests.get(url=self.target,headers=self.headers)
            #可以根据返回的内容判断视频是否存在
            if req.text.find('视频去哪了呢?_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili')!=-1 or  req.text.find(' 404  
')!=-1: print ("视频不存在!") else: break self.getinfo() bf=BeautifulSoup(req.text,'lxml') #对数据进行解析,获取视频名称 self.name=bf.find_all('span',class_="tit")[0].text print("视频名称:%s" %self.name) grade=[] for i in range(len(self.grade1)): grade.append(str(i)+':'+str(self.grade1[i])) a=input("请选择下载画质:%s(数字越高越清晰,只需要输入序号)" %grade) grade.clear() for i in range(len(self.grade2)): grade.append(str(i)+':'+str(self.grade2[i])) b=input("请选择下载音质:%s(数字越高越音质好,只需要输入序号)" %grade) #下面是多线程的代码,一个线程下视频,一个下音频 p1 = Process(target=self.down,args=(a,1)) p1.start() p2 = Process(target=self.down,args=(b,2)) p2.start() p1.join() p2.join() print ("下载成功!") def down(self,v,a): #判断下载的内容 if a==1: av=self.av+'.mp4' target=self.video[int(v)] print ("正在下载视频.....") else: av=self.av+'.mp3' target = self.music[int(v)] print ("正在下载音频......") #下面是下载视频和音频的代码 with closing(requests.get(url=target, stream=True, headers=self.headers)) as r: # 这里就是下载图片的代码了 with open(av, 'ab+') as f: # 这里是保存图片 for chunk in r.iter_content(chunk_size=1024): # 下面都是下载图片数据的代码,所以不解释 if chunk: f.write(chunk) f.flush() if __name__=="__main__": bi=Bilibili() bi.start()

这里时间有限我就不演示效果了,反正我测试是没有问题的。

爬评论

一个视频下面有许多评论,那么我们应该怎么获取呢?首先我们看网页发现这个是没有的,所以就只好抓包了,然后找了半天发现他返回的是json数据,而且就是一个api地址,所以我们只需要发请求过去就行了。

这个是我获得的json数据,大家的只需要自己找一下就可以找的,还挺简单的,代码我也贴一下(这里我就不用面向对象写了,赶时间):

# -*- coding:UTF-8 -*-
import requests,json
target='https://api.bilibili.com/x/v2/reply?callback=jQuery&jsonp=jsonp&pn=replace2&type=1&oid=replace1&sort=0'
headers = {'Referer':'ttps://www.bilibili.com/','User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}
s=input("请输入av号(输入数字即可):")
target1=target.replace('replace1',s).replace('replace2','1')#这里其实就是替换两个部分一个是id还有一个是评论的页数
req=requests.get(url=target1,headers=headers)
i=req.text.find('(')+1#这个是把返回的数据处理一下变成json数据
bf=req.text[i:-1]
req.encoding='utf-8'
hot=json.loads(bf)#这里就是转换数据
num=hot['data']['page']['count']#获取评论的数目
print("评论数:%s" %num)
saylist=[]
print("=====================热评=============================")
for each in hot['data']['hots']:
    print('<'+each['member']['uname']+">:"+each['content']['message'])
    print("-----------------------------------------")
print("=====================评论=============================")
for j in range(int(num/20+1)):#这里就是遍历发送请求获取所有的评论
    target1 = target.replace('replace1', s).replace('replace2',str(j))
    req = requests.get(url=target1, headers=headers)
    i = req.text.find('(') + 1
    bf = req.text[i:-1]
    req.encoding = 'utf-8'
    hot = json.loads(bf)
    for each in hot['data']['replies']:
        print('<' + each['member']['uname'] + ">:" + each['content']['message'])
        print("-----------------------------------------")
        if len(saylist)>=num:
            break

我们拿一个很热门的视频来做一下实验:这个大概有2万条评论吧,一直在输出,感觉挺牛逼的。。

爬弹幕

爬弹幕也很简单,其实就是一个文件,我们打开F12就可以看一些信息,我这里找了一下,很快就找到我们想找的东西了。

打开这个文件就可以看到弹幕了

不过这个我们需要获取oid,所以我们还是不能直接下载,需要先获取oid,这里我看了一下,oid其实就是cid,所以我们先根据av号在获取cid号,那直接上代码:

#coding=utf-8
import requests,json
from bs4 import BeautifulSoup
target='https://api.bilibili.com/x/web-interface/view?aid=replace'
headers = {'Referer':'ttps://www.bilibili.com/','User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}
s=input("请输入av号:")
target=target.replace('replace',s)#简单进行替换
req=requests.get(url=target,headers=headers)
js1=json.loads(req.text)
cid=js1['data']['cid']#获取cid号
target='https://api.bilibili.com/x/v1/dm/list.so?oid=replace'
target=target.replace('replace',str(cid))
req=requests.get(url=target,headers=headers)
req.encoding = 'utf-8'#对resopn进行编码转换(因为肯会获取到乱码数据)
bf=BeautifulSoup(req.text,'lxml')
con1=bf.find_all('d')#这里就是进行弹幕分割找到所有弹幕
for each in range(len(con1)):
    print(con1[each].string)

获取视频在线人数

这个既不是json也不能再源码里看到,那么它到底是怎么得到的呢?其实是通过websocket得到的,那么我们来看一下流程是什么,大家按下F12就可以看到有哪些请求,我们可以直接选择ws来查看。

可以看到其实只有两次通信,我们就直接上代码,其他的都在代码里面解释:

我们要用到websocket通信,所以需要安装响应的模块,只需要输入:pip install websocket_client就可以了

我们可以看到这个是我们需要发送的第一次信息,这里我搞了一晚上。。原因很简单,因为这个发送的是二进制数据,而我发送的又是数组,所以还需要自己修改数组的内容,进制转换之间用Python又不能很好的添加到数组中,所以我只好想别的方法,下面就直接贴代码吧:

import websocket,json,requests
target='https://api.bilibili.com/x/web-interface/view?aid=replace'
headers = {'Referer':'ttps://www.bilibili.com/','User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}
s=input("请输入av号:")
target=target.replace('replace',s)#简单进行替换
req=requests.get(url=target,headers=headers)
js1=json.loads(req.text)
cid=js1['data']['cid']#获取cid号
ws_url='wss://broadcast.chat.bilibili.com:7823/sub'#这个是B站的websocket地址
up1= [0x00, 0x00, 0x00, 0x5B, 0x00, 0x12, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x7B, 0x22, 0x72, 0x6F, 0x6F, 0x6D, 0x5F, 0x69, 0x64, 0x22, 0x3A, 0x22, 0x76, 0x69, 0x64, 0x65, 0x6F, 0x3A, 0x2F, 0x2F]
up2=[0x22, 0x2C, 0x22, 0x70, 0x6C, 0x61, 0x74, 0x66, 0x6F, 0x72, 0x6D, 0x22, 0x3A, 0x22, 0x77, 0x65, 0x62, 0x22, 0x2C, 0x22, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x73, 0x22, 0x3A, 0x5B, 0x31, 0x30, 0x30, 0x30, 0x5D, 0x7D ]
up3 =[0x00, 0x00, 0x00, 0x21, 0x00, 0x12, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x5B, 0x6F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x20, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x5D ]
for each in range(len(s)):
    up1.append(int(s[each])+48)#这里就是自己把字符转换成16进制
up1.append(0x2f)#这里是添加 ‘/’
for each in range(len(str(cid))):
    up1.append(int(str(cid)[each])+48)
for each in up2:
    up1.append(each)#这里是把代码进行合并,获得发送的内容
ws=websocket.create_connection(ws_url,timeout=10)#建立socket连接
ws.send(bytes(up1))#发送一次,接受一次,再发送一次,然后接受就可以得到我们需要的数据了
ws.recv()
ws.send(bytes(up3))
a=ws.recv().decode('utf-8')
js=json.loads(a[18:])#这里是把获取的内容变成json以便后面访问
print("当前在线:%s" %js['data']['room']['online'])

实际演示效果:

爬表情

这个其实就是在json数据里面,我们可以直接获得这个json数据,然后获取表情的地址,最后下载即可,这里直接贴代码:

#-*- coding:UTF-8 -*-
import requests,json,os
from urllib.request import urlretrieve
from threading import Thread
urls = []
names = []

#下载图像
def download(url,name):#这里就是下载图像的函数,配合多线程使用,使下载速度加倍
    print ('正在下载:%s' %name)
    if(not os.path.exists('img')):#这里是创建文件夹
        os.makedirs('img')
    name='img/'+str(name)+'.png'#这里就是换一个路径,下载到img下
    urlretrieve(url,name)#下载图像

def moredown():
    threads = []#这里我们使用多线程,要把这些线程放到列表里
    for i in range(len(urls)):#我们要下多少图片就开多少线程
        t =Thread(target=download(urls[i],names[i]))#把函数加到线程里面
        t.start()#开始线程
        threads.append(t)#把线程都加到列表里面,方便后面判断是否下载完毕
    for t in threads:
        t.join()#这里就是等待线程结束的代码
    print ("下载完成")

if __name__=="__main__":
    target = 'https://api.bilibili.com/x/v2/reply/v2/emojis?callback=emoji&jsonp=jsonp'
    headers = {'Referer': 'ttps://www.bilibili.com/',
               'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}
    req = requests.get(url=target, headers=headers)
    js = json.loads(req.text[6:-1])
    for each in js['data']['free'][0]['emojis']:
        urls.append(each['url'])
        names.append(each['id'])
    for each in js['data']['vip']:
        a = each['emojis']
        for i in a:
            urls.append(i['url'])
            names.append(i['id'])
    moredown()

实际效果:

然后我们可以看一下我们下载的文件:

总共下载了322个表情,看来B站所有表情包都被我爬过来了。

看我的新表情

爬个人信息

这个其实就有点难度了,因为要获取个人信息的话,你需要登录网站才行,这里我目前还没有学,所以就只能通过cookie数据来获取个人信息。

这里也直接贴代码:

# -*- coding:UTF-8 -*-
import requests,json,time
from urllib.request import urlretrieve
target1='https://api.bilibili.com/x/space/myinfo?jsonp=jsonp&callback=__jp0'
target2='https://api.bilibili.com/x/relation/tags?jsonp=jsonp&callback=__jp5'
headers={'Referer':'https://space.bilibili.com/343147393/fans/follow','User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36','Cookie':"你的cookie数据"}
req=requests.get(url=target1,headers=headers)
req.encoding=('utf-8')
js=json.loads(req.text[6:-1])
req=requests.get(url=target2,headers=headers)
req.encoding=('utf-8')
js2=json.loads(req.text[6:-1])
timeArray = time.localtime(js['data']['jointime'])
jointime=time.strftime("%Y-%m-%d %H:%M:%S", timeArray)
timeArray = time.localtime(js['data']['birthday'])
birthday=time.strftime("%m-%d", timeArray)
print("UID:%s" %js['data']['mid'])
print("昵称:%s" %js['data']['name'])
print("性别:%s" %js['data']['sex'])
print("首页壁纸:%s" %js['data']['face'])
print("等级:%s" %js['data']['level'])
print("注册时间:%s" %jointime)
print("生日:%s" %birthday)
print("当前经验:%s" %js['data']['level_exp']['current_exp'])
print("硬币数:%s" %js['data']['coins'])
print("粉丝数:%s" %js['data']['following'])
for each in js2['data']:
    print("我的关注<%s>:%s" %(each['name'],each['count']))

实际效果:

到这里B站的数据就爬到这了,差不多可以爬的我们都爬了一遍,其他的其实都大同小异,只要知道一些简单的用法,其他的其实都大同小异。


文章作者: 小游
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 小游 !
  目录