python爬取B站视频_分享;学习

记录学习历程~

欢迎广大道友指导与交流~


本文仅供学习、交流等非商业性用途使用。

转载请声明附带链接:https://blog.csdn.net/Ws_Zhou_Xu/article/details/119709502

B站视频自某年以后其缓存的视频皆为分段分开的,比较不方便。因此个人想着用Python将其更加自动化。

既然视频和音频分开了,那么必然需要先请求获取视频和音频的url再通过各自的url分别获取各自的内容。最后再借助moviepy将视频和音频合成。

关于批量处理爬取,一开始想得过于复杂(当然,相较于其他更快的方式,基于此方式来说是复杂的),导致连连异常。经过一觉,发现豁然开朗。复杂点在于当找到位于Network中的关键name之后,想着更加自动化的获取通用ID走了不少弯路。后面才发现通用ID就在网址中。

大体思路如下:在进入喜欢UP主的空间后,点击播放全部(图1),

图1

 →进入新页面,发现网址(图2),并没有附着视频BV,因此需要换一种方式思考。

图2

→右键检查页面,在Network栏下发现了特殊name(图3),里面包含了Up主其他的视频(图4,5)。

图3

                               图4                                                              图5

 →思路清晰起来了,我们可以通过请求图2页面,找到图3所示信息群,再请求该URL获取json数据迭代索引获取各bv_id。

代码及分析如下:

①获取bv_id并导入相关

importrequestsimportreimportosfrommoviepy.editorimportVideoFileClip, AudioFileClipfromlxmlimportetreeimportjsonimporttime# 构建bv的headersheaders_bv = {'User-Agent':'XXX'}# 此处为爬取视频的Url所需params不同于获取bv的Urldata = {'accept_description':'高清 1080P+','accept_quality':112}# 专属bvUrlget_bvUrl ='https://api.bilibili.com/x/v2/medialist/resource/list?type=1&otype=2&biz_id='+ \            input('请输入您要的UP主的url的id:') +'&bvid=&with_current=true&mobi_app=web&ps=20&'\'direction=false&sort_field=1&tid=0&desc=true'r = requests.get(get_bvUrl, headers=headers_bv)# 获取bv列表bv_list = []bv = json.loads(r.content)# 将获取到的内容json化foriinrange(20):    bvID = bv['data']['media_list'][i]['bv_id']# 根据preview显示数据索引bv_list.append(bvID)print(bv_list)downloadCount =0# 此处计数是控制下载视频数量

 说明:User-Agent获取在网页Headers部,可自行获取键入。

②构建循环附带跳出条件

if__name__ =='__main__':whileTrue:ifdownloadCount <20:# 此处的url和headers专门用于请求视频数据不同于请求bv_idurl ='https://www.bilibili.com/video/'+ bv_list[downloadCount]            headers = {'Referer':'https://www.bilibili.com/video/'+ str(bv_list[downloadCount]),'User-Agent':'XXX'}            biliSpider = bilibiliSpider()# 此处见下文biliSpider.run()            downloadCount +=1# 运行一次加1,到20就跳出结束下载视频time.sleep(6)else:break

 ③构建bilibiliSpider()类

1.主体框架

classbilibiliSpider():defsend_requests(self, url):# 发出请求通用格式(方便defget_data(self, webData):# 获取标题,JSON数据,视频和音频Urldefsave_data(self, fileName, videoUrl, audioUrl):# 请求视频和音频Url并获取内容存储defmerge_data(self, videoName):# 利用moviepy将视频和音频合成defrun(self):# 调用各方法

 说明:此处的合成视频和音频方法是调整过的,起初使用ffmpeg发现不太好使,经过交流群前辈指导改用moivepy,较方便且好使。

2.发出通用请求格式(方便

defsend_requests(self, url):self.url = urlglobalres        res = requests.get(url, headers=headers, params=data)returnres

 说明:global意味在于其他方法处需要请求内容。

3.请求获取标题,JSON数据,视频和音频Url

defget_data(self, webData):self.webData = webData# 提取标题pattern ='(.*?)'title = re.findall(pattern, res.text, re.S)[0]# 提取json数据_element = etree.HTML(res.content)        jsonData = str(_element.xpath('//head/script[5]/text()')[0].encode('utf-8').decode('utf-8'))[20:]        jsonData = json.loads(jsonData)# 提取视频的地址videoUrl = jsonData['data']['dash']['video'][0]['backupUrl'][0]        print('提取到视频:', videoUrl)        audioUrl = jsonData['data']['dash']['audio'][0]['backupUrl'][0]        print('提取到音频:', audioUrl)        videoData = [title, videoUrl, audioUrl]returnvideoData

说明:此处的title索引、jsonData索引、videoUrl索引、audioUrl索引和上文的Referer等信息皆可在图中找到,获取大概流程皆为右键检查页面获取信息(图6,7,8)。

图6

 图7

图8

4.保存数据

defsave_data(self, fileName, videoUrl, audioUrl):self.fileName = fileName        self.videoUrl = videoUrl        self.audioUrl = audioUrl# 发出请求,获取内容get_videoData = self.send_requests(videoUrl).content        get_audioData = self.send_requests(audioUrl).content# 存储数据withopen(str(fileName) +'.mp4','wb')asf:            f.write(get_videoData)withopen(str(fileName) +'.mp3','wb')asf:            f.write(get_audioData)

 5.合成视频和音频

defmerge_data(self, videoName):self.videoName = videoName        print('开始合成', videoName)# 设置路径path1 =r'D:\result'+f'\{videoName}'+'.mp4'path2 =r'D:\result'+f'\{videoName}'+'.mp3'video_path = path1# 获取视频切片get_video = VideoFileClip(video_path)# 切片+切片video_audio_combine = get_video.set_audio(AudioFileClip(path2))        video_audio_combine.write_videofile(f'{videoName}+'+'.mp4', audio_codec='aac')        print('合成完毕', videoName)ifos.path.exists(path1):            os.remove(path1)else:passifos.path.exists(path2):            os.remove(path2)else:pass

 说明:路径一定要对,不然找不到会报错(可以自己设置路径,前后统一即可)。后面条件语句那开始是清楚之前单独的视频和音频,合成后的名字多了加号,以防被系统按照单独的视频给误删。

起初使用ffmpeg合成视频和音频并不顺利(图9),因此改用moviepy。

图9

6.调用方法

    def run(self):

        webData = self.send_requests(url)

        videoData = self.get_data(webData)

        self.save_data(videoData[0], videoData[1], videoData[2])

        self.merge_data(videoData[0])

        # 这也就是为什么当初构造videoData列表并return了

 说明:全文尽量统一

全文代码如下:

importrequestsimportreimportosfrommoviepy.editorimportVideoFileClip, AudioFileClipfromlxmlimportetreeimportjsonimporttimeos.chdir(r'D:\result')headers_bv = {'User-Agent':'XXX'}data = {'accept_description':'高清 1080P+','accept_quality':112}get_bvUrl ='https://api.bilibili.com/x/v2/medialist/resource/list?type=1&otype=2&biz_id='+ \            input('请输入您要的UP主的url的id:') +'&bvid=&with_current=true&mobi_app=web&ps=20&'\'direction=false&sort_field=1&tid=0&desc=true'r = requests.get(get_bvUrl, headers=headers_bv)# 获取bv列表bv_list = []bv = json.loads(r.content)foriinrange(20):    bvID = bv['data']['media_list'][i]['bv_id']    bv_list.append(bvID)print(bv_list)downloadCount =0classbilibiliSpider():defsend_requests(self, url):self.url = urlglobalres        res = requests.get(url, headers=headers, params=data)returnresdefget_data(self, webData):self.webData = webData# 提取标题pattern ='(.*?)'title = re.findall(pattern, res.text, re.S)[0]# 提取json数据_element = etree.HTML(res.content)        jsonData = str(_element.xpath('//head/script[5]/text()')[0].encode('utf-8').decode('utf-8'))[20:]        jsonData = json.loads(jsonData)# 提取视频的地址videoUrl = jsonData['data']['dash']['video'][0]['backupUrl'][0]        print('提取到视频:', videoUrl)        audioUrl = jsonData['data']['dash']['audio'][0]['backupUrl'][0]        print('提取到音频:', audioUrl)        videoData = [title, videoUrl, audioUrl]returnvideoDatadefsave_data(self, fileName, videoUrl, audioUrl):self.fileName = fileName        self.videoUrl = videoUrl        self.audioUrl = audioUrl# 发出请求,获取内容get_videoData = self.send_requests(videoUrl).content        get_audioData = self.send_requests(audioUrl).content# 存储数据withopen(str(fileName) +'.mp4','wb')asf:            f.write(get_videoData)withopen(str(fileName) +'.mp3','wb')asf:            f.write(get_audioData)defmerge_data(self, videoName):self.videoName = videoName        print('开始合成', videoName)# 设置路径path1 =r'D:\result'+f'\{videoName}'+'.mp4'path2 =r'D:\result'+f'\{videoName}'+'.mp3'video_path = path1# 获取视频切片get_video = VideoFileClip(video_path)# 切片+切片video_audio_combine = get_video.set_audio(AudioFileClip(path2))        video_audio_combine.write_videofile(f'{videoName}+'+'.mp4', audio_codec='aac')        print('合成完毕', videoName)ifos.path.exists(path1):            os.remove(path1)else:passifos.path.exists(path2):            os.remove(path2)else:passdefrun(self):webData = self.send_requests(url)        videoData = self.get_data(webData)        self.save_data(videoData[0], videoData[1], videoData[2])        self.merge_data(videoData[0])if__name__ =='__main__':whileTrue:ifdownloadCount <20:            url ='https://www.bilibili.com/video/'+ bv_list[downloadCount]            headers = {'Referer':'https://www.bilibili.com/video/'+ str(bv_list[downloadCount]),'User-Agent':'XXX'}            biliSpider = bilibiliSpider()            biliSpider.run()            downloadCount +=1time.sleep(6)else:break

试运行:

 综上,还有提升空间,比如下载速度太慢。然后是画质的选择,爬的时候默认是最高的,具体自定义画质还在研究(即文中的params貌似没啥用,待测试)。

敬请指教与交流!

你可能感兴趣的:(python爬取B站视频_分享;学习)