通过selenium和request实现作者视频的批量下载。更新于2023.12.13.

可能会随着时间的代码失效,需要更新请求头和response中url、标题等信息的获取位置。

【请求头中两个参数获取方法】:

打开某条视频->F12->点击media->刷新网页->找到视频元素并点击->复制其中一段文字->

点击搜索->输入搜索信息(这里可以复制元素的URL到浏览器,查看是否能够打开MP4链接)->

在header标头中找到response响应标头->获取其中的cookie和user-agent

【获取标题,时间,作者和视频信息的方法】:

print(response.text)在其中搜索相应信息的位置

然后定位,用(.*?)标识。

【获取视频下载链接url方法】:

将视频信息video_info转码后,用pprint标准化格式输出,寻找其中不同清晰度视频位置,用键值对取值方法定位video_url,之后便可以下载。

【关于脚本运行】先登录后在页面等待刷新出作者所有视频(这一步主要是selenium的爬取,也可以自己手动下拉),之后就是request的工作,因为所有的视频id都已经存到列表里,下一次运行可以将selenium部分省略掉,直接从request部分开始运行。

【关于失败重试】有时候因为网络原因会有几个视频下载失败,会存入失败列表,重新运行程序,把失败列表读进去重新下载就好。

 上代码:

(特别加入了一个时间修正模块,将下载视频的时间修正为发布视频的时间)

# 同时下载视频或图集(主页版,没有发布时间信息)
import os
import winsound

from selenium import webdriver
from selenium.webdriver.common.by import By
import re
from requests import utils
import requests
import json
from datetime import datetime
from pprint import pprint


# 采集作者所有作品的ID的函数
def Get_IDs(her_web):
    # 访问网站
    wb = webdriver.Edge()
    # 输入网址
    wb.get(her_web)
    print('验证')
    input()
    lis = wb.find_elements(By.CSS_SELECTOR, '.Eie04v01')
    print('共采集到', len(lis), '条作品')
    IDs_Video = []
    IDs_Photo = []
    for li in lis:
        ID = li.find_element(By.CSS_SELECTOR, 'a').get_attribute('href')
        if "note" in ID:
            ID = ID.replace('https://www.XXXXXX.com/note/', '')
            IDs_Photo.append(ID)
        elif "video" in ID:
            ID = ID.replace('https://www.XXXXXX.com/video/', '')
            IDs_Video.append(ID)
        else:
            print('出现未定义格式!')

    print(len(IDs_Video), '条视频', '\n   ', IDs_Video)
    # URLs_Video.reverse()
    print(len(IDs_Photo), '条图集', '\n   ', IDs_Photo)

    return IDs_Video, IDs_Photo


# 通过ID获取响应的函数
def request_header(Uid, ID):
    # 请求url
    url = 'https://www.XXXXXX.com/user/' + Uid + '?modal_id=' + ID
    print(url)
    # 加上一个请求头,伪装成浏览器
    headers = {
        # cookie:用户信息,登录或不登录都有
        'cookie': '',#登录后输入自己的cookie
        # user-agent:浏览器信息,版本,电脑
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.36'

    }
    response = requests.get(url, headers=headers)
    return response, headers


# 保存数据的函数
def Download_DY(video_name, video_url, headers):  # 保存数据

    # 获取视频的二进制内容
    video_content = requests.get(url=video_url, headers=headers).content
    with open(video_name, mode='wb') as f:
        f.write(video_content)
        print('下载成功!')


# 替换掉不能命名的字符
def replace_x(words):
    words = words.replace('/', '')
    words = words.replace(':', '')
    words = words.replace('*', '')
    words = words.replace('?', '')
    words = words.replace('〈', '')
    words = words.replace('〉', '')
    words = words.replace('|', '')
    # words = words.replace('"', '')
    # words = words.replace('...', '')
    return words


# 修改时间的函数
def modify_time(video_name, release_time):
    release_time = release_time.replace(' ', 'T')
    a = datetime.strptime(release_time, '%Y-%m-%dT%H:%M')
    a.timestamp()
    os.utime(video_name, (a.timestamp(), a.timestamp()))  # 只能修改 访问时间 与 修改时间(不能修改创建时间)


# 发出提示音
def Sound_beep():
    duration = 1000  # millisecond
    freq = 440  # Hz
    winsound.Beep(freq, duration)


# 主函数
# 文件保存位置
file_path = 'D:\\下载站\\DY下载\\'

# 作者主页
while True:
    print('请输入作者主页')
    her_web = input()
    if her_web == '0':
        break
    Uid = 'MS4wLjABAAAA5ExKqjgHzZ3DaRhImU9EvklLe_9QPeTp0uIuf1kV2h4'

    # 采集作者所有作品的URL
    try:
        (IDs_Video, IDs_Photo) = Get_IDs(her_web)
    except:
        print('获取主页失败,请重试。。。')
        continue
    # IDs_Video.reverse()
    IDs_Photo_Failed = []  # 用于保存失败的URL
    IDs_Video_Failed = []  # 用于保存失败的URL
    # 先获取所有图集
    for ID_Photo in IDs_Photo:
        try:

            # 将作品ID对应的URL请求响应输出(请求头写在函数里)
            (response, headers) = request_header(Uid, ID_Photo)

            # 视频信息
            video_info = re.findall('<script id="RENDER_DATA" type="application/json">(.*?)</script', response.text)[0]
            # 作者姓名
            try:
                name = re.findall('<span class="Nu66P_ba"><span><span><span><span>(.*?)</span>', response.text)[0]
            except:
                name = ''
            # 作品文案
            try:
                title = re.findall('video_title" content="(.*?)"/>', response.text)[0]
            except:
                title = ''

            title = replace_x(title)
            print(title)
            if not title == '':
                title = '-' + title

            video_name = file_path + ID_Photo + title

            # 转码
            html_data = requests.utils.unquote(video_info)
            # 转换数据类型
            json_data = json.loads(html_data)
            # pprint(json_data)

            # 提取视频连接 键值对取值--通过冒号左边的内容,提取冒号右边的内容
            # (这一段还没改)video_url = json_data['app']['videoDetail']['images'][0]['urlList']
            photo_number = int(len(json_data['app']['videoDetail']['images']))
            print('共', photo_number, '张图')
            for i in range(1, photo_number + 1):
                photo_urls = json_data['app']['videoDetail']['images'][i - 1]['urlList'][3]
                # print(photo_urls)
                # 创建文件夹(并且判别文件夹是否存在)
                is_exists = os.path.exists(video_name)  # path是文件夹或者文件的相对路径或者绝对路径
                if is_exists:
                    # print('文件夹已存在')
                    j = 1
                else:
                    # print('文件夹不存在,已创建')
                    os.mkdir(video_name)
                photo_name_i = video_name + '\\' + str(i) + '.jpg'
                # 保存数据
                # 获取视频的二进制内容
                video_content = requests.get(url=photo_urls, headers=headers).content
                with open(photo_name_i, mode='wb') as f:
                    f.write(video_content)
                    print('第', i, '张图片下载成功!')
            # 更正时间
            # modify_time(video_name, release_time)
        except:
            IDs_Photo_Failed.append(ID_Photo)
            print('出现错误,可能导致下载失败!')

    # 再获取所有视频
    for ID_Video in IDs_Video:
        # 将作品ID对应的URL请求响应输出(请求头写在函数里)
        (response, headers) = request_header(Uid, ID_Video)
        # 视频信息

        try:
            video_info = re.findall('<script id="RENDER_DATA" type="application/json">(.*?)</script', response.text)[0]
            # 作者姓名
            try:
                name = re.findall('<span class="Nu66P_ba"><span><span><span><span>(.*?)</span>', response.text)[0]
            except:
                name = ''

            # 作品文案
            try:
                title = re.findall('video_title" content="(.*?)"/>', response.text)[0]
            except:
                title = ''

            title = replace_x(title)
            print(title)
            if not title == '':
                title = '-' + title

            video_name = file_path + ID_Video + title + '.mp4'
            # 转码
            html_data = requests.utils.unquote(video_info)
            # 转换数据类型
            json_data = json.loads(html_data)
            # print(type(html_data))
            # print(type(json_data))
            # pprint(json_data)
            # 提取视频连接 键值对取值--通过冒号左边的内容,提取冒号右边的内容
            video_url = 'https:' + json_data['app']['videoDetail']['video']['bitRateList'][0]['playApi']
            print(video_url)

            # 下载视频
            Download_DY(video_name, video_url, headers)
            # 更正时间
            # modify_time(video_name, release_time)
        except:
            IDs_Video_Failed.append(ID_Video)
            print('出现错误,可能导致下载失败!')
    print('视频失败列表:', '\n', IDs_Video_Failed)
    print('图集失败列表:', '\n', IDs_Photo_Failed)
    Sound_beep()

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐