代码版

# !/user/bin/env python
# -*- coding:utf-8 -*-
"""
    time: 2018-12-16
    title:B站视频下载
    annotation:
    author: pqx
    email:[email protected]
"""
import os
import re
import time
import shutil
import requests
import hashlib
import urllib.request
from requests.adapters import HTTPAdapter
from xml.dom.minidom import parseString
from moviepy.editor import *

def clean_txt(title):#清洗标题中不能用于命名文件的字符
    rstr = r"[\/\\\:\*\?\"\<\>\|]"  # '/ \ : * ? " < > |'
    title = re.sub(rstr, "_", title)  # 替换为下划线
    return title

def create_folder(name):#创建文件夹
    try:
        if '{}'.format(name) not in os.listdir():#如果不存在
            os.makedirs('{}'.format(name))#则创建
    except:
        return ''

headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'}
def get_one_page_text(url,headers=headers,code='utf-8'):#访问一个页面 返回页面信息
    try:
        s = requests.Session()#保持会话
        s.mount('http://', HTTPAdapter(max_retries=3))#最大重试
        s.mount('https://', HTTPAdapter(max_retries=3))
        r=s.get(url,headers=headers,timeout=15)#超时设置
        r.raise_for_status()#状态码 如果不是200则报错
        r.encoding=code#r.apparent_encoding#字符类型
        return  r.text#返回页面
    except Exception as e:
        t=time.strftime('%Y/%m/%d %H:%M:%S %a')#时间格式化
        with open(r'D:\bilibili_video\Exception.txt','a+',encoding='utf-8') as f:
            f.write('time:{}\n\nurl:{}\n\n{}\n\n'.format(t,url,e))

def get_params(start_url,quality,p):
    html = get_one_page_text(start_url)
    cid = re.search(r'cid=(\d+)&',html).group(1)
    title = clean_txt(re.search(r'<h1 title="(.*?)">',html).group(1))+'第%sP'%p

    path = r'D:\bilibili_video\{}'.format(title)
    create_folder(path)
    SEC1 = '94aba54af9065f71de72f5508f1cd42e' #上面的SEC已经失效了
    ts = str(int(time.time())) #时间戳
    params = 'appkey=84956560bc028eb7&cid={}&otype=xml&qn={}&quality={}&type='.format(cid, quality, quality) #otype=json也行!!
    encrypt = hashlib.md5(bytes(params+SEC1,'utf-8')).hexdigest()
    video_list=get_down_load_url(params, encrypt, start_url)
    print('视频cid : %s 共 %s 段' % (cid, len(video_list)))
    down_load( video_list, start_url, path,title)
    splice_mp4(path,title)

def get_down_load_url(params,encrypt,start_url):
    url_api = 'https://interface.bilibili.com/v2/playurl?' + params + '&sign=' + encrypt
    headers = {'Referer':start_url,'User-Agent':'Mozilla/5.0'}
    html = get_one_page_text(url_api,headers=headers)
    doc = parseString(html.encode('utf8'))
    durl = doc.getElementsByTagName('durl')
    video_list = []
    for i in durl:
        video = i.getElementsByTagName('url')[0]
        url_video = video.childNodes[0].data
        video_list.append(url_video)
    return video_list

def down_load(video_list,start_url,path,title):
    num = 1
    for i in video_list:
        opener = urllib.request.build_opener()
        opener.addheaders = [
            # ('Host', 'upos-hz-mirrorks3.acgvideo.com'),  #注意修改host,不用也行
            ('User-Agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:56.0) Gecko/20100101 Firefox/56.0'),
            ('Accept', '*/*'),
            ('Accept-Language', 'en-US,en;q=0.5'),
            ('Accept-Encoding', 'gzip, deflate, br'),
            ('Range', 'bytes=0-'),  # Range 的值要为 bytes=0- 才能下载完整视频
            ('Referer', start_url),  # 注意修改referer,必须要加的!
            ('Origin', 'https://www.bilibili.com'),
            ('Connection', 'keep-alive')
        ]
        print('视频标题 : %s ,第%s段正在下载' % (title, num))
        urllib.request.install_opener(opener)
        urllib.request.urlretrieve(url=i, filename=path+'\\%s-%s.flv'%(title,num))  # 写成mp4也行  title + '-' + num + '.flv'
        print('视频标题 : %s ,第%s段下载完成' % (title,  num))
        num += 1

def splice_mp4(path,title):
    print('合并视频 : %s ' % (title))
    LL = sorted(os.listdir(path))
    if len(LL)>=2:
        ll=[]
        for file in LL:
            if os.path.splitext(file)[1] == '.flv':
                filepath = os.path.join(path, file)
                video = VideoFileClip(filepath)
                ll.append(video)
        final_clip = concatenate_videoclips(ll)
        final_clip.write_videofile(path+'\\{}.mp4'.format(title), fps=24, remove_temp=False)
        print('视频 : %s 合并完成' % (title))
        os.remove('./{}TEMP_MPY_wvf_snd.mp3'.format(title))  # 删文件
    elif len(LL)==1:
        os.rename(os.path.join(path, LL[0]), os.path.join(path, title + ".mp4"))
        print('视频 : %s 合并完成' % (title))
    new_path = r'D:\bilibili_video'
    shutil.move(path + '\\{}.mp4'.format(title), new_path + '\\{}.mp4'.format(title))

def datas():
    quality = input('请输入您要下载视频的清晰度(1080p:80;720p:64;480p:32;360p:15)(填写80或64或32或15):')
    start = input('请输入您要下载的B站av号或者视频链接地址如:av19956343,或 19956343,或https://www.bilibili.com/video/av19956343:')
    p=input('请输入您要下载的B站av号P数(只有1p请直接回车或输入数字 如:1)(多选请输入p数 如:1,3,5  以英文","号分隔)(选择范围请输入 如:2-10,以英文"-"号分隔):')
    if start.replace('av','').isdigit() == True:
        av_url = 'https://www.bilibili.com/video/av'+start.replace('av','')
    else:
        av_url=start
    start_url_list=[]
    if p==''or int(p).isdigit()==True:
        start_url = av_url + '/?p=%s' % p
        start_url_list.append([start_url,int(p)])
    elif ','in p:
        for p in p.split(','):
            start_url=av_url+'/?p=%s'%p
            start_url_list.append([start_url,p])
    elif '-'in p:
        for p in range(int(p.split('-')[0]),int(p.split('-')[1])+1):
            start_url=av_url+'/?p=%s'%p
            start_url_list.append([start_url,p])
    else:
        print('输入有误,程序退出')
    return quality,start_url_list

def main ():
    quality,start_url_list=datas()[0],datas()[1]
    for start_url in start_url_list:
        get_params(start_url[0],quality,start_url[1])

if __name__=='__main__':
    main()

图片版
bilibili视频下载

相关文章:

猜你喜欢
相关资源
相似解决方案