爬取B站视频

爬取B站视频


歌曲:暗号 - 周杰伦


Part0 懒人纯享版

  • 下载地址见结尾


Part1 环境准备

- python3环境
- 安装lxml模块 # pip install lxml
- 安装requests模块 # pip install requests
- 安装ffmpeg软件 # 见下文


Part2 获取网页代码

  • 通过requests模块,获取到目标页面的网页源代码。input函数接收url,并以"?"分割。
self.url = input('请输入网址:').split("?", 1)[0]
self.headers = {
"user-agent": "Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/\
91.0.4472.77Safari/537.36Edg/91.0.864.37",
}
self.htmlData = requests.get(url=self.url, headers=self.headers)
  • 通过xpath定位,提取网页的title作为视频的文件名,并替换掉部分不支持作为文件名的字符。(如:空格,斜杠,制表符等)
self.title = etree.HTML(self.htmlData.content).xpath('//*[@id="viewbox_report"]/h1/span/text()')[0]\
 .replace(' ','_').replace('\t','_').replace('/','_')
  • 获取页面xpath的方法
- 获取页面源代码
- 使用浏览器打开获取到的源代码
- F12检查指定元素
- 右键复制元素的xpath地址



Part3 获取视频地址

  • 分析网页源代码,得知视频地址存放于json格式的数据中
  • 提取数据,并打印json格式数据进行分析
import pprint
# self.htmlData就是part1中获取到的源代码
jsonStr = self.htmlData.text.split('window.__playinfo__=')[-1].split('<')[0].split(';')[0]
jsons = json.loads(jsonStr)
pprint.pprint(jsons)
  • 从json格式数据中,获得视频地址
data = jsons["data"]
dash = data['dash']
videos = dash['video']
audios = dash['audio']
mp3_path = audios[0]['baseUrl']
mp4_path = videos[0]['baseUrl']


Part4 下载视频

  • 同样是通过requests模块,访问获得的视频地址,从而获取视频数据,只是需要在headers消息头中添加Range参数。
headers = {
 'Range': 'bytes={}-{}'.format(min, max),
 'Referer': 'https://www.bilibili.com',
 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) \
 Chrome/75.0.3770.100 Safari/537.36'
}
resp = requests.get(url, headers=headers)
  • 创建文件夹video
import os
if not os.path.exists('video'):
 os.makedirs('video')
  • 将视频数据追加写入文件,以title命名
with open('video/{}.mp4'.format(self.title), 'ab+') as f:
 f.write(resp.content)


Part5 视频合成

  • 需要提前安装ffmpeg软件。下载地址见结尾
- 解压文件后,需要添加环境变量
- 安装后,需要重启计算机
  • 利用ffmpeg进行视频合成。(将前面获得的视频与音频文件合成为output文件)
subprocess.call('ffmpeg -i video/{}.mp4 -i video/{}.mp3 -c:v copy -c:a aac -strict \
 experimental video/{}_output.mp4'\
 .format(self.title,self.title,self.title), shell=True)


完整代码

import requests
import json
import subprocess
from lxml import etree
import os
# import pprint
# ffmpeg 需要自己安装
# testUrl:https://www.bilibili.com/video/BV19r4y167Tf?spm_id_from=333.1007.top_right_bar_window_view_later.content.click

class getFile_fromBiliBili():
    # 获取网页代码
    def getHtml(self):
        self.url = input('请输入网址:').split("?", 1)[0]
        self.headers = {
            "user-agent": "Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/\
                    91.0.4472.77Safari/537.36Edg/91.0.864.37",
        }
        self.htmlData = requests.get(url=self.url, headers=self.headers)
        self.title = etree.HTML(self.htmlData.content).xpath('//*[@id="viewbox_report"]/h1/span/text()')[0]\
            .replace(' ','_').replace('\t','_').replace('/','_')

    # 网页代码经过分割处理,获取视频地址
    def getPath(self):
        jsonStr = self.htmlData.text.split('window.__playinfo__=')[-1].split('<')[0].split(';')[0]
        jsons = json.loads(jsonStr)
        # pprint.pprint(jsons) # 打印json格式数据
        data = jsons["data"]
        mp3_path = ''
        mp4_path = ''
        # 当音频视频分开的时候
        try:
            dash = data['dash']
            videos = dash['video']
            audios = dash['audio']
            mp3_path = audios[0]['baseUrl']
            mp4_path = videos[0]['baseUrl']
        # 音频视频没有分开的时候
        except:
            mp4_path = ''
            durls = data['durl']
            for durl in durls:
                if durl['order'] == 1:
                    mp4_path = durl['url']

        self.mp3_path = mp3_path
        self.mp4_path = mp4_path

    def download_mp4(self,min, max, url):
        if url == '':
            return ''
        headers = {
            'Range': 'bytes={}-{}'.format(min, max),
            'Referer': 'https://www.bilibili.com',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) \
            Chrome/75.0.3770.100 Safari/537.36'
        }
        resp = requests.get(url, headers=headers)
        if resp.status_code != 206:
            return resp.status_code
        print(resp.status_code)
        with open('video/{}.mp4'.format(self.title), 'ab+') as f:
            f.write(resp.content)
        return 0

    def download_mp3(self,min, max, url):
        # 如果没有音频
        if url == '':
            return ''
        headers = {
            'Range': 'bytes={}-{}'.format(min, max),
            'Referer': 'https://www.bilibili.com',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) \
            Chrome/75.0.3770.100 Safari/537.36'
        }
        resp = requests.get(url, headers=headers)
        if resp.status_code != 206:
            return resp.status_code
        print(resp.status_code)
        with open('video/{}.mp3'.format(self.title), 'ab+') as f:
            f.write(resp.content)
        return 0

    # 视频下载模块
    def download_mp3_mp4(self,mp3_path, mp4_path):
        if not os.path.exists('video'):
            os.makedirs('video')
        min = 0
        max = 200000
        mp4_status_code = 0
        mp3_status_code = 0
        while True:
            if mp4_status_code == 0:
                mp4_status_code = self.download_mp4(min, max, mp4_path)
                print("mp4_status_code:", mp4_status_code)
            else:
                pass
            if mp3_status_code == 0:
                mp3_status_code = self.download_mp3(min, max, mp3_path)
                print('mp3_status_code:', mp3_status_code)
            else:
                pass
            if mp4_status_code != 0 and mp3_status_code != 0:
                break
            min = max + 1
            max = max + 200000

    # 视频合成模块
    def compose(self):
        try:
            subprocess.call('ffmpeg -i video/{}.mp4 -i video/{}.mp3 -c:v copy -c:a aac -strict \
            experimental video/{}_output.mp4'\
            .format(self.title,self.title,self.title), shell=True)
            print('合成完成!!!')
        except:
            print('合成失败!!!')

    def run(self):
        self.getHtml()
        self.getPath()
        self.download_mp3_mp4(self.mp3_path, self.mp4_path)
        self.compose()


if __name__ == '__main__':
    b = getFile_fromBiliBili()
    b.run()


参考链接

- bilibili视频爬虫:https://blog.csdn.net/qq_45695453/article/details/105757919
- ffmpeg的安装与使用:https://blog.csdn.net/qq_39516859/article/details/81843419

下载链接

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

推荐阅读更多精彩内容