python实现的B站直播录制工具

项目地址:

https://github.com/Redlnn/blive_record

前言

  • 作者: Red_lnn
  • 不允许将本项目运用于非法以及违反B站用户协议的用途
  • 仅支持单个主播,多个主播请复制多份并分开单独启动
  • 运行时如要停止录制并退出,请按键盘 Ctrl+C
  • 如要修改录制设置,请以纯文本方式打开.py文件
  • 利用 FFmpeg 直接抓取主播推送的流,无需打开浏览器
  • 有新功能需求请直接提 Pull requests
  • 建议录制为 flv 格式(默认),以防止意外中断导致录制文件损坏,若要进行剪辑可使用 FFmpeg 转换为 mp4 文件后再倒入到剪辑软件(使用 FFmpeg 转换 flv 为 mp4 : ffmpeg -i {input}.flv -c:v copy -c:a copy {output}.mp4)

使用方式

1.安装 Python(>=3.7) 并设置环境变量

2.打开终端或命令行进入本脚本所在目录

3.通过 pip 安装必须的第三方库

Windows:

pip install -r requirements.txt

Linux:

python3 -m pip install -r requirements.txt

4.下载 ffmpeg 并正确设置环境变量(下载地址
5.Windows 直接双击运行start.bat
6.Linux 先运行 chmod +x start.sh 再运行 ./start.sh

主要代码

blive_record.py

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

"""
*--------------------------------------*
 B站直播录播姬 By: Red_lnn
 仅支持单个主播,多个主播请复制多份并分开单独启动
 运行时如要停止录制并退出,请按键盘 Ctrl+C
 如要修改录制设置,请以纯文本方式打开.py文件
 利用ffmpeg直接抓取主播推送的流,不需要打开浏览器
*--------------------------------------*
"""

# import ffmpy3  # noqa
import logging
import os
import signal
import sys
import threading
import time
import traceback
from json import loads
from logging import handlers
from subprocess import PIPE, Popen, STDOUT

import requests
from regex import match

# 导入配置
from config import *   # noqa

record_status = False  # 录制状态,True为录制中
kill_times = 0  # 尝试强制结束FFmpeg的次数

logging.addLevelName(15, 'FFmpeg')  # 自定义FFmpeg的日志级别
logger = logging.getLogger('Record')
logger.setLevel(logging.DEBUG)

fms = '[%(asctime)s %(levelname)s] %(message)s'
# datefmt = "%Y-%m-%d %H:%M:%S"
datefmt = "%H:%M:%S"

default_handler = logging.StreamHandler(sys.stdout)
if debug:
    default_handler.setLevel(logging.DEBUG)
elif verbose:
    default_handler.setLevel(15)
else:
    default_handler.setLevel(logging.INFO)
default_handler.setFormatter(logging.Formatter(fms, datefmt=datefmt))
logger.addHandler(default_handler)

if save_log:
    # file_handler = logging.FileHandler("debug.log", mode='w+', encoding='utf-8')
    if not os.path.exists(os.path.join('logs')):
        os.mkdir(os.path.join('logs'))
    file_handler = handlers.TimedRotatingFileHandler(os.path.join('logs', 'debug.log'), 'midnight', encoding='utf-8')
    if debug:
        default_handler.setLevel(logging.DEBUG)
    else:
        default_handler.setLevel(15)
    file_handler.setFormatter(logging.Formatter(fms, datefmt=datefmt))
    logger.addHandler(file_handler)

def get_timestamp() -> int:
    """
    获取当前时间戳
    """
    return int(time.time())

def get_time() -> str:
    """
    获取格式化后的时间
    """
    time_now = get_timestamp()
    time_local = time.localtime(time_now)
    dt = time.strftime("%Y%m%d_%H%M%S", time_local)
    return dt

def record():
    """
    录制过程中要执行的检测与判断
    """
    global p, record_status, last_record_time, kill_times  # noqa
    while True:
        line = p.stdout.readline().decode()
        p.stdout.flush()
        logger.log(15, line.rstrip())
        if match('video:[0-9kmgB]* audio:[0-9kmgB]* subtitle:[0-9kmgB]*', line) or 'Exiting normally' in line:
            record_status = False  # 如果FFmpeg正常结束录制则退出本循环
            break
        elif match('frame=[0-9]', line) or 'Opening' in line:
            last_record_time = get_timestamp()  # 获取最后录制的时间
        elif 'Failed to read handshake response' in line:
            time.sleep(5)  # FFmpeg读取m3u8流失败,等个5s康康会不会恢复
            continue
        time_diff = get_timestamp() - last_record_time  # 计算上次录制到目前的时间差
        if time_diff >= 65:
            logger.error('最后一次录制到目前已超65s,将尝试发送终止信号')
            logger.debug(f'间隔时间:{time_diff}s')
            kill_times += 1
            p.send_signal(signal.SIGTERM)  # 若最后一次录制到目前已超过65s,则认为FFmpeg卡死,尝试发送终止信号
            time.sleep(0.5)
            if kill_times >= 3:
                logger.critical('由于无法结束FFmpeg进程,将尝试自我了结')
                sys.exit(1)
        if 'Immediate exit requested' in line:
            logger.info('FFmpeg已被强制结束')
            break
        if p.poll() is not None:  # 如果FFmpeg已退出但没有被上一个判断和本循环第一个判断捕捉到,则当作异常退出
            logger.error('ffmpeg未正常退出,请检查日志文件!')
            record_status = False
            break

def main():
    global p, room_id, record_status, last_record_time, kill_times  # noqa
    while True:
        record_status = False
        while True:
            logger.info('------------------------------')
            logger.info(f'正在检测直播间:{room_id}')
            try:
                room_info = requests.get(f'https://api.live.bilibili.com/room/v1/Room/get_info?room_id={room_id}',
                                         timeout=5)
            except (requests.exceptions.ReadTimeout, requests.exceptions.Timeout, requests.exceptions.ConnectTimeout):
                logger.error(f'无法连接至B站API,等待{check_time}s后重新开始检测')
                time.sleep(check_time)
                continue
            live_status = loads(room_info.text)['data']['live_status']
            if live_status == 1:
                break
            elif live_status == 0:
                logger.info(f'没有开播,等待{check_time}s重新开始检测')
            time.sleep(check_time)
        if not os.path.exists(os.path.join('download')):
            try:
                os.mkdir(os.path.join('download'))
            except:  # noqa
                logger.error(f'无法创建下载文件夹 ↓\n{traceback.format_exc()}')
                sys.exit(1)
        if os.path.isfile(os.path.join('download')):
            logger.error('存在与下载文件夹同名的文件')
            sys.exit(1)
        logger.info('正在直播,准备开始录制')
        m3u8_list = requests.get(
            f'https://api.live.bilibili.com/xlive/web-room/v1/playUrl/playUrl?cid={room_id}&platform=h5&qn=10000')
        m3u8_address = loads(m3u8_list.text)['data']['durl'][0]['url']
        # 下面命令中的timeout单位为微秒,10000000us为10s(https://www.cnblogs.com/zhifa/p/12345376.html)
        command = ['ffmpeg', '-rw_timeout', '10000000', '-timeout', '10000000', '-listen_timeout', '10000000',
                   '-headers',
                   '"Accept: */*? Accept-Encoding: gzip, deflate, br? Accept-Language: zh,zh-TW;q=0.9,en-US;q=0.8,en;'
                   f'q=0.7,zh-CN;q=0.6,ru;q=0.5? Origin: https://live.bilibili.com/{room_id}? '
                   'User-Agent: Mozilla/5.0 (Windows NT 10.0;Win64; x64) '
                   'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36?"', '-i',
                   m3u8_address, '-c:v', 'copy', '-c:a', 'copy', '-bsf:a', 'aac_adtstoasc',
                   '-f', 'segment', '-segment_time', str(segment_time), '-segment_start_number', '1',
                   os.path.join('download', f'[{room_id}]_{get_time()}_part%03d.{file_extensions}'), '-y']
        if debug:
            logger.debug('FFmpeg命令如下 ↓')
            command_str = ''
            for _ in command:
                command_str += _
            logger.debug(command_str)
        p = Popen(command, stdin=PIPE, stdout=PIPE, stderr=STDOUT, shell=False)
        record_status = True
        start_time = last_record_time = get_timestamp()
        try:
            t = threading.Thread(target=record)
            t.start()
            while True:
                if not record_status:
                    break
                if verbose or debug:
                    time.sleep(20)
                    logger.info(f'--==>>> 已录制 {round((get_timestamp() - start_time) / 60, 2)} 分钟 <<<==--')
                else:
                    time.sleep(60)
                    logger.info(f'--==>>> 已录制 {int((get_timestamp() - start_time) / 60)} 分钟 <<<==--')
                if not record_status:
                    break
        except KeyboardInterrupt:
            # p.send_signal(signal.CTRL_C_EVENT)
            logger.info('停止录制,等待ffmpeg退出后本程序会自动退出')
            logger.info('若长时间卡住,请再次按下ctrl+c (可能会损坏视频文件)')
            logger.info('Bye!')
            sys.exit(0)
        kill_times = 0
        logger.info('FFmpeg已退出,重新开始检测直播间')
        # time.sleep(check_time)

if __name__ == '__main__':
    logger.info('B站直播录播姬 By: Red_lnn')
    logger.info('如要停止录制并退出,请按键盘 Ctrl+C')
    logger.info('如要修改录制设置,请以纯文本方式打开.py文件')
    logger.info('准备开始录制...')
    time.sleep(0.3)
    try:
        main()
    except KeyboardInterrupt:
        logger.info('Bye!')
        sys.exit(0)

config.py(配置文件)

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

"""
*------------以下为可配置项-------------*
"""
# room_id = 1151716  # 莴苣某人
# room_id = 1857249  # Red_lnn
room_id = 1151716  # 要录制的B站直播间的直播间ID
segment_time = 3600  # 录播分段时长(单位:秒)
check_time = 60  # 开播检测间隔(单位:秒)
file_extensions = 'flv'  # 录制文件后缀名(文件格式)
verbose = True  # 是否打印ffmpeg输出信息到控制台
debug = False  # 是否显示并保存调试信息(优先级高于 verbose)
save_log = True  # 是否保存日志信息为文件,同一天多次启动本脚本会共用同一个日志文件,每天凌晨分割一次日志文件
"""
*------------以上为可配置项-------------*
"""

以上就是python实现的B站直播录播工具的详细内容,更多关于python B站直播录播的资料请关注我们其它相关文章!

(0)

相关推荐

  • 利用python+ffmpeg合并B站视频及格式转换的实例代码

    利用python+ffmpeg合并B站视频及格式转换 B站客户端下载的视频一般有两种格式:早期的多为blv格式(由flv格式转换而来,音视频轨道在同一文件下). 如今的多为m4s格式,音频轨视频轨分开 以下为利用ffmpeg简单对文件处理,使其转换为大多数播放器能正常播放的mp4格式 前提:已正常安装ffmpeg import tkinter as tk from tkinter import filedialog import os import tkinter.messagebox from

  • 用基于python的appium爬取b站直播消费记录

    基于python的Appium进行b站直播消费记录爬取 之前看文章说fiddler也可以进行爬取,但尝试了一下没成功,这次选择appium进行爬取.类似的,可以运用爬取微信朋友圈和抖音等手机app相关数据 正文 #环境配置参考 前期工作准备,需要安装python.jdk.PyCharm.Appium-windows-x.x.Appium_Python_Client.Android SDK,pycharm可以用anaconda的jupyter来替代 具体可以参考这篇博客,讲的算是很清楚啦 http

  • python 模拟登录B站的示例代码

    需要将模拟的浏览器,添加到环境变量中哦.代码中用的是chrome from selenium import webdriver from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from selenium.webdriv

  • python实现b站直播自动发送弹幕功能

    基本开发环境 · Python 3.6 · Pycharm 相关模块使用 import requests import time from tkinter import * import random 目标i网页分析 首先你要登陆B站账号,然后随便点击一个直播间,这里建议先选择人气少的,弹幕少的,这样方便查看效果 如上图所示,先打开开发者工具,定位到xhr输入发送内容,点击发送,会有一个post请求的send数据接口. 所以只需要请求这个数据接口即可发送弹幕.就是正常的时候爬取数据,使用requ

  • 使用python tkinter开发一个爬取B站直播弹幕工具的实现代码

    项目地址 https://github.com/jonssonyan... 开发工具 python 3.7.9 pycharm 2019.3.5 代码 import threading import time import tkinter.simpledialog from tkinter import END, simpledialog, messagebox import requests class Danmu(): def __init__(self, room_id): # 弹幕url

  • python 爬取B站原视频的实例代码

    B站原视频爬取,我就不多说直接上代码.直接运行就好. B站是把视频和音频分开.要把2个合并起来使用.这个需要分析才能看出来.然后就是登陆这块是比较难的. import os import re import argparse import subprocess import prettytable from DecryptLogin import login '''B站类''' class Bilibili(): def __init__(self, username, password, **

  • Python爬虫自动化爬取b站实时弹幕实例方法

    最近央视新闻记者王冰冰以清除可爱和专业的新闻业务水平深受众多网友喜爱,b站也有很多up主剪辑了关于王冰冰的视频.我们都是知道b站是一个弹幕网站,那你知道如何爬取b站实时弹幕吗?本文以王冰冰视频弹幕为例,向大家介绍Python爬虫实现自动化爬取b站实时弹幕的过程. 1.导入需要的库 import jieba # 分词 from wordcloud import WordCloud # 词云 from PIL import Image # 图片处理 import numpy as np # 图片处理

  • 教你如何使用Python下载B站视频的详细教程

    前言 众所周知,网页版的B站无法下载视频,然本人喜欢经常在B站学习,奈何没有网时,无法观看视频资源,手机下载后屏幕太小又不想看,遂写此程序以解决此问题 步骤 话不多说,进入正题 1.在电脑上下载python的开发环境,点一下,观看具体步骤 2.下载pycharm开发工具,点一下观看具体步骤 3.同时按键盘上的win键与r键,在弹出的对话框中输入cmd 点击确定进入cmd命令行,在里面输入pip install you-get,之后按键盘enter键,进行you-get的下载,下载完后退出cmd

  • ffmpeg+Python实现B站MP4格式音频与视频的合并示例代码

    安装 官网下载 http://ffmpeg.org/ 选择需要的版本 在这个网址下载ffmpeg,https://github.com/BtbN/FFmpeg-Builds/releases 将解压后得到的以下几个文件放置在E:\FFmpeg下 环境变量 此电脑--属性--高级系统设置--环境变量 在系统变量(也就是下面那一半)处找到新建,按如下所示的方法填写 再将%FFMPEG_HOME%以及%FFMPEG_HOME%\bin写入系统变量的Path中 然后一路确定即可 验证 win+R,cmd

  • Python获取B站粉丝数的示例代码

    要使用代码,需要安装Python 3.x,并且要安装库,在cmd输入pip install requests json time 复制代码,修改最上方变量改成你自己的UID,保存为xxx.py,运行就可以了 用于学习了解的核心代码: import requests import json bilibili_api = requests.get("http://api.bilibili.com/x/relation/stat?vmid=1") # 访问网址,数据存到变量,1是用户UID

  • Python基于Tkinter开发一个爬取B站直播弹幕的工具

    简介 使用Python Tkinter开发一个爬取B站直播弹幕的工具,启动后在弹窗中输入房间号即可,弹幕内容会保存在脚本文件同级目录下的.log扩展名的文件中 开发工具 python 3.7.9 pycharm 2019.3.5 实现代码 import threading import time import tkinter.simpledialog # 使用Tkinter前需要先导入 from tkinter import END, messagebox import requests # 全

随机推荐