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 ='<span class="tit">(.*?)</span>'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 ='<span class="tit">(.*?)</span>'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貌似没啥用,待测试)。

敬请指教与交流!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,976评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,249评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,449评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,433评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,460评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,132评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,721评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,641评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,180评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,267评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,408评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,076评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,767评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,255评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,386评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,764评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,413评论 2 358

推荐阅读更多精彩内容