阿里云

120行代码下载抖音无水印视频「Python语言」

过年在家没什么事情做,一直在刷抖音,就想写个代码,试着去下载抖音的原视频文件,昨天写了一会,在下载上面出现了问题,没有成功的下载视频文件。今天上午又研究了一下。成功实现了下载抖音无水印视频文件。整体代码120行。下面一起来看一下吧!

一、需要手机上下载抖音

打开抖音随便找一个视频,点击分享按钮,将其分享到微信,我们要获取到分享的内容,如图一。

1.png

二、获取抖音短链接

将图一中的链接地址提取出来,这里就手动提取一下吧,不用代码执行了。留着备用,可以保存到文本文件中,方便后续的粘贴。

三、实现过程

通过分享的内容,提取出抖音的短链接,我们在浏览器上实际打开这个链接,会看到这是一个视频的播放页面,这个页面的视频文件也是可以进行下载的,但是这样的下载是存在水印的,来分析一下源代码,发现有一段Javascript代码中,出现了视频的播放路径,如图二。

2.png

将参数地址在浏览器上进行打开,发现是视频的播放地址,我们继而来观察一下这个播放地址,同时我们在下载的时候可以下载到无水印的视频文件,那么服务器上面肯定存在无水印的视频源文件,水印一词:watermark,猜想一下,将链接中的playwm是不是播放地址加水印的呢?那么我们就将wm进行去掉,发现地址仍可以播放,但是必须是在移动设备端,当我们在pc设备端时就是白屏的情况。拿到了视频的存储位置,接下来就是对文件进行下载了。需要模拟移动设备请求视频地址,然后对文件进行下载。

四、代码(详细代码见评论,已上传至百度网盘分享)

# -*- coding: utf-8 -*-
# 下载抖音无水印视频
from bs4 import BeautifulSoup
import requests
import re
import os
import time


def main():
    print("抖音短视频,无水印素材下载")
    url = input("请输入链接地址:")
    article = get_code(url)
    pattern = re.compile('s_vid=([a-z0-9]+)&line=0')
    script = article.find("script", text=pattern)
    s_vid = pattern.search(script.text).group(1)
    # 拼合链接
    video_url = 'https://aweme.snssdk.com/aweme/v1/play/?s_vid=%s&line=0' % s_vid
    res_url = get_redirect_url(video_url)
    # 视频名称
    video_name_data = get_true_name(url)
    # 进行下载
    print("正在下载中~~~~")
    do_load_media(res_url, './video/%s.mp4' % video_name_data)
    print("运行完成")


# 获取源代码
def get_code(url):
    if not url:
        url = input("请输入链接地址:")
    # 获取响应码
    res = requests.get(url)
    code = res.status_code
    if code != 200:
        print('无法响应')
    # 获取页面代码
    headers = {
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
        "Cookie": "anonymid=j3jxk555-nrn0wh; _r01_=1; _ga=GA1.2.1274811859.1497951251; _de=BF09EE3A28DED52E6B65F6A4705D973F1383380866D39FF5; ln_uact=mr_mao_hacker@163.com; depovince=BJ; jebecookies=54f5d0fd-9299-4bb4-801c-eefa4fd3012b|||||; JSESSIONID=abcI6TfWH4N4t_aWJnvdw; ick_login=4be198ce-1f9c-4eab-971d-48abfda70a50; p=0cbee3304bce1ede82a56e901916d0949; first_login_flag=1; ln_hurl=http://hdn.xnimg.cn/photos/hdn421/20171230/1635/main_JQzq_ae7b0000a8791986.jpg; t=79bdd322e760beae79c0b511b8c92a6b9; societyguester=79bdd322e760beae79c0b511b8c92a6b9; id=327550029; xnsid=2ac9a5d8; loginfrom=syshome; ch_id=10016; wp_fold=0"
    }
    content = requests.get(url, headers=headers, timeout=3000)
    # 设置编码格式
    content.coding = 'UTF-8'
    # 以文本形式获取源码
    content_text = content.text
    # 利用解析器进行解析操作
    article = BeautifulSoup(content_text, "html.parser")
    return article


# 下载视频
def do_load_media(url, save_path):
    try:
        headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) '
                          'Chrome/71.0.3578.98 Safari/537.36'}
        pre_content_length = 0
        # 循环接收视频数据
        while True:
            # 若文件已经存在,则断点续传,设置接收来需接收数据的位置
            if os.path.exists(save_path):
                headers['Range'] = 'bytes=%d-' % os.path.getsize(save_path)
            res = requests.get(url, stream=True, headers=headers)

            content_length = int(res.headers['content-length'])
            # 若当前报文长度小于前次报文长度,或者已接收文件等于当前报文长度,则可以认为视频接收完成
            if content_length < pre_content_length or (
                    os.path.exists(save_path) and os.path.getsize(save_path) == content_length) or content_length == 0:
                break
            pre_content_length = content_length

            # 写入收到的视频数据
            with open(save_path, 'ab') as file:
                file.write(res.content)
                file.flush()
                print('下载成功:文件大小 : %s  总下载大小:%s' % (StrOfSize(os.path.getsize(save_path)), StrOfSize(content_length)))
    except Exception as e:
        print(e)


# 文件大小转化
def StrOfSize(size):
    '''
    auth: wangshengke@kedacom.com ;科达柯大侠
    递归实现,精确为最大单位值 + 小数点后三位
    '''

    def strofsize(integer, remainder, level):
        if integer >= 1024:
            remainder = integer % 1024
            integer //= 1024
            level += 1
            return strofsize(integer, remainder, level)
        else:
            return integer, remainder, level

    units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
    integer, remainder, level = strofsize(size, 0, 0)
    if level + 1 > len(units):
        level = -1
    return ('{}.{:>03d} {}'.format(integer, remainder, units[level]))


# 重定向的链接,模拟安卓请求地址
def get_redirect_url(url):
    header = {
        'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
        'Upgrade-Insecure-Requests': '1',
    }
    data = requests.get(headers=header, url=url, timeout=5)
    return data.url


# 获取名称
def get_true_name(url):
    true_url = get_redirect_url(url)
    article = get_code(true_url)
    name = article.find(class_="desc")
    if name:
        return name.string
    else:
        return time.time()


if __name__ == '__main__':
    main()

五、代码解读

代码中的视频下载方法、文件大小转换方法均来源于网络,其实整个过程并不复杂,主要是分析抖音播放的代码和请求,通过模拟方式分步骤进行获取参数,最终获取到无水印视频的播放地址,这个地址可以通过其他的下载工具进行下载,抖音视频文件时间在60s之内,文件也不是很大,默认代码下载的方式也不会太耗费时间。


喜欢就点个赞吧