python爬取B站视频

news/2024/7/19 10:18:32 标签: python, 爬虫, B站, 去水印

参考:https://cloud.tencent.com/developer/article/1768680

参考的代码有点问题,请求头需要修改,上代码:

python">import requests
import re  # 正则表达式
import pprint
import json
from moviepy.editor import AudioFileClip, VideoFileClip
from bs4 import BeautifulSoup as bs

headers = {
    # 防盗链 告诉服务器 我们请求的url网址是从哪里跳转过来的
    'referer': 'https://www.bilibili.com/a',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'
}

def send_request(url):
    response = requests.get(url=url, headers=headers)
    return response

def get_video_data(html_data):
    """解析视频数据"""

    # 提取视频的标题
    soup = bs(html_data, 'lxml')
    title = soup.find_all(name='h1',attrs={"class":"video-title special-text-indent"})[0].get_text()
    # print(title)

    # 提取视频对应的json数据
    json_data = re.findall('<script>window\.__playinfo__=(.*?)</script>', html_data)[0]
    # print(json_data)  # json_data 字符串
    json_data = json.loads(json_data)
    pprint.pprint(json_data)

    # 提取音频的url地址
    audio_url = json_data['data']['dash']['audio'][0]['backupUrl'][0]
    print('解析到的音频地址:', audio_url)

    # 提取视频画面的url地址
    video_url = json_data['data']['dash']['video'][0]['backupUrl'][0]
    print('解析到的视频地址:', video_url)

    video_data = [title, audio_url, video_url]
    return video_data

def save_data(file_name, audio_url, video_url):
    # 请求数据
    print('正在请求音频数据')
    audio_data = send_request(audio_url).content
    print('正在请求视频数据')
    video_data = send_request(video_url).content
    with open(file_name + '.mp3', mode='wb') as f:
        f.write(audio_data)
        print('正在保存音频数据')
    with open(file_name + '.mp4', mode='wb') as f:
        f.write(video_data)
        print('正在保存视频数据')

def merge_data(video_name):
    print('视频合成开始:', video_name)
    audioclip = AudioFileClip(video_name+'.mp3')
    videoclip = VideoFileClip(video_name+'.mp4')
    # 3.获取视频和音频的时长
    video_time = videoclip.duration
    audio_time = audioclip.duration
    # 4.对视频或者音频进行裁剪
    if video_time > audio_time:
        # 视频时长>音频时长,对视频进行截取
        videoclip_new = videoclip.subclip(0, audio_time)
        audioclip_new = audioclip
    else:
        # 音频时长>视频时长,对音频进行截取
        videoclip_new = videoclip
        audioclip_new = audioclip.subclip(0, video_time)
    # 5.视频中加入音频
    video_with_new_audio = videoclip_new.set_audio(audioclip_new)
    # 6.写入到新的视频文件中
    video_with_new_audio.write_videofile("output.mp4",
                                         codec='libx264',
                                         audio_codec='aac',
                                         temp_audiofile='temp-video.m4a',
                                         remove_temp=True
                                         )
    print('视频合成结束:', video_name)


url = 'https://www.bilibili.com/video/BV1bK421a7qG/?spm_id_from=333.1007.tianma.6-4-22.click'
response = send_request(url)
response.encoding = requests.utils.get_encodings_from_content(response.text)[0]
html_data = response.text
video_data = get_video_data(html_data)
save_data(video_data[0], video_data[1], video_data[2])
merge_data(video_data[0])

效果

小姐姐挺靓,就是左下角有水印,想办法去除水印,参考:python实战之去除视频水印&字幕_python 去除视频水印-CSDN博客

python">import os
import sys
import cv2
import numpy
from moviepy import editor
 
TEMP_VIDEO = 'temp.mp4'
 
 
class WatermarkRemover():
 
    def __init__(self, video_path, output, threshold: int, kernel_size: int):
        self.threshold = threshold  # 阈值分割所用阈值
        self.kernel_size = kernel_size  # 膨胀运算核尺寸
        self.video_path = video_path
        self.output = output
 
 
    #根据用户手动选择的ROI(Region of Interest,感兴趣区域)框选水印或字幕位置。
    def select_roi(self, img: numpy.ndarray, hint: str) -> list:
        '''
    框选水印或字幕位置,SPACE或ENTER键退出
    :param img: 显示图片
    :return: 框选区域坐标
    '''
        COFF = 0.7
        w, h = int(COFF * img.shape[1]), int(COFF * img.shape[0])
        resize_img = cv2.resize(img, (w, h))
        roi = cv2.selectROI(hint, resize_img, False, False)
        cv2.destroyAllWindows()
        watermark_roi = [int(roi[0] / COFF), int(roi[1] / COFF), int(roi[2] / COFF), int(roi[3] / COFF)]
        return watermark_roi
 
 
    #对输入的蒙版进行膨胀运算,扩大蒙版的范围
    def dilate_mask(self, mask: numpy.ndarray) -> numpy.ndarray:
 
        '''
    对蒙版进行膨胀运算
    :param mask: 蒙版图片
    :return: 膨胀处理后蒙版
    '''
        kernel = numpy.ones((self.kernel_size, self.kernel_size), numpy.uint8)
        mask = cv2.dilate(mask, kernel)
        return mask
    
    #根据手动选择的ROI区域,在单帧图像中生成水印或字幕的蒙版。
    def generate_single_mask(self, img: numpy.ndarray, roi: list, threshold: int) -> numpy.ndarray:
        '''
    通过手动选择的ROI区域生成单帧图像的水印蒙版
    :param img: 单帧图像
    :param roi: 手动选择区域坐标
    :param threshold: 二值化阈值
    :return: 水印蒙版
    '''
        # 区域无效,程序退出
        if len(roi) != 4:
            print('NULL ROI!')
            sys.exit()
 
        # 复制单帧灰度图像ROI内像素点
        roi_img = numpy.zeros((img.shape[0], img.shape[1]), numpy.uint8)
        start_x, end_x = int(roi[1]), int(roi[1] + roi[3])
        start_y, end_y = int(roi[0]), int(roi[0] + roi[2])
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        roi_img[start_x:end_x, start_y:end_y] = gray[start_x:end_x, start_y:end_y]
 
        # 阈值分割
        _, mask = cv2.threshold(roi_img, threshold, 255, cv2.THRESH_BINARY)
        return mask
 
    #通过截取视频中多帧图像生成多张水印蒙版,并通过逻辑与计算生成最终的水印蒙版
    def generate_watermark_mask(self, video_path: str) -> numpy.ndarray:
        '''
    截取视频中多帧图像生成多张水印蒙版,通过逻辑与计算生成最终水印蒙版
    :param video_path: 视频文件路径
    :return: 水印蒙版
    '''
        video = cv2.VideoCapture(video_path)
        success, frame = video.read()
        roi = self.select_roi(frame, 'select watermark ROI')
        mask = numpy.ones((frame.shape[0], frame.shape[1]), numpy.uint8)
        mask.fill(255)
 
        step = video.get(cv2.CAP_PROP_FRAME_COUNT) // 5
        index = 0
        while success:
            if index % step == 0:
                mask = cv2.bitwise_and(mask, self.generate_single_mask(frame, roi, self.threshold))
            success, frame = video.read()
            index += 1
        video.release()
 
        return self.dilate_mask(mask)
 
    #根据手动选择的ROI区域,在单帧图像中生成字幕的蒙版。
    def generate_subtitle_mask(self, frame: numpy.ndarray, roi: list) -> numpy.ndarray:
        '''
    通过手动选择ROI区域生成单帧图像字幕蒙版
    :param frame: 单帧图像
    :param roi: 手动选择区域坐标
    :return: 字幕蒙版
    '''
        mask = self.generate_single_mask(frame, [0, roi[1], frame.shape[1], roi[3]], self.threshold)  # 仅使用ROI横坐标区域
        return self.dilate_mask(mask)
 
    def inpaint_image(self, img: numpy.ndarray, mask: numpy.ndarray) -> numpy.ndarray:
        '''
    修复图像
    :param img: 单帧图像
    :parma mask: 蒙版
    :return: 修复后图像
    '''
        telea = cv2.inpaint(img, mask, 1, cv2.INPAINT_TELEA)
        return telea
 
 
    def merge_audio(self, input_path: str, output_path: str, temp_path: str):
        '''
    合并音频与处理后视频
    :param input_path: 原视频文件路径
    :param output_path: 封装音视频后文件路径
    :param temp_path: 无声视频文件路径
    '''
        with editor.VideoFileClip(input_path) as video:
            audio = video.audio
            with editor.VideoFileClip(temp_path) as opencv_video:
                clip = opencv_video.set_audio(audio)
                clip.to_videofile(output_path)
 
    def remove_video_watermark(self):
        '''
    去除视频水印
    '''
        if not os.path.exists(self.output):
            os.makedirs(self.output)
 
        filenames = [os.path.join(self.video_path, i) for i in os.listdir(self.video_path)]
        mask = None
 
        for i, name in enumerate(filenames):
            if i == 0:
                # 生成水印蒙版
                mask = self.generate_watermark_mask(name)
 
            # 创建待写入文件对象
            video = cv2.VideoCapture(name)
            fps = video.get(cv2.CAP_PROP_FPS)
            size = (int(video.get(cv2.CAP_PROP_FRAME_WIDTH)), int(video.get(cv2.CAP_PROP_FRAME_HEIGHT)))
            video_writer = cv2.VideoWriter(TEMP_VIDEO, cv2.VideoWriter_fourcc(*'mp4v'), fps, size)
 
            # 逐帧处理图像
            success, frame = video.read()
 
            while success:
                frame = self.inpaint_image(frame, mask)
                video_writer.write(frame)
                success, frame = video.read()
 
            video.release()
            video_writer.release()
 
            # 封装视频
            (_, filename) = os.path.split(name)
            output_path = os.path.join(self.output, filename.split('.')[0] + '_no_watermark.mp4')  # 输出文件路径
            self.merge_audio(name, output_path, TEMP_VIDEO)
 
    if os.path.exists(TEMP_VIDEO):
        os.remove(TEMP_VIDEO)
 
    def remove_video_subtitle(self):
        '''去除视频字幕'''
        if not os.path.exists(self.output):
            os.makedirs(self.output)
 
        filenames = [os.path.join(self.video_path, i) for i in os.listdir(self.video_path)]
        roi = []
 
        for i, name in enumerate(filenames):
            # 创建待写入文件对象
            video = cv2.VideoCapture(name)
            fps = video.get(cv2.CAP_PROP_FPS)
            size = (int(video.get(cv2.CAP_PROP_FRAME_WIDTH)), int(video.get(cv2.CAP_PROP_FRAME_HEIGHT)))
            video_writer = cv2.VideoWriter(TEMP_VIDEO, cv2.VideoWriter_fourcc(*'mp4v'), fps, size)
 
            # 逐帧处理图像
            success, frame = video.read()
            if i == 0:
                roi = self.select_roi(frame, 'select subtitle ROI')
 
            while success:
                mask = self.generate_subtitle_mask(frame, roi)
                frame = self.inpaint_image(frame, mask)
                video_writer.write(frame)
                success, frame = video.read()
 
            video.release()
            video_writer.release()
 
            # 封装视频
            (_, filename) = os.path.split(name)
            output_path = os.path.join(OUTPUT_PATH, filename.split('.')[0] + '_no_sub.mp4')  # 输出文件路径
            self.merge_audio(name, output_path, TEMP_VIDEO)
 
        if os.path.exists(TEMP_VIDEO):
            os.remove(TEMP_VIDEO)
 
 # 去水印
video_path = 'video'
output_path = 'output'
remover = WatermarkRemover(video_path,output_path,threshold=80, kernel_size=5)
remover.remove_video_watermark()   
#去字幕
# remover = WatermarkRemover(video_path,output_path,threshold=80, kernel_size=5)
# remover.remove_video_subtitle()

效果一般吧:


http://www.niftyadmin.cn/n/5462477.html

相关文章

网络类型整理

1、点到点 &#xff1a;在一个网段内只能存在&#xff0c;两个物理节点 MA-多路访问 -- 在一个网段内物理节点的数量不限制 MA--- BMA NBMA 2、BMA -- 广播型多路访问 3、NBMA--非广播型多路访问 注&#xff1a;不同网络类型实际为不同的数据链路层技术&#xff1b;由于二…

C++之STL的algorithm(1)之遍历算法(for_each、transform)整理、函数对象与谓词回顾

C之STL的algorithm&#xff08;1&#xff09;之遍历算法&#xff08;for_each、transform&#xff09;整理、函数对象与谓词回顾 注&#xff1a;整理一些突然学到的C知识&#xff0c;随时mark一下 例如&#xff1a;忘记的关键字用法&#xff0c;新关键字&#xff0c;新数据结构…

VS Code常用前端开发插件和基础配置

VS Code插件安装 VS Code提供了非常丰富的插件功能&#xff0c;根据你的需要&#xff0c;安装对应的插件可以大大提高开发效率。 完成前端开发&#xff0c;常见插件介绍&#xff1a; 1、Chinese (Simplified) Language Pack 适用于 VS Code 的中文&#xff08;简体&#xff…

MySQL中匹配年月问题

一般数据库中给到的时间都是年-月-日形式的&#xff0c;那怎么匹配年-月/的形式&#xff1f; 如2021年8月怎么写&#xff08;怎么在数据库中查询到关于2021年8月的数据&#xff09;&#xff1a; 法一&#xff1a;使用month()函数和year()函数 select字段列表 from 表名 wher…

Redis 全景图(3)--- Redis 应用于缓存

前言 这是关于 Redis 全景图的最后一篇文章。因为一次写太多会限流&#xff0c;我也是没办法&#xff0c;才分成三篇文章来写。这篇文章是关于 Redis 应用于缓存的。 其实为什么要讲这个话题呢&#xff1f; Redis 应用在很多地方呀&#xff0c;为什么一定要挑着这个话题来讲呢…

Electron安全防护实战:应对常见安全问题及权限控制措施

Electron安全防护实战&#xff1a;应对常见安全问题及权限控制措施 引言常见安全问题及其危害提升 Electron 应用安全性的措施限制渲染进程权限防止XSS与内容注入加固应用更新流程严格管理硬件权限使用安全的第三方模块加密敏感数据存储实现进程间通信&#xff08;IPC&#xff…

sed语句应用

sed语句应用 基本sed命令 替换 替换命令应用于与 address 匹配的行。如果没有指定地址&#xff0c;那么就应用于Pattern 匹配的所有行。如果正则表达式作为地址来提供&#xff0c;并且没有指定模式&#xff0c;那么替换命令匹配由地址匹配的内容。当替换命令是应用于同一个地…

5.vector容器的使用

文章目录 vector容器1.构造函数代码工程运行结果 2.赋值代码工程运行结果 3.容量和大小代码工程运行结果 4.插入和删除代码工程运行结果 5.数据存取工程代码运行结果 6.互换容器代码工程运行结果 7.预留空间代码工程运行结果 vector容器 1.构造函数 /*1.默认构造-无参构造*/ …