如何用 Python 爬虫实现哔哩哔哩视频下载?

作者:段小草
链接:https://www.zhihu.com/question/486484268/answer/2911872392
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

人之所以区别于动物,是会使用工具。现成的工具有太多了,程序员更倾向于不重复造轮子。

直接使用 you-get

题主只说了使用 Python,没说具体如何使用。众所周知, you-get 所有代码都是由 Python 完成的。

除了 B站,you-get 还支持几乎所有市面主流的视频网站。

使用方法也非常简单。

安装:

pip install you-get

使用

$ you-get 视频链接

作为命令行工具,you-get 也提供了丰富的可供选择,比如选择分辨率、查看视频信息等,详见 Github 仓库:

https://github.com/soimort/you-getgithub.com/soimort/you-get

分析 you-get 源码

如果题主不单单是为了工具,而是想自己写代码,或者是为了完成某门作业之类的。比较快捷的做法是去查看 you-get 源码中关于 B站的部分。不过可能会缺少下载的部分,因为 you-get 的下载器是同一的,bilibili.py 更偏向于视频播放地址的解析。

详见:

https://github.com/soimort/you-get/blob/1c841f7e8ce60130572a8f03fb038eda99deff6a/src/you_get/extractors/bilibili.pygithub.com/soimort/you-get/blob/1c841f7e8ce60130572a8f03fb038eda99deff6a/src/you_get/extractors/bilibili.py

自己观察视频加载播放

如果题主不想吃做好的饭,非要挑战自己,尝试重新造轮子的话。那就需要自己去找真实的播放链接了。

一般来讲,获取网站的视频播放有几种方式:

①最简单的情况下,直接查看network,会看到一个体积巨大的请求,右键,在新链接打开,直接下载就是了。

②一些网站会把视频切割成 .ts 的分段文件,然后用一个 .m3u8 的文件,可以下载所有的 .ts 文件后进行拼接重新输出。(可以搜索 m3u8 ts 拼接这些关键字,这里不展开了)

③在源码里分析,看会不会找到真实播放地址的线索,等等。


我之前设想过一个工作流:自动监测某个账号的视频更新-下载视频-语音转文字-摘要成笔记。因为现在长视频的内容太多了,其中确实有一些优秀视频,但看视频远比图文费时间,其中的主要内容还不能直接复制/粘贴整理。不过后来由于懒一直没有执行…

发布于 2023-02-26 16:37・IP 属地河南

赞同 19 2 条评论 分享 收藏 喜欢 收起

更多回答

拾柒

合格证书

关注

前言

嗨喽,大家好呀~这里是爱看美女的茜茜呐

又到了学Python时刻~



现在好看的妹子真的太多啦~

如何一次性把这些好看的视频全保存下来捏?



开发环境:

( 源码、教程、文档、软件点击此处跳转跳转文末名片加入君羊,找管理员小姐姐领取呀~ )



实现一个案例的流程:

一. 思路分析

案例的分析 视频 media .mp4 .mp3 .m4a m3u8视频流

.m4s: 视频/音频 格式

数据来源分析: 找到视频内容

network抓包

二. 代码实现(代码实现基本流程)

  1. 发送请求 使用代码的方式访问网站
  2. 获取数据 取出网页源代码
  3. 解析数据 提取视频链接
  4. 保存数据



代码展示

PS:完整源码如有需要的小伙伴可以加下方的群去找管理员免费领取



导入模块

import requests
import re       # 正则表达式模块
import json
import pprint
import subprocess
import os

伪装(请求头)

headers = {
    # 用户信息
    "cookie": "buvid3=355AA300-6A61-04E5-A05C-E891D886F69632716infoc; b_nut=1675085932; i-wanna-go-back=-1; _uuid=387EA3810-FBF5-E92C-827E-2510B578C5B9A33232infoc; buvid4=15C69C98-F6A7-EC6A-872F-E69C1840DD6D33724-023013021-1pW1w45e5fZS9RtebDiGZw%3D%3D; nostalgia_conf=-1; CURRENT_FNVAL=4048; rpdid=|(kmJY|k))lY0J'uY~l|)lmY|; SESSDATA=17eb9f1a%2C1690782878%2C6a25c%2A22; bili_jct=4af9076b42f76603dfe4cf018ad2000f; DedeUserID=422789639; DedeUserID__ckMd5=fc4901c78719b545; CURRENT_QUALITY=80; b_ut=5; b_lsid=51ED8F105_1861C3EEC10; theme_style=light; is-2022-channel=1; sid=66dafqju; fingerprint=97ca5a8b555e63aca787c9cd27273c7e; buvid_fp_plain=undefined; buvid_fp=8cc52ae38b592ff26dc2b393eebd890b; PVID=2; innersign=1; bp_video_offset_422789639=758803412994228200",
    # 防盗链
    "referer": "https://www.****.com/",
    # 浏览器基本信息
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36"
}

发送请求

url = 'https://www.***.com/video/BV1qv4y1k7UE/?vd_source=8f216a44bce0dbe14e3447c34c2ab3e2'
response = requests.get(url, headers=headers)

获取数据

html_data = response.text

解析数据

json_str = re.findall('__playinfo__=(.*?)', html_data)[0]
# 

(.*?)

title = re.findall('

(.*?)

', html_data)[0] # Python基础 字典 json_data = json.loads(json_str) # pprint.pprint(json_data) audio_url = json_data['data']['dash']['audio'][0]['baseUrl'] print(audio_url) video_url = json_data['data']['dash']['video'][0]['baseUrl'] print(video_url)

保存数据

audio_data = requests.get(audio_url, headers=headers).content
with open(f'{title}.mp3', mode='wb') as f:
    f.write(audio_data)
video_data = requests.get(video_url, headers=headers).content
with open(f'{title}.mp4', mode='wb') as f:
    f.write(video_data)

ffmpeg = f'ffmpeg -i {title}.mp4 -i {title}.mp3 -acodec copy -vcodec copy {title+"-out.mp4"}'
subprocess.run(ffmpeg)
os.remove(f'{title}.mp4')
os.remove(f'{title}.mp3')








尾语

感谢你观看我的文章呐~本次航班到这里就结束啦

希望本篇文章有对你带来帮助 ,有学习到一点知识~

躲起来的星星 也在努力发光,你也要努力加油(让我们一起努力叭)。


发布于 2023-02-10 22:56

赞同 添加评论 分享 收藏 喜欢 收起

苛岚异梦

关注

5 人赞同了该回答

一:由于哔哩哔哩的视频文件和音频文件是分离的,所有这里分别下载后在使用ffmepg进行合成

ffmpeg官网下载 https://ffmpeg.org/ (下载后需要配置环境变量)需要重启! 链接:https://pan.baidu.com/s/1aDSlIQ1kPOOUsVOAmcFfHw 提取码:gvw4

gitee地址 https://gitee.com/ddpoi/bili

博客地址https://www.ddblog.xyz/articles/53

欢迎大佬前来指点

# import time
import requests
import re
import os
import subprocess
# import sys,io
# sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='gb18030')

headers = {
    'referer': 'https://www.bilibili.com/video/BV1J3411h7pm?spm_id_from=333.5.0.0',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36 Edg/97.0.1072.69'
}


# 获取视频bvid
def get_bvid(mid,path):
    ''' 这里ps=30,是一页的视频数量,pn代表页数,可以自己加一个循环 '''
    url = 'https://api.bilibili.com/x/space/arc/search?mid={}&ps=30&tid=0&pn=1&keyword=&order=pubdate&jsonp=jsonp'.format(mid)
    response = requests.get(url=url,headers=headers).json()
    mm = response['data']['list']['vlist']
    for i in mm:
        title = (i['title'])
        bvid = (i['bvid'])
        # print(title)
        # print(bvid)
        get_cid(bvid,title,path)


# 获取cid
def get_cid(bvid,title,path):
    url = 'https://api.bilibili.com/x/player/pagelist?bvid={}&jsonp=jsonp'.format(bvid)
    response = requests.get(url=url,headers=headers).json()
    nn = response['data']
    for i in nn:
        cid = (i['cid'])
        # print(cid)
        get_video(bvid,cid,title,path)


# 获取视频,音频的url
def get_video(bvid,cid,title,path):
    url = 'https://api.bilibili.com/x/player/playurl?cid={}&bvid={}&qn=120&type=&otype=json&fourk=1&fnver=0&fnval=4048&session=521a3c69be48043423dab7dfacc0dfde'.format(cid,bvid)
    response = requests.get(url=url,headers=headers).json()
    video_url = response['data']['dash']['video'][0]['baseUrl']
    audio_url = response['data']['dash']['audio'][0]['baseUrl']
    # print(audio_url)
    # print(video_url)
    save(title,video_url,audio_url,path)

# 保存视频,音频文件,(下载时文件名取第一个字符,防止含有特殊符号而报错)
def save(title,video_url,audio_url,path):
    try:
        video = requests.get(url=video_url,headers=headers)
        with open(path+title[:1]+'.mp4','ab') as f:
            f.write(video.content)
            print('{}下载完成....'.format(title + '.mp4'))
        audio = requests.get(url=audio_url, headers=headers)
        with open(path+title[:1]+'.mp3','ab') as f:
            f.write(audio.content)
            print('{}下载完成....'.format(title + '.mp3'))
    except:
        pass
    video_add(title,path)

'''
由于有些up主视频标题包含特殊字符,例如 ? | / 等。所以需要将这些字符过滤出去
'''

# 将下载的视频,音频进行合成
def video_add(title,path):
    intab = "?/|.><:*"
    for s in intab:
        if s in title:          # 将特殊字符转化为空格
            print(s)
            title = title.replace(s, '')
    #  如果程序异常,选择忽略
    try:
        # print(title)
        print('开始合成')
        os.chdir(path)
        cmd = f'ffmpeg -i {title[:1]}.mp4 -i {title[:1]}.mp3 -c:v copy -c:a aac -strict experimental {title[:1]}_1.mp4'
        # print(cmd)
        subprocess.call(cmd, shell=True)
        '''选择是否删除原mp3、mp4文件'''
        os.remove(title[:1]+'.mp4')
        os.remove(title[:1]+'.mp3')
        os.rename(title[:1] + '_1.mp4', title + '.mp4')
        print('合成结束')
    except:
        pass


# 判断输入的是mid还是视频链接
def main(mid,path):
    if mid == "":
        print('错误,请重新输入') # mid输入不能为空
        return False
    if re.match(r'[a-zA-Z]', mid):      # 用正则来判断输入的mid类型
        bvid = (mid.split('?')[0]).split('/')[4]        # 取出视频链接中的bvid
        print(bvid)
        title = input('请输入视频名称:')   # 手动指定视频名称
        get_cid(bvid,title,path)
    else:
        get_bvid(mid,path)

if __name__ == '__main__':
    mid = input('请输入up主的mid或视频链接:')
    print('路径一定要加/,默认为当前目录')
    file_path = input('请输入保存文件路径:')      # 输入文件路径
    if file_path == "" :
        path = './'
    else:
        path = file_path
    main(mid,path)
展开阅读全文

页面更新:2024-03-20

标签:爬虫   轮子   路径   源码   代码   链接   文件   工具   数据   视频   网站

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top