python3GUI--抖音无水印视频下载工具(附源码)

本文阅读 3 分钟
首页 安全分享,WEB安全 正文

本次要用到以下依赖库:re json os random tkinter threading requests pillow 其中后两个需要安装后使用

0.复制抖音分享短链接

img

1.启动

img

2.运行

img

3.结果

img

(小姐姐挺漂亮,视频确实无水印)

设计流程分为总体设计和详细设计,这里我会使用viso画出几个流程图,用以展示我的思路,详细设计部分列举了两个函数实现的具体流程。

1.总体设计

img

2.详细设计

2.1 analysis_real_addr()函数

img

2.2 download_video()函数 img

4.1 DouYin_Video_Downloader-v2.0.py

#--*coding:utf-8*--
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
import os
import threading
from PIL import Image,ImageTk
from DouYin_Downloader_Engine import Douyin_Engine
''' 抖音无水印视频下载工具 难点:1.视频接口 2.进度条 V2.0改进: '''

imgs=['./rely/dy_logo.png','./rely/my_favicon.ico']
class App:
    def __init__(self):
        self.video_path = './video/'
        self.window = Tk()
        self.window.title('Downloader-v2.0')
        width = 295
        height = 400
        screenWidth = self.window.winfo_screenwidth()  # 获取显示区域的宽度
        screenHeight = self.window.winfo_screenheight()  # 获取显示区域的高度
        left = (screenWidth - width) / 2
        top = (screenHeight - height) / 2
        self.window.geometry("%dx%d+%d+%d" % (width, height, left, top))
        self.window.resizable(0, 0)
        self.window.iconbitmap(imgs[1])
        self.create_widget()
        self.set_widget()
        self.place_widget()
        self.window.mainloop()

    def create_widget(self):
        self.paned=PanedWindow(self.window)
        self.img=imgs
        photo = Image.open(imgs[0])
        photo = photo.resize((200, 50))
        self.paned.image = ImageTk.PhotoImage(photo)
        self.l0 = ttk.Label(self.window, imag=self.paned.image, justify='center')
        self.f1 = ttk.Labelframe(self.window, text='视频链接地址:')
        self.e1 = ttk.Entry(self.f1, width=30)
        self.f2 = ttk.Labelframe(self.window, text='信息:')
        self.t1 = Text(self.f2, height=6, width=30)
        self.b1 = ttk.Button(self.window, text='解析',)
        self.b2 = ttk.Button(self.window, text='下载')
        self.b3 = ttk.Button(self.window, text='打开文件夹')
        self.b4 = ttk.Button(self.window, text='退出')
        self.f3 = ttk.Labelframe(self.window)
        self.l1 = ttk.Label(self.f3, text=' 敬告:本软件仅供学习交流使用!')
        self.f4 = ttk.LabelFrame(self.window, text='下载进度:')
        self.canvas = Canvas(self.f4,  bg="white")
        self.l2_var = StringVar()
        self.l2 = ttk.Label(self.f4, textvariable=self.l2_var, text='未下载')
        self.m = Menu(self.window)

    def place_widget(self):
        self.l0.pack()
        self.l1.pack(fill=X)
        self.f1.place(x=40, y=55)
        self.e1.pack()
        self.f2.place(x=40, y=102)
        self.t1.pack()
        self.b1.place(x=40, y=265)
        self.b2.place(x=170, y=265)
        self.b3.place(x=40, y=298)
        self.b4.place(x=170, y=298)
        self.f3.place(x=40, y=329)
        self.f4.place(x=40, y=210)
        self.canvas.pack(side='left')
        self.l2.pack(side='left', anchor=S)

    def set_widget(self):
        self.l1.config( width=30, justify='center', foreground='red')
        self.t1.insert(END, '{_xing}\n将抖音分享链接地址粘贴在上面输入框中,本程序会自动解析出视频的下载地址并且显示在这里\n{_xing}'.format(_xing='*' * 29))
        self.b1.config( command=lambda: self.thread_it(self.analysis_video))
        self.b2.config(state='disable', command=lambda: self.thread_it(self.download_video))
        self.b3.config(command=self.open_dir)
        self.b4.config( command=self.quit_window)
        self.canvas.config(width=210, height=20)
        self.window['menu'] = self.m
        self.s1 = Menu(self.m, tearoff=False)
        self.s2 = Menu(self.m, tearoff=False)
        self.s3=Menu(self.m,tearoff=False)
        self.s1.add_command(label='打开文件夹', command=self.open_dir)
        self.s1.add_separator()
        self.s1.add_command(label='退出', command=self.quit_window)
        self.s2.add_command(label='解析', command=self.analysis_video)
        self.s2.add_command(label='下载', command=self.download_video,state='disable')
        self.s3.add_command(label='关于',command=self.show_info)
        self.m.add_cascade(label='文件', menu=self.s1)
        self.m.add_cascade(label='视频', menu=self.s2)
        self.m.add_cascade(label='关于',menu=self.s3)
        self.window.protocol("WM_DELETE_WINDOW",self.quit_window)
        self.window.bind('<Escape>',self.escape)
        self.window.bind('<Return>',lambda :self.thread_it(self.download_video))

    def analysis_video(self):
        src_link=self.e1.get()
        if len(src_link)!=0:
            global item
            item=Douyin_Engine().analysis_real_addr(src_link)
            if item:
                self.t1.delete(0.0,END)
                self.t1.insert(END,item['video_title']+"\n"+item["video_link"])
                self.b2.config(state=NORMAL)
                self.s2.entryconfig('下载',state='normal')
            else:
                messagebox.showwarning('警告','解析失败!')
            print(item)
        else:
            messagebox.showwarning('警告','请输入链接!')

    def download_video(self):
        try:
            os.mkdir(self.video_path)
        except:
            pass
        video_title=item['video_title']
        full_path=self.video_path+video_title+'.mp4'
        fill_line = self.canvas.create_rectangle(1.5, 1.5, 0, 23, width=0, fill="green")
        for n in Douyin_Engine().download_video(full_path,item['video_link']):
            self.canvas.coords(fill_line, (0, 0, n, 60))
            self.window.update()
        self.t1.delete('0.0', END)
        self.t1.insert(END, '{title}.mp4\n下载完成!'.format(title=video_title))

    # 打开文件夹函数
    def open_dir(self):
        try:
            os.mkdir(self.video_path)
        except:
            pass
        abs_path = os.path.abspath(self.video_path)
        os.startfile(abs_path)

    def escape(self,event):
        self.quit_window()

    # 退出窗口函数
    def quit_window(self):
        ret=messagebox.askyesno('退出','是否要退出?')
        if ret:
            self.window.destroy()

    def show_info(self):
        messagebox.showinfo('关于','***本软件仅供学习交流使用!***\n\n将抖音分享链接地址粘贴在输入框中\n解析后显示视频的无水印下载地址')

    # 函数打包进线程
    def thread_it(self,func, *args):
        t = threading.Thread(target=func, args=args)
        t.setDaemon(True)  # 先守护主线程
        t.start()  # 再启动

if __name__ == '__main__':
    app=App()
''' test_url: https://v.douyin.com/JcxTMj2/ '''

4.2 DouYin_Downloader_Engine.py

import requests
import json
import re

class Douyin_Engine:

    def clean_txt(self, str):  # 清洗标题中不能用于命名文件的字符
        rstr = r"[\/\\\:\*\?\"\<\>\|]"  # '/ \ : * ? " < > |'
        str = re.sub(rstr, "_", str)  # 替换为下划线
        return str

    def analysis_real_addr(self,src_link):
        item={ }
        headers = { 
            'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
            'sec-fetch-dest': 'document',
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.104 Safari/537.36'
        }
        try:
            r=requests.get(src_link,headers=headers,allow_redirects=False)
            item_id=re.findall(r'/video/(\d+)/?',r.text)
            interface_url='https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids={}'.format(''.join(item_id))
            r2=requests.get(interface_url,headers=headers)
            _json=json.loads(r2.text)
            title=_json.get('item_list')[0].get('share_info').get('share_title')
            wm_link=_json.get('item_list')[0].get('video').get('play_addr').get('url_list')[0]
            item['video_title']=self.clean_txt(title)
            item['video_link']=re.sub('playwm','play',wm_link)
            return item
        except :
            return False

    def download_video(self,file,link):
        headers = { 
            'sec-fetch-dest': 'document',
            'user-agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.104 Mobile Safari/537.36'
        }
        r=requests.get(link,headers=headers,stream=True)
        chunk_size=1024
        video_size=int(r.headers['Content-Length'])
        raise_data = 210 / (video_size / chunk_size)  # 增量大小,210为进度条的长度
        with open(file,'wb')as f:
            size_ = 0
            for data in r.iter_content(chunk_size):
                size_+=raise_data
                f.write(data)
                yield size_

5.1说明:

5.2总结: 本次使用python的tkinter撰写了一个抖音无水印视频爬取GUI工具,因为中间写过几个GUI界面了对tkinter的widget有些熟悉了,线程用起来更加熟练。他山之石,可以攻玉,本次思路、代码的撰写参考了:

tkinter实现下载进度条(python)

以上帮助我解决了进度条问题。

最新版抖音(20200624)去水印原理及源码,简单的原理与面临的挑战

以上帮助我解决了视频接口获取问题。 本程序做到了UI界面与爬虫分开。

在此感谢帮助我的人!,工具已经打包上传到了蓝奏云 https://wws.lanzoux.com/isCLul0dlfa, 思路、代码方面有什么不足欢迎各位大佬指正、批评!觉得工具还行的话点个赞呗! img

本文为互联网自动采集或经作者授权后发布,本文观点不代表立场,若侵权下架请联系我们删帖处理!文章出自:https://blog.csdn.net/a1397852386/article/details/113357965
-- 展开阅读全文 --
BUUCTF Web [极客大挑战 2019]Knife
« 上一篇 06-24
安全面试之XSS(跨站脚本攻击)
下一篇 » 07-24

发表评论

成为第一个评论的人

热门文章

标签TAG

最近回复