手把手教你抖音系列視頻批量下載器開發

📢博客主頁:https://blog.csdn.net/as604049322

📢本文由 小小明 - 代碼實體 原創,首發於 CSDN🙉

程序使用演示

大家好,我是小小明。這裏開發了一個抖音視頻下載器,打開效果如下:

如果本地的谷歌遊覽器之前從來沒有訪問過抖音主頁,點擊開始下載按鈕會有如下輸出:

此時我們只需要點擊 訪問抖音主頁,程序則會使用本地的谷歌遊覽器訪問抖音主頁。再次點擊下載按鈕:

可以看到該視頻是一個合集視頻:

那麼程序只需要勾選第一個選項即可下載整個合集:

這樣一次性就將整個合集都下載下來了:

開發流程

首先根據上一篇文章:提取谷歌遊覽器 Cookie 的五重境界

讀取谷歌遊覽器安裝的位置和本地抖音相關的 cookie:

"""
小小明的代碼
CSDN主頁:https://blog.csdn.net/as604049322
"""
__author__ = '小小明'
__time__ = '2022/1/23'

import base64
import json
import os
import sqlite3
import winreg

import win32crypt
from cryptography.hazmat.primitives.ciphers.aead import AESGCM


def load_local_key(localStateFilePath):
    "讀取chrome保存在json文件中的key再進行base64解碼和DPAPI解密得到真實的AESGCM key"
    with open(localStateFilePath, encoding='u8') as f:
        encrypted_key = json.load(f)['os_crypt']['encrypted_key']
    encrypted_key_with_header = base64.b64decode(encrypted_key)
    encrypted_key = encrypted_key_with_header[5:]
    key = win32crypt.CryptUnprotectData(encrypted_key, None, None, None, 0)[1]
    return key


def decrypt_value(key, data):
    "AESGCM解密"
    nonce, cipherbytes = data[3:15], data[15:]
    aesgcm = AESGCM(key)
    plaintext = aesgcm.decrypt(nonce, cipherbytes, None).decode('u8')
    return plaintext


def fetch_host_cookie(host):
    "獲取指定域名下的所有cookie"
    userDataDir = os.environ['LOCALAPPDATA'] + r'\Google\Chrome\User Data'
    localStateFilePath = userDataDir + r'\Local State'
    cookiepath = userDataDir + r'\Default\Cookies'
    # 97版本已經將Cookies移動到Network目錄下
    if not os.path.exists(cookiepath) or os.stat(cookiepath).st_size == 0:
        cookiepath = userDataDir + r'\Default\Network\Cookies'
    # print(cookiepath)
    sql = f"select name,encrypted_value from cookies where host_key like '%.{host}'"
    cookies = {}
    key = load_local_key(localStateFilePath)
    with sqlite3.connect(cookiepath) as conn:
        cu = conn.cursor()
        for name, encrypted_value in cu.execute(sql).fetchall():
            cookies[name] = decrypt_value(key, encrypted_value)
    return cookies


def get_chrome_path():
    try:
        key = winreg.OpenKey(winreg.HKEY_CLASSES_ROOT, r"ChromeHTML\Application")
        path = winreg.QueryValueEx(key, "ApplicationIcon")[0]
        chrome_path = path[:path.rfind(",")]
        return chrome_path
    except FileNotFoundError as e:
        return


if __name__ == '__main__':
    print(fetch_host_cookie("douyin.com"))
    print(get_chrome_path())

有了這個工具類,我們就不再需要使用 selenium。

然後是視頻解析的核心代碼如下:

def get_video_url(url, cookies):
    headers = {
        "User-Agent""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36",
        "referer""https://www.douyin.com"
    }
    res = requests.get(url, headers=headers, cookies=cookies)
    if res.status_code == 200:
        RENDER_DATA, = re.findall(
            r'<script id="RENDER_DATA" type="application/json">([^<>]+)</script>', res.text)
        data = json.loads(unquote(RENDER_DATA))
        key = '8' if url.find("collection") != -1 else '34'
        try:
            detail = data[key]['aweme']['detail']
            title = detail['desc']
        except Exception as e:
            print(f"{url}無效,報錯的key:", e)
            return
        if not title:
            title, = re.findall("<title[^>]+>\s*([^>]+)\s*</title>", res.text)
        video_url = urljoin(url, detail['video']['playApi'])
        collection_urls = set(re.findall("//www.douyin.com/collection/\d+/\d+", res.text))
        collection_urls = [urljoin("https://www.douyin.com", url) for url in collection_urls]
        collection_urls.sort(key=lambda s: int(s[s.rfind("/") + 1:]))

        collection_title = re.findall("<h2 [^>]+>([^<>]+)</h2>", res.text)[0]
        return video_url, title, collection_urls, collection_title
    else:
        print('視頻鏈接請求失敗!!!')

視頻下載的核心代碼:

def download_video(video_url, title, folder):
    start_time = time.time()
    res = requests.get(url=video_url, stream=True)
    done_size, total = 0, int(res.headers['content-length'])
    chunk_size = 1024 * 1024
    title = format_filename(title)
    file_size = round(int(res.headers['content-length']) / 1024 / 1024, 2)
    basename = f"{title}.mp4"
    filename = f"{folder}/{title}.mp4"
    if os.path.exists(filename):
        print(basename, "已存在,跳過...")
        return
    print("-----------------------------------")
    print(f'開始下載文件:{basename}\n當前文件大小:{file_size}MB')
    with open(filename, 'wb') as f:
        for chunk in res.iter_content(chunk_size):
            f.write(chunk)
            done_size += len(chunk)
            cost_time = time.time() - start_time
            yield done_size, cost_time, total
            # print(f"進度:{done_size / total:.2%},{done_size / cost_time / 1024 / 1024:.2f}MB/s")
    cost_time = time.time() - start_time
    print(f'文件:{basename} 下載完成!\n耗時:{cost_time:0.2f} 秒')

關於視頻鏈接的分析,大家可以參考才哥的文章:

用 Python 搞定抖 X 無水印短視頻下載

鏈接:https://mp.weixin.qq.com/s/NNVT6IH6dpT0rTeu1-oD6w

UI 界面設計核心代碼如下:

layout = [
    [sg.Text('抖音視頻地址:'font=("楷體", 12)),
     sg.In(key='url'size=(70, 1)text_color="#bb8b59",
           default_text="https://www.douyin.com/video/6803929443069988103")],
    [sg.Checkbox('如果是一個合集則下載整個合集'key="download_collection"default=False),
     sg.Button('開始下載'),
     sg.Button('清空輸出'),
     sg.Button('訪問抖音主頁'),
     sg.Button('訪問當前地址'),
     ],
    [sg.Output(size=(85, 10)key="out"text_color="#15d36a")],
    [
        sg.ProgressBar(1000, size=(20, 20)key='video_bar'bar_color=("#bb8b59""#295273")),
        sg.Text('000.0MB,00/00\n00:00<00:00'key="message_video"),
        sg.ProgressBar(1000, size=(20, 20)key='progressbar'bar_color=("#15d36a""#295273")),
        sg.Text('00.00MB/00.00MB\n00.00MB/s'key="message")
    ],
    [sg.Text('輸出目錄:'font=("楷體", 12)),
     sg.In(size=(35, 1)key="save_dir"),
     sg.FolderBrowse('...'target='save_dir'initial_folder="."),
     sg.Checkbox(' 下載完畢後 \n打開所在目錄'key="open_folder"default=True),
     sg.Button('打開輸出目錄'),
     ],
    [sg.Text("@小小明:https://blog.csdn.net/as604049322")],
]

程序下載

該工具的完整代碼和已打包的工具下載地址:

https://gitcode.net/as604049322/python_gui/-/tree/master/douyin
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/N2XRF6cUzETDumZ_dwsVcQ