[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站的数据就爬到这了,差不多可以爬的我们都爬了一遍,其他的其实都大同小异,只要知道一些简单的用法,其他的其实都大同小异。