开发原因:

  • 一些脚本可以获得b站视频的下载链接,但下载时没有文件名;
  • 虽然有youtobe-dl,但下载速度较慢,所以想要去写一个自己的程序自己使用,目前脚本自己下载资料时一直使用,顺便解决一下问题。

说明:已更新于2021/5/4 可用

5/4 更新说明:解决了普通番剧下载出错的问题

4/27更新说明:不知道什么原因使用线程池ThreadPoolExecutor时,创建多个线程同时下载总是会出现卡在那里无法下载的情况,所以将最大线程数调整为1,调整后下载效果稳定。

4/13更新说明

  • 修复单个视频不能下载BV号的视频
  • 使用代理下载视频,批量下载更稳定,若下载失败的,将写入err.txt文件,打开文件复制内容,请再次运行程序,选择批量下载不同视频,粘贴下载未成功下载的视频

问题

由于很多视频网页格式并不一样,很有可能一个视频可以下载,但另外一个视频却不能正常下载,为了完善下载功能,需要不断测试,如果老铁有使用这个脚本的话,不能下载的视频可以留言给我哦。

  • 批量下载不同视频暂时无法使用,后续更新

待实现的功能

  • b站大会员视频下载

爬取思路与小结

  • 在查找过程中,查看源代码,bv号可以转化为av号,ss号可以转化为ep号
    即可以相互转换,如图,图中一个视频就有ep号,av,bv,cv号,代码中利用了bv号可以转化为av号,ss号可以转化为ep号
  • 只能对网页里已有的链接进行爬取,无法爬取大会员视频。
  • 打包Python
    • pip install pyinstaller
    • cd 到bilbili_down.py文件所在位置
    • 在cmd终端直接使用 pyinstaller bilbili_down.py
  • 这是我已经打包好的:感兴趣的老铁可以试一下功能:https://nmydt.lanzous.com/iFNApnzy0wj (由于后续仍然更新,,暂时不打包了)

代码


import json,requests,os,re,shutil,ssl,time,random,threading
from concurrent.futures import ThreadPoolExecutor
from lxml import etree
import os,syscount=0
errs = {}## 设置请求头等参数,防止被反爬
headers = {'Accept': '*/*','Accept-Language': 'en-US,en;q=0.5','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36'
}
params = {'from': 'search','seid': '9698329271136034665'
}
def re_video_info(text, pattern):'''利用正则表达式匹配出视频信息并转化成json'''match = re.search(pattern, text)return json.loads(match.group(1))def create_folder(aid):'''创建文件夹'''if not os.path.exists(aid):os.mkdir(aid)def remove_move_file(aid):'''删除和移动文件'''file_list = os.listdir('./')for file in file_list:## 移除临时文件if file.endswith('_video.mp4'):os.remove(file)passelif file.endswith('_audio.mp4'):os.remove(file)pass## 保存最终的视频文件elif file.endswith('.mp4'):if os.path.exists(aid + '/' + file):os.remove(aid + '/' + file)shutil.move(file, aid)
def BV_move_av(url):r=requests.get(url)r.close()html = etree.HTML(r.text)av_url = html.xpath('/html/head/meta[@itemprop="url"]/@content')[0]aid = re.search('\d+',av_url).group(0)return aid
def ss_move_ep(url):r=requests.get(url)r.close()url = "https://www.bilibili.com/bangumi/play/ep"+str(json.loads(re.search('"epList\":(.*?),\"epI',r.text).group(1))[0]['id'])return url
def download_video_batch(referer_url, video_url, audio_url, video_name, index):global proxytime.sleep(1)try:#批量下载系列视频'''## 更新请求头headers.update({"Referer": referer_url})## 获取文件名short_name = video_name.split('/')[2]print("%d.\t视频下载开始:%s" % (index, short_name))## 下载并保存视频video_content = requests.get(video_url, headers=headers,proxies=proxy)video_content.close()print('%d.\t%s\t视频大小:' % (index, short_name),round(int(video_content.headers.get('content-length', 0)) / 1024 / 1024, 2), '\tMB')with open('%s_video.mp4' % video_name, 'ab') as output:headers['Range'] = 'bytes=0'+ '-'output.write(video_content.content)## 下载并保存音频audio_content = requests.get(audio_url, headers=headers,proxies=proxy)audio_content.close()print('%d.\t%s\t音频大小:' % (index, short_name),round(int(audio_content.headers.get('content-length', 0)) / 1024 / 1024, 2), '\tMB')with open('%s_audio.mp4' % video_name, 'ab') as output:headers['Range'] = 'bytes=0' + '-'output.write(audio_content.content)return video_name, indexexcept Exception as e:av = referer_url.split('av')[-1]f = open('err.txt','w',encoding='utf-8')f.write(av+" ")print(short_name+'下载失败')def record_err(video_url, audio_url, video_name):errs[video_name] = (video_url, audio_url)
def download_video_single(referer_url, video_url, audio_url, video_name):'''单个视频下载'''## 更新请求头headers.update({"Referer": referer_url})print("视频下载开始:%s" % video_name)## 下载并保存视频video_content = requests.get(video_url, headers=headers)video_content.close()print('%s\t视频大小:' % video_name, round(int(video_content.headers.get('content-length', 0)) / 1024 / 1024, 2), '\tMB')received_video = 0with open('%s_video.mp4' % video_name, 'ab') as output:headers['Range'] = 'bytes=' + str(received_video) + '-'response = requests.get(video_url, headers=headers)response.close()output.write(response.content)## 下载并保存音频audio_content = requests.get(audio_url, headers=headers)audio_content.close()print('%s\t音频大小:' % video_name, round(int(audio_content.headers.get('content-length', 0)) / 1024 / 1024, 2), '\tMB')received_audio = 0with open('%s_audio.mp4' % video_name, 'ab') as output:headers['Range'] = 'bytes=' + str(received_audio) + '-'response = requests.get(audio_url, headers=headers)response.close()output.write(response.content)received_audio += len(response.content)print("视频下载结束:%s" % video_name)video_audio_merge_single(video_name)
def video_audio_merge_batch(result):'''使用ffmpeg批量视频音频合并'''video_name = result.result()[0]index = result.result()[1]import subprocessvideo_final = video_name.replace('video', 'video_final')command = 'ffmpeg -i "%s_video.mp4" -i "%s_audio.mp4" -c copy "%s.mp4" -y -loglevel quiet' % (video_name, video_name, video_final)subprocess.Popen(command, shell=True)print("%d.\t视频下载结束:%s" % (index, video_name.split('/')[2]))def video_audio_merge_single(video_name):'''使用ffmpeg单个视频音频合并'''print("视频合成开始:%s" % video_name)import subprocesscommand = 'ffmpeg -i "%s_video.mp4" -i "%s_audio.mp4" -c copy "%s.mp4" -y -loglevel quiet' % (video_name, video_name, video_name)subprocess.Popen(command, shell=True)print("视频合成结束:%s" % video_name)
def batch_download():'''使用多线程批量下载视频'''rm_folder('video_final')rm_folder('video')## 提示输入需要下载的系列视频对应的idaid = input("请输入要下载的视频id(举例:链接https://www.bilibili.com/video/BV1Ke411W71L?p=1中id为1Ke411W71L\nhttps://www.bilibili.com/video/av91748877?p=1中id为91748877,默认为91748877)")if aid:if re.search('\D',aid):aid = BV_move_av('https://www.bilibili.com/video/BV'+aid)else:aid = '91748877'## 提示选择清晰度quality = input('请选择清晰度(1代表高清,2代表清晰,3代表流畅),默认高清\t')if quality == '2':passelif quality == '3':passelse:quality = '1'acc_quality = int(quality) - 1## ssl模块,处理https请求失败问题,生成证书上下文ssl._create_default_https_context = ssl._create_unverified_context## 获取视频主题url = 'https://www.bilibili.com/video/av{}?p=1'.format(aid)print(url)r = requests.get(url, params=params, headers=headers)r.close()html = etree.HTML(r.text)
#     print(etree.tostring(html,encoding='utf-8').decode('utf-8'))title = html.xpath('//*[@id="viewbox_report"]/h1/span/text()')[0]rm_folder(title)print('您即将下载的视频系列是:', title)## 创建临时文件夹create_folder('video')create_folder('video_final')## 通过api获取视频信息r = requests.get('https://api.bilibili.com/x/player/pagelist?aid={}'.format(aid))r.close()res_json = r.json()video_name_list = res_json['data']print('共下载视频{}个'.format(len(video_name_list)))## 定义一个线程池,大小为3pool = ThreadPoolExecutor(1)
#     print(video_name_list)for i, video_content in enumerate(video_name_list):video_name = ('./video/P' + str(video_content['page'])+' '+video_content['part']).replace(" ", "-")origin_video_url = 'https://www.bilibili.com/video/av{}'.format(aid) + '?p=%d' % (i + 1)## 请求视频,获取信息res = requests.get(origin_video_url, headers=headers,proxies=proxy)res.close()## 解析出视频详情的jsonvideo_info_temp = re_video_info(res.text, '__playinfo__=(.*?)</script><script>')video_info = {}## 获取视频品质quality = video_info_temp['data']['accept_description'][acc_quality]## 获取视频时长video_info['duration'] = video_info_temp['data']['dash']['duration']## 获取视频链接video_url = video_info_temp['data']['dash']['video'][acc_quality]['baseUrl']## 获取音频链接audio_url = video_info_temp['data']['dash']['audio'][acc_quality]['baseUrl']## 计算视频时长video_time = int(video_info.get('duration', 0))video_minute = video_time // 60video_second = video_time % 60print('{}.\t当前视频清晰度为{},时长{}分{}秒'.format(i + 1, quality, video_minute, video_second))## 将任务加入线程池,并在任务完成后回调完成视频音频合并pool.submit(download_video_batch, origin_video_url, video_url, audio_url, video_name, i + 1).add_done_callback(video_audio_merge_batch)pool.shutdown(wait=True)time.sleep(5)## 整理视频信息if os.path.exists(title):shutil.rmtree(title)os.rename('video_final', title)try:shutil.rmtree('video')except:shutil.rmtree('video')
def rm_folder(filename):try:shutil.rmtree(filename)except Exception as e:pass
def batch_down_fanju():'''使用多线程批量下载番剧'''rm_folder('video_final')rm_folder('video')## 提示输入需要下载的系列视频对应的idurl = input('请输入下载的番剧链接:(支持ep,ss加数字类型,如https://www.bilibili.com/bangumi/play/ep21434,\nhttps://www.bilibili.com/bangumi/play/ss28763/)')if not url:url = 'https://www.bilibili.com/bangumi/play/ep21434'if 'ep' not in url:url = ss_move_ep(url)r = requests.get(url)r.close()html = etree.HTML(r.text)title = html.xpath('//*[@id="media_module"]/div/a')[0].textrm_folder(title)data = json.loads(re.search('__playinfo__=(.*?)</script>',r.text).group(1))catalog = json.loads(re.search('__INITIAL_STATE__=(.*?)\;\(function()',r.text).group(1))# name = ''.join(catalog['epList'][0]['titleFormat']+' '+catalog['epList'][0]['longTitle'])eplist = catalog['epList']all_num = len(catalog['epList'])urls=[]re.search('\D+',url).group(0)id = int(re.search('\d+',url).group(0))url_half = re.search('\D+',url).group(0)[urls.append(url_half+str(i['id'])) for i in eplist if i.get('badge')=='']quality = input('请选择清晰度(1代表高清,2代表清晰,3代表流畅),默认高清\t')if quality == '2':passelif quality == '3':passelse:quality = '1'acc_quality = int(quality) - 1## 创建临时文件夹create_folder('video')create_folder('video_final')pool = ThreadPoolExecutor(3)for i,ul in enumerate(urls):r = requests.get(ul)r.close()try:data = json.loads(re.search('__playinfo__=(.*?)</script>',r.text).group(1))except Exception as e:print(e)print(ul)continuename = ''.join(catalog['epList'][i]['titleFormat']+' '+catalog['epList'][i]['longTitle'])print(name)video_name = ('./video/' + name).replace(" ", "-")duration = data['data']['dash']['duration']quality = data['data']['support_formats'][acc_quality]['display_desc']video_url = data['data']['dash']['video'][acc_quality]['backupUrl'][0]audio_url = data['data']['dash']['audio'][acc_quality]['backupUrl'][0]video_minute = duration // 60video_second = duration % 60print('{}.\t当前视频清晰度为{},时长{}分{}秒'.format(i + 1, quality, video_minute, video_second))pool.submit(download_video_batch, url, video_url, audio_url, video_name, i + 1).add_done_callback(video_audio_merge_batch)pool.shutdown(wait=True)time.sleep(5)    ## 整理视频信息if os.path.exists(title):shutil.rmtree(title)os.rename('video_final', title)rm_folder('video')def multiple_download():'''批量下载多个独立视频'''## 提示输入所有aidaid_str = input('请输入要下载的所有视频id,id之间用空格分开\n举例:有5个链接https://www.bilibili.com/video/av89592082、https://www.bilibili.com/video/av68716174、https://www.bilibili.com/video/av87216317、\nhttps://www.bilibili.com/video/av83200644和https://www.bilibili.com/video/BV1qJ411W7T6?p=3,则输入89592082 68716174 87216317 83200644 1qJ411W7T6?p=3\n,默认为89592082 68716174 87216317 83200644 1qJ411W7T6?p=3\t')if aid_str:passelse:aid_str = '89592082 68716174 87216317 83200644 1qJ411W7T6?p=3'if os.path.exists(aid_str):shutil.rmtree(aid_str)aids = aid_str.split(' ')## 提示选择视频质量quality = input('请选择清晰度(1代表高清,2代表清晰,3代表流畅),默认高清\t')if quality == '2':passelif quality == '3':passelse:quality = '1'acc_quality = int(quality) - 1## 创建文件夹create_folder('video')print(2222222)## 创建线程池,执行多任务pool = ThreadPoolExecutor(1)for aid in aids:## 将任务加入线程池pool.submit(single_download, aid, acc_quality)pool.shutdown(wait=True)time.sleep(5)## 删除临时文件,移动文件remove_move_file(aid_str)def single_download(aid, acc_quality):'''单个视频实现下载'''## 请求视频链接,获取信息if re.search('\D',aid.split('?')[0]) :print(re.search('\D',aid))origin_video_url = 'https://www.bilibili.com/video/BV'+aidelse:origin_video_url = 'https://www.bilibili.com/video/av' + aidprint(origin_video_url)res = requests.get(origin_video_url, headers=headers)res.close()html = etree.HTML(res.text)if not re.search('\D',aid):title = html.xpath('//*[@id="viewbox_report"]/h1/span/text()')[0]else:if len(origin_video_url.split('p='))>1:page = int(origin_video_url.split('p=')[-1])-1else:page=0try:title = json.loads(re.search('__INITIAL_STATE__=(.*?);',res.text).group(1))['videoData']['pages'][page]['part']except:title = json.loads(re.search('__INITIAL_STATE__=(.*?]});',res.text).group(1))['videoData']['pages'][page]['part']video_info_temp = re_video_info(res.text, '__playinfo__=(.*?)</script><script>')video_info = {}## 获取视频质量quality = video_info_temp['data']['accept_description'][acc_quality]## 获取视频时长video_info['duration'] = video_info_temp['data']['dash']['duration']## 获取视频链接video_url = video_info_temp['data']['dash']['video'][acc_quality]['baseUrl']## 获取音频链接audio_url = video_info_temp['data']['dash']['audio'][acc_quality]['baseUrl']## 计算视频时长video_time = int(video_info.get('duration', 0))video_minute = video_time // 60video_second = video_time % 60print('当前视频清晰度为{},时长{}分{}秒'.format(quality, video_minute, video_second))## 调用函数下载保存视频download_video_single(origin_video_url, video_url, audio_url, title)def single_input():'''单个文件下载,获取参数'''## 获取视频aidaid = input('请输入要下载的视频id(举例:链接https://www.bilibili.com/video/av89592082中id为89592082,请输入89592082,默认为89592082\t,链接https://www.bilibili.com/video/BV1qJ411W7T6?p=2,请输入1qJ411W7T6?p=2\t)')if aid:passelse:aid = '89592082'## 提示选择视频质量quality = input('请选择清晰度(1代表高清,2代表清晰,3代表流畅),默认高清\t')if quality == '2':passelif quality == '3':passelse:quality = '1'acc_quality = int(quality) - 1## 调用函数进行下载single_download(aid, acc_quality)def main():'''主函数,提示用户进行三种下载模式的选择'''download_choice = input('请输入您需要下载的类型:\n1代表下载单个视频,2代表批量下载系列视频,3代表批量下载多个不同视频,默认下载单个视频\t')print("*"*30)## 批量下载系列视频if download_choice == '2':down_choic = input('请输入您需要下载的类型:\n1代表下载系列视频,2代表批量下载番剧')print("*"*30)if down_choic=='1':batch_download()else:batch_down_fanju()## 批量下载多个单个视频elif download_choice == '3':multiple_download()## 下载单个视频else:single_input()
def get_ip():global proxy,ipsr = requests.get('http://39.106.56.194/ip.html')ips = r.text.split('<br>')[:-1]if len(ips)==0:print('ip'+'正在获取中,请稍等')time.sleep(30)get_ip()returnip = random.choice(ips)print('当前选择的ip为:'+ip)proxy = {'http':ip}if __name__ == '__main__':'''调用主函数'''print("*"*14+"声明"+"*"*14) print("本程序单个视频下载只对av加数字有效,\n批量下载视频对av,bv,ss,ep加数字有效,\n批量下载独立视频只对av加数字有效\nBy 雾进\t\t 博客地址:https://blog.csdn.net/a12355556/")print("*"*30)get_ip()try:main()    except Exception as e:print(e)os.system("pause")sys.exit(1)

本项目在实施的过程中可能参考了其他大佬的实现思路,如有侵犯他人利益,请联系更改或删除。

爬虫案例 b站学习系列视频,番剧,单个视频 下载相关推荐

  1. 教你快速爬取哔哩哔哩整部番剧的视频弹幕

    快速爬取哔哩哔哩整部番剧的视频弹幕 使用工具:ibili 下载方式: npm i ibili 为了进行测试,现在我打算爬取 刀剑神域 Alicization 的整部番剧的视频弹幕 首先为了获取整部番剧 ...

  2. B站一键查询视频/分P视频/番剧的弹幕发送者

    近几年因为B站的门槛越来越低,时常会看到一些让人眼前一亮的弹幕.直接举报拉黑这种做法显然已经太便宜了.. 网上大部分查询工具时效性已经过了或者有局限性,所以我打算自己写一款能通过视频.分P视频.番剧来 ...

  3. 我把B站番剧的视频和弹幕一起下载下来了……

    众所周知,B 站是神器的网站 「里面的人,个个都是人才,说话又好听,超喜欢那里的.」 B 站里有很多宝藏 UP 主,视频质量非常高. 有时候我们想要下载一个视频,但是没有弹幕的 B 站视频是没有灵魂的 ...

  4. JavaCV开发详解之29:使用javacv将多个视频拼接合成单个视频,多个音频拼接合成单个音频,以多个mp4视频合成一个mp4为例

    javacv实战专栏目录: JavaCV实战专栏文章目录(JavaCV速查手册) 前言 上一章中我们讲了多张图片合成视频,本章将再此基础上继续拓展,将多个视频/音频拼接合成一个视频/音频文件. 参考资 ...

  5. HttpClient实战爬虫B站番剧

    准备 我们打开一个b站首页,选择番剧索引. https://www.bilibili.com/anime/index/#season_version=-1&area=-1&is_fin ...

  6. 2020-04-06-B站学习视频

    B站学习视频资源 我的很多视频学习都会在B站进行发布,现在就整理在我的博客中. B站学习搜索关键词 B站学习搜索关键词 跟踪SCIhub网址及下载文章 scihub的科学使用方法 E-prime通过T ...

  7. 使用mpv在线播放Bilibili和AcFun网站视频和番剧(Windows + Linux)

    简介 通过 you-get + mpv 直接播放Bilibili和Acfun视频和番剧,无需下载. 说明 此脚本依赖于 mpv.python3 和 you-get,使用前先确保您的电脑已安装它们. 获 ...

  8. Python-100-Days之 数据分析 Day67 番剧数据可视化

    Day67 数据可视化之调用API并可视化 API:类似接口,在后端的数据可以通过API传递给前端,前端可以实现数据的可视化.与HTTP网页爬虫相比,API接口已经存储好数据,不太需要去解析网页. 参 ...

  9. Python3 Request+Redis 爬取BILIBILI番剧列表

    该项目是结合Redis与Python爬虫爬取哔哩哔哩番剧索引中的所有番剧,目标网站链接如下: 番剧索引 - 哔哩哔哩 (゜-゜)つロ 干杯~-bilibili 前情提要 该项目需要提前下载并配置好Wi ...

最新文章

  1. html右侧浮动栏随着滚动,jQuery实现div浮动层跟随页面滚动效果
  2. [YTU]_2906( 多重继承 日期与时间)
  3. object-c html,object.html
  4. php中isdefin,在PHP中定義全局常量數組的最“優雅”方法是什么
  5. 合并单链表,输出单链表中间元素,判断是否有环等
  6. DoraCMS 源码知识点备注
  7. OA(part2)--Outlier Evaluation Techniques
  8. python executemany执行延迟_运维架构师-Python 自动化运维开发-031
  9. 变量使用self.foo还是_foo
  10. python调用接口获取数据_python:接口间数据传递与调用方法
  11. 【Pytorch版本】损失函数大汇总
  12. 微信开放平台_第三方平台授权流程_授权码
  13. java 历届试题 农场阳光 蓝桥杯1040
  14. 微信html页面缓存问题,浅谈微信页面入口文件被缓存解决方案_简单_前端开发者...
  15. 18V降压3.3V的降压IC和LDO芯片方案,15V降压3.3V的降压IC和LDO芯片方案
  16. 如何把Eclipse修改为黑色主题
  17. 脱颖而出:为您的独立游戏扩大客户群 游戏运营
  18. 单片机c语言idata什么意思,单片机C语言中的data,idata,xdata,pdata,code
  19. 什么是数据工程师,主要做哪些工作?
  20. VLDB‘22 HiEngine极致RTO论文解读

热门文章

  1. 一分钟学会看k线图_新手入门:资深炒股高手教你一分钟学会看k线图
  2. docker学习应用-02
  3. java栈堆溢出怎么解决_JVM之栈溢出和堆溢出
  4. 02. QCefView + QWebChannel 实现Qt程序中嵌入Web页面
  5. 供应链复杂系统脆性传播模型与管控方法研究
  6. Apache踩坑:500 Internal Server Error报错
  7. 学习python爬虫一个月的小兄弟,是如何日赚800实现经济独立的
  8. 为什么期货交易越做越难?
  9. 最详尽的4K技术科普
  10. html5用canvas画小黄人