吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

搜索
查看: 6239|回复: 121
收起左侧

[Python] Python爬取抖音UP主的全部视频

    [复制链接]
panison 发表于 2022-3-28 17:26
本帖最后由 panison 于 2022-3-28 18:42 编辑

说明:
    本人业余中的闲散人员,相关源代码仅交流学习用。
   程序运行时,需要输入抖音UP主的URL。例如:在电脑端,通过官网搜索,可以找到:XXX的主页,https://www.douyin.com/user/xxxxxxxxxxxxxxxxxx

    源代码没有做容错处理,也没有针对抖音的反爬措施进行处理,因此运行中途可能会意外中止。
思路:
    思路其实相差不大,细节处理上有区别。最核心的部分都是解析视频真实URL。
    在编写代码的过程中,遇到了一些困难,通过查找资料,所幸把问题都解决了。

截图:
   

程序运行截图

程序运行截图


源代码:


  
[Python] 纯文本查看 复制代码
import os
import time
import re
from lxml import etree
import requests
from selenium import webdriver


# 定义函数get_video_ids(author_url),返回UP主全部短视频的ID的列表
# 参数author_url:抖音UP主的主页
# 例如,XXX的主页 https://www.douyin.com/user/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
def get_video_ids(author_url):
    ids = list()
    count = 0
    retry = 0
    n = 0
    flag = True
    chrome_option = webdriver.ChromeOptions()
    chrome_option.add_argument('headless')  # 静默模式
    driver = webdriver.Chrome(options=chrome_option)
    driver.get(author_url)
    while flag and retry <= 15:
        driver.execute_script("window.scrollBy(0,2000)")  # scrollBy(x, y),JavaScript中操作内容滚动指定的像素数
        n = n + 1
        time.sleep(2)
        html_source = driver.page_source
        items = etree.HTML(html_source).xpath("//li[@class='ECMy_Zdt']")
        count_items = len(items)
        print("操作页面内容滚动{0:0>3}次后,获取视频ID{1:0>4}个。".format(n, count_items))
        if count_items != count:
            count = count_items
        else:
            if retry < 15:
                retry = retry + 1
            else:
                flag = False
                print("已经达到可获取视频ID的最大数量,开始逐个获取视频ID:\n")
                for item in items:
                    video_id = item.xpath("a/@href")[0].split("/")[-1]
                    print("获取短视频ID:{}".format(video_id))
                    ids.append(video_id)
    return ids


# 定义函数get_video_info(video_id),返回元组(短视频下载地址,短视频标题)
# 参数video_id:抖音短视频唯一ID
def get_video_info(video_id):
    # 通过url0获取json数据(Chrome浏览器,F12进入开发者模式,模拟手机端,可以看到url0)
    url0 = "https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids=" + video_id
    r_url0 = requests.get(url0, headers={"user-agent": "Mozilla/5.0"})
    # 获取json数据中的视频地址及视频标题
    url1 = r_url0.json()["item_list"][0]['video']["play_addr"]["url_list"][0]
    # 防止出现空标题,加上短视频ID
    title = video_id + "-" + r_url0.json()["item_list"][0]['share_info']["share_title"].split("#")[0].split("@")[0]
    # 获取url1重定向后的真实视频地址
    r_url1 = requests.get(url1, headers={"user-agent": "Mozilla/5.0"}, allow_redirects=False)
    url = r_url1.headers['Location']
    return url, title


# 定义函数get_file_name(string),从字符串中提取合法文件名
def get_file_name(string):
    pattern = re.compile(r'[?*/\\|.:><]')
    txt = re.sub(pattern, '', string)
    return txt


# 定义函数download_video(save_path, url, title),下载并以短视频标题作为文件名保存短视频到指定路径
def download_video(save_path, url, title):
    if os.path.exists(save_path):
        pass
    else:
        os.makedirs(save_path)
    with requests.get(url, headers={"user-agent": "Mozilla/5.0"}, stream=True) as r:
        total_size = int(int(r.headers["Content-Length"]) / 1024 + 0.5)
        file_name = get_file_name(title)
        full_path = save_path + get_file_name(title) + ".mp4"
        with open(file=full_path, mode="wb") as f:
            print('当前下载:【{}】,视频文件大小:【{}KB】'.format(file_name, total_size))
            count = 0
            scale = 50
            start = time.perf_counter()
            for chunk in r.iter_content(chunk_size=1024):
                f.write(chunk)
                count = count + 1
                i = int(scale*(count/total_size))
                a = "=" * i
                b = "." * (scale - i)
                c = (i / scale) * 100
                dur = time.perf_counter() - start
                speed = count/dur
                print("\r下载进度:{0:^3.0f}%[{1:}>{2:}] 耗时:{3:.2f}s 平均下载速度:{4:.2f}KB/S。".format(c, a, b, dur, speed), end="")
            print("\n视频文件下载完毕,存放于:【{0:}】。".format(full_path))


# 定义主程序
def main():
    # 获取UP主全部短视频的ID
    url = input("请输入抖音UP主的主页:")
    print("\n获取UP主全部短视频的ID...")
    ids = get_video_ids(url)
    print("获取完毕!共获取短视频ID{}个!".format(len(ids)))

    # 根据短视频ID,批量获取下载地址、短视频标题
    print("\n根据短视频的ID获取短视频的下载地址、标题信息...")
    videos_info = list()
    for video_id in ids:
        video_info = get_video_info(video_id)
        videos_info.append(video_info)
        print("短视频标题:【{0:}】;下载地址:【{1:}】".format(video_info[1], video_info[0]))

    # 批量下载短视频
    print("\n开始批量下载短视频:")
    cwd = os.getcwd()
    path = cwd + "/videos/"
    total = len(videos_info)
    for i in range(total):
        print("\n将下载第【{0:0>4}/{1:0>4}】个短视频:".format(i+1, total))
        print("="*50)
        download_video(path, videos_info[i][0], videos_info[i][1])


if __name__ == "__main__":
    main()


免费评分

参与人数 16吾爱币 +14 热心值 +12 收起 理由
枫叶丷 + 1 我很赞同!
zhaonan007007 + 1 + 1 谢谢@Thanks!
amwkfnui + 1 + 1 我很赞同!
905319129 + 1 + 1 谢谢@Thanks!
kongdang1 + 1 我很赞同!
念天悠 + 1 + 1 谢谢@Thanks!
kc1763 + 1 + 1 热心回复!
小鱼儿、 + 1 我很赞同!
承蒙上苍眷顾 + 1 谢谢@Thanks!
48627913 + 1 + 1 用心讨论,共获提升!
无缺i + 1 + 1 用心讨论,共获提升!
foxking + 1 我很赞同!
paypojie + 1 + 1 我很赞同!
lishufeng7272 + 1 + 1 谢谢@Thanks!
xxxlsy + 1 + 1 热心回复!
嘴角微微上扬 + 1 我很赞同!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

zhaonan007007 发表于 2022-4-19 23:33
牛逼闪闪,我初学Python,能大概看懂代码的逻辑,感觉Python比我之前自学的VBA还简单(虽然只是开始了一丢丢就放弃了……)自己写还需很长一段距离,怎么找到正确的逻辑就更需要琢磨了
不知道是否方便增加两个功能:一个是查重比对的环节,已经下载的视频就不再继续下载了;二是自定义下载路径(可以并分开存放多个up的视频)
我想着功能1的过程是:1把成功下载的视频ID存下来ID.TXT,2下次运行时先看有没有这个ID.TXT,3进行比较,4下载新增的ID对应的视频
功能2的过程是:获取up页面的标题,然后把名字加到路径里,ID.TXT也保存在对应的文件夹下,也根据功能1进行比对
不知道这两个功能是否复杂,不管能不能增加,都再次表示感谢
zhaonan007007 发表于 2022-4-23 14:41
本帖最后由 zhaonan007007 于 2022-4-26 09:22 编辑

一个是查重比对的环节,已经下载的视频就不再继续下载了;二是自定义下载路径(可以并分开存放多个up的视频)
在楼主的基础上更新了一下
[Python] 纯文本查看 复制代码
import osimport time
import re
from lxml import etree
import requests
from selenium import webdriver
import numpy as np

# 定义函数get_video_ids(author_url),返回UP主全部短视频的ID的列表
# 参数author_url:抖音UP主的主页
# 例如,XXX的主页 https://www.douyin.com/user/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
def get_video_ids(author_url):
    ids = list()
    count = 0
    retry = 0
    n = 0
    flag = True
    chrome_option = webdriver.ChromeOptions()
    chrome_option.add_argument('headless')  # 静默模式
    driver = webdriver.Chrome(options=chrome_option)
    driver.get(author_url)
    while flag and retry <= 15:
        driver.execute_script("window.scrollBy(0,2000)")  # scrollBy(x, y),JavaScript中操作内容滚动指定的像素数
        n = n + 1
        time.sleep(2)
        html_source = driver.page_source
        items = etree.HTML(html_source).xpath("//li[@class='ECMy_Zdt']")
        count_items = len(items)
        print("操作页面内容滚动{0:0>3}次后,获取视频ID{1:0>4}个。".format(n, count_items))
        if count_items != count:
            count = count_items
        else:
            if retry < 2:
                retry = retry + 1
            else:
                flag = False
                print("已经达到可获取视频ID的最大数量,开始逐个获取视频ID:\n")
                for item in items:
                    video_id = item.xpath("a/@href")[0].split("/")[-1]
                    print("获取短视频ID:{}".format(video_id))
                    ids.append(video_id)
    return ids

# 定义函数get_author_nickname,得到UP主的抖音名称
# 参考函数get_video_info写的
def get_author_nickname(video_id):
    url_author = "https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids=" + video_id
    url0_author = requests.get(url_author, headers={"user-agent": "Mozilla/5.0"})
    # 获取json数据中的抖音名称
    author_nickname = url0_author.json()["item_list"][0]['author']['nickname']
    return author_nickname

# 定义函数get_video_info(video_id),返回元组(短视频下载地址,短视频标题)
# 参数video_id:抖音短视频唯一ID
def get_video_info(video_id):
    # 通过url0获取json数据(Chrome浏览器,F12进入开发者模式,模拟手机端,可以看到url0)
    url0 = "https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids=" + video_id
    r_url0 = requests.get(url0, headers={"user-agent": "Mozilla/5.0"})
    # 获取json数据中的视频地址及视频标题,replace是用于下载无水印视频
    url1 = r_url0.json()["item_list"][0]['video']["play_addr"]["url_list"][0].replace('/playwm/', '/play/')
    # 防止出现空标题,加上短视频ID
    # 判断首字母是否为#,True保留#后面的内容,False丢弃#后面的内容
    title_temp=r_url0.json()["item_list"][0]['share_info']["share_title"]
    if  title_temp.startswith('#'):
        title = video_id + "-" + title_temp.split("#")[1].split("@")[0]
    else:
        title = video_id + "-" + title_temp.split("#")[0].split("@")[0]
    # 获取url1重定向后的真实视频地址
    r_url1 = requests.get(url1, headers={"user-agent": "Mozilla/5.0"}, allow_redirects=False)
    url = r_url1.headers['Location']
    return url, title


# 定义函数get_file_name(string),从字符串中提取合法文件名
def get_file_name(string):
    #pattern = re.compile(r'[?*/\\|.:><]')
    #txt = re.sub(pattern, '', string)
    #有文章说,上面这块儿代码可以简化为下面这句,增加对空格的替换
    txt = re.sub('[?*/\\|.:>< ]',"",string)
    return txt


# 定义函数download_video(save_path, url, title),下载并以短视频标题作为文件名保存短视频到指定路径
def download_video(save_path, url, title):
    if os.path.exists(save_path):
        pass
    else:
        os.makedirs(save_path)
    with requests.get(url, headers={"user-agent": "Mozilla/5.0"}, stream=True) as r:
        total_size = int(int(r.headers["Content-Length"]) / 1024 + 0.5)
        file_name = get_file_name(title)
        full_path = save_path + get_file_name(title) + ".mp4"
        with open(file=full_path, mode="wb") as f:
            print('当前下载:【{}】,视频文件大小:【{}KB】'.format(file_name, total_size))
            count = 0
            scale = 50
            start = time.perf_counter()
            for chunk in r.iter_content(chunk_size=1024):
                f.write(chunk)
                count = count + 1
                i = int(scale * (count / total_size))
                a = "=" * i
                b = "." * (scale - i)
                c = (i / scale) * 100
                dur = time.perf_counter() - start
                speed = count / dur
                print("\r下载进度:{0:^3.0f}%[{1:}>{2:}] 耗时:{3:.2f}s 平均下载速度:{4:.2f}KB/S。".format(c, a, b, dur, speed), end="")
            print("\n视频文件下载完毕,存放于:【{0:}】。".format(full_path))


# 定义主程序
def main():
    # 获取UP主全部短视频的ID
    url = input("请输入抖音UP主的主页:")
    print("\n获取UP主全部短视频的ID...")
    ids = get_video_ids(url)
    print("获取完毕!共获取短视频ID{}个!".format(len(ids)))

    # 获取UP主nickname
    author_nickname = get_author_nickname(ids[0])

    # 拿ids对比0_ID.txt文件中的数据,输出新的ids_new
    cwd = os.getcwd()
    video_path = cwd + "/" + author_nickname + "/"
    if os.path.exists(video_path):
        pass
    else:
        os.makedirs(video_path)
    author_ID_path = video_path +"0_ID.txt"
    with open(author_ID_path, 'a'):
        if os.path.exists(author_ID_path):
            ids_old = np.loadtxt(author_ID_path, dtype=str)
            ids_new = list(set(ids).difference(set(ids_old)))
        else:
            pass
    # 根据短视频ID,批量获取下载地址、短视频标题
    print("\n根据短视频的ID获取短视频的下载地址、标题信息...")
    videos_info = list()
    for video_id in ids_new:    #把in ids 改为 in ids_new
        video_info = get_video_info(video_id)
        videos_info.append(video_info)
        print("短视频标题:【{0:}】;下载地址:【{1:}】".format(video_info[1], video_info[0]))

    # 批量下载短视频
    print("\n开始批量下载短视频:")
    #cwd = os.getcwd()
    #video_path = cwd + "/" + author_nickname + "/"
    total = len(videos_info)
    for i in range(total):
        print("\n将下载第【{0:0>4}/{1:0>4}】个短视频:".format(i + 1, total))
        print("=" * 50)
        download_video(video_path, videos_info[i][0], videos_info[i][1])
        # 将下载完成的ID写入0_ID.txt
        with open(author_ID_path, 'a') as g:
            g.write(ids_new[i] + '\n')

if __name__ == "__main__":
    main()

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
panison + 1 + 1 谢谢@Thanks!

查看全部评分

zhaonan007007 发表于 2022-4-19 23:58
本帖最后由 zhaonan007007 于 2022-4-20 00:01 编辑

琢磨功能时,搜索到一个代码,可以下载无水印视频
url1 = r_url0.json()["item_list"][0]['video']["play_addr"]["url_list"][0]
在您的这段末尾加上.replace('/playwm/', '/play/')变成
url1 = r_url0.json()["item_list"][0]['video']["play_addr"]["url_list"][0].replace('/playwm/', '/play/')
无水印视频文件大概能小25%
cy465728759 发表于 2022-3-28 17:31
是那种大人看的视频吗
TakeKeyEasy 发表于 2022-3-29 00:08
作者能不能做成桌面程序方便使用一些
by、怒神 发表于 2022-3-28 18:06
cy465728759 发表于 2022-3-28 17:31
是那种大人看的视频吗

小伙子你的思想很危险
xqyangyangv2022 发表于 2022-3-28 18:09
看了能不能学&#128523; 关键搞不会啊
cnjlzhe 发表于 2022-3-28 18:22
挺有用的。
九星辰楪 发表于 2022-3-28 18:28
试试看,最近在学爬虫
独存 发表于 2022-3-28 18:30
感谢分享
傲天越 发表于 2022-3-28 18:41
学习一下,感谢分享!!!
eeeeda 发表于 2022-3-28 18:41
可以爬取其他平台4K视频吗?
 楼主| panison 发表于 2022-3-28 18:43
eeeeda 发表于 2022-3-28 18:41
可以爬取其他平台4K视频吗?

不可以,并不通用。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2022-6-25 18:32

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表