douyin无水印视频下载

一个支持多线程的douyin无水印视频下载器(Python 实现)

前言

本文是一篇 Python 爬虫技术学习笔记,通过分析抖音网页结构,学习以下技术点:

  • 网页数据解析(正则表达式、JSON解析)
  • HTTP请求与Session管理
  • Python多线程编程(ThreadPoolExecutor)
  • 命令行工具开发(argparse)

声明:本文仅供技术学习交流,请勿用于任何商业用途或侵犯他人权益的行为。

GitHub 项目地址:

https://github.com/yanghongm/douyin-downloader

技术栈

  • Python 3.7+
  • requests - HTTP请求库
  • concurrent.futures - 多线程模块
  • argparse - 命令行参数解析
  • dataclass - 数据类

核心技术点

1. URL解析与正则表达式

抖音有多种URL格式,需要用正则表达式统一提取视频ID:

python 复制代码
import re

URL_PATTERNS = [
    r'douyin\.com/video/(\d+)',           # PC端链接
    r'iesdouyin\.com/share/video/(\d+)',  # 分享链接
    r'v\.douyin\.com/(\w+)',              # 短链接
]

def extract_video_id(url: str) -> str:
    for pattern in URL_PATTERNS:
        match = re.search(pattern, url)
        if match:
            return match.group(1)
    return None

知识点

  • re.search() 在字符串中搜索匹配
  • (\d+) 捕获组,匹配数字
  • match.group(1) 获取第一个捕获组

2. 短链接重定向处理

短链接需要跟随重定向获取真实URL:

python 复制代码
import requests

def resolve_short_url(short_url: str) -> str:
    session = requests.Session()
    resp = session.get(short_url, allow_redirects=True)
    return resp.url  # 返回最终跳转的URL

知识点

  • allow_redirects=True 自动跟随重定向
  • resp.url 获取最终URL

3. 网页数据解析

从HTML中提取JSON数据的两种方式:

方式一:正则提取

python 复制代码
# 提取页面中的JSON数据
pattern = r'"playApi"\s*:\s*"([^"]+)"'
match = re.search(pattern, html)
if match:
    video_url = match.group(1)
    # 处理Unicode转义
    video_url = video_url.replace("\\u002F", "/")

方式二:JSON解析

python 复制代码
import json
from urllib.parse import unquote

# 提取script标签中的JSON
pattern = r'<script id="RENDER_DATA"[^>]*>([^<]+)</script>'
match = re.search(pattern, html)
if match:
    data = json.loads(unquote(match.group(1)))
    # 递归查找需要的字段

4. 递归查找嵌套数据

当JSON结构复杂时,可以用递归函数查找:

python 复制代码
def find_in_dict(obj, key: str, depth: int = 0):
    """递归查找字典中的值"""
    if depth > 15:  # 防止无限递归
        return None

    if isinstance(obj, dict):
        if key in obj and obj[key]:
            return obj[key]
        for v in obj.values():
            result = find_in_dict(v, key, depth + 1)
            if result:
                return result

    elif isinstance(obj, list):
        for item in obj:
            result = find_in_dict(item, key, depth + 1)
            if result:
                return result

    return None

5. 多线程并发下载

使用 ThreadPoolExecutor 实现线程池:

python 复制代码
from concurrent.futures import ThreadPoolExecutor, as_completed

def process_batch(urls: list, workers: int = 5):
    with ThreadPoolExecutor(max_workers=workers) as executor:
        # 提交所有任务
        futures = {
            executor.submit(process_single, url): url
            for url in urls
        }

        # 获取完成的结果
        for future in as_completed(futures):
            url = futures[future]
            try:
                result = future.result()
                print(f"完成: {url}")
            except Exception as e:
                print(f"失败: {url}, 错误: {e}")

知识点

  • ThreadPoolExecutor 线程池管理
  • executor.submit() 提交任务
  • as_completed() 按完成顺序迭代

6. 线程安全的计数器

多线程环境下需要使用锁保护共享变量:

python 复制代码
import threading

class ThreadSafeCounter:
    def __init__(self):
        self.count = 0
        self.lock = threading.Lock()

    def increment(self):
        with self.lock:
            self.count += 1
            return self.count

7. 流式下载大文件

下载大文件时使用流式传输,避免内存溢出:

python 复制代码
def download_file(url: str, filepath: str):
    with requests.get(url, stream=True) as resp:
        resp.raise_for_status()

        total = int(resp.headers.get('content-length', 0))
        downloaded = 0

        with open(filepath, 'wb') as f:
            for chunk in resp.iter_content(chunk_size=8192):
                if chunk:
                    f.write(chunk)
                    downloaded += len(chunk)
                    # 可以在这里更新进度条

知识点

  • stream=True 启用流式传输
  • iter_content() 分块读取
  • chunk_size 每块大小

8. 命令行参数解析

使用 argparse 构建命令行工具:

python 复制代码
import argparse

def main():
    parser = argparse.ArgumentParser(description='示例工具')

    parser.add_argument('url', nargs='?', help='目标URL')
    parser.add_argument('-f', '--file', help='URL列表文件')
    parser.add_argument('-o', '--output', default='.', help='输出目录')
    parser.add_argument('-w', '--workers', type=int, default=5, help='线程数')

    args = parser.parse_args()

    if args.file:
        # 批量处理模式
        with open(args.file, 'r') as f:
            urls = [line.strip() for line in f if line.strip()]
        process_batch(urls, args.workers)
    elif args.url:
        # 单个处理模式
        process_single(args.url)

9. 数据类简化代码

使用 dataclass 定义数据结构:

python 复制代码
from dataclasses import dataclass

@dataclass
class VideoInfo:
    video_id: str
    title: str
    author: str
    url: str
    cover_url: str = ""  # 可选字段,有默认值
复制代码
## 异常处理最佳实践

```python
import requests
from requests.exceptions import RequestException

def safe_request(url: str, retries: int = 3):
    for attempt in range(retries):
        try:
            resp = requests.get(url, timeout=30)
            resp.raise_for_status()
            return resp
        except RequestException as e:
            if attempt == retries - 1:
                raise
            time.sleep(1)  # 重试前等待

总结

本文介绍了以下Python技术:

  1. 正则表达式 - 文本模式匹配与提取
  2. HTTP请求 - requests库的高级用法
  3. JSON解析 - 复杂嵌套数据的处理
  4. 多线程 - ThreadPoolExecutor线程池
  5. 线程安全 - Lock锁的使用
  6. 流式IO - 大文件的高效处理
  7. CLI开发 - argparse参数解析

这些技术在爬虫开发、数据处理、自动化脚本等场景都非常实用。

免责声明

  1. 本文仅供技术学习和研究使用
  2. 请遵守相关网站的服务条款和robots协议
  3. 请勿将技术用于任何非法用途
  4. 任何因使用本文技术造成的问题,作者不承担责任
  5. 尊重内容创作者的版权,支持正版
相关推荐
feasibility.2 小时前
多模态模型Qwen3-VL在Llama-Factory中断LoRA微调训练+测试+导出+部署全流程--以具身智能数据集open-eqa为例
人工智能·python·大模型·nlp·llama·多模态·具身智能
喵手2 小时前
Python爬虫实战:采集各大会展平台的展会名称、举办时间、展馆地点、主办方、行业分类等结构化数据(附CSV导出 + SQLite持久化存储)!
爬虫·python·爬虫实战·零基础python爬虫教学·采集大会展平台信息·展会名称举办时间展馆地址·采集数据csv/json导出
编码者卢布2 小时前
【Azure APIM】如何实现对经过APIM并到达后端服务请求的全链路追踪呢?
python·flask·azure
0思必得02 小时前
[Web自动化] Selenium执行JavaScript语句
前端·javascript·爬虫·python·selenium·自动化
焱童鞋2 小时前
解决 MeteoInfoLab 3.9.11 中 contourfm 导致的 ArrayIndexOutOfBoundsException
开发语言·python
封奚泽优2 小时前
化学配对记忆游戏:用Python和Pygame打造趣味化学学习工具
python·pygame
梦幻精灵_cq2 小时前
问题切入『视角很重要』——ansi-color有效编码序列“单背景判定”小部件的“简洁精妙”
python
有代理ip2 小时前
成功请求的密码:HTTP 2 开头响应码深度解析
java·大数据·python·算法·php
0思必得02 小时前
[Web自动化] Selenium截图
前端·爬虫·python·selenium·自动化