Mobile wallpaper 1
2203 字
11 分钟
下载已购小鹅通视频

📖 前言#

最近购买了小鹅通平台上的付费课程,想下载到本地方便用BiliNote总结视频。但发现小鹅通使用了M3U8+AES-128加密技术,普通的视频下载工具完全无法使用。

基本上是AI做的,给大家个参考吧。网上的软件大都要收费,CSDN上求软件的基本上都是课程的10%收费,然后设置个上限让你付款。

🔗 参考方案: 🧠 技术干货|如何免费下载小鹅通视频?(纯开源方案,无需付费软件)

项目成果

  • ✅ 支持小鹅通AES-128加密视频
  • ✅ 6线程并行下载,速度快3-6倍
  • ✅ 自动FFmpeg处理,生成标准MP4格式
  • ✅ 完整的错误恢复机制
  • ✅ 实战验证:28节课程,100%成功率

🎯 小鹅通视频保护机制#

技术分析#

小鹅通平台采用了多重视频保护技术:

  1. M3U8分段技术

    • 将完整视频切分成数百个小片段(.ts文件)
    • 每个片段通常10秒左右
    • 通过.m3u8索引文件记录所有片段
  2. AES-128加密

    • 每个视频片段都经过AES-128加密
    • 需要密钥(key)才能解密
    • 每个片段使用独立的IV(初始化向量)
  3. 动态链接

    • M3U8链接有时效性
    • 需要登录验证
    • 防盗链保护

💡 解决方案#

整体思路#

1. 获取M3U8链接(浏览器开发者工具)
2. 下载M3U8播放列表
3. 解析加密信息(密钥URL、IV)
4. 下载加密密钥
5. 并行下载所有视频片段(6线程)
6. 逐个解密片段(AES-128 CBC模式)
7. FFmpeg合并为标准MP4
8. 完成!

技术栈#

  • Python 3.8+ - 主要开发语言
  • requests - HTTP请求处理
  • pycryptodome - AES加密解密
  • concurrent.futures - 并发下载
  • FFmpeg - 视频格式转换
  • subprocess - 调用FFmpeg

🔧 核心实现#

1. 获取M3U8链接#

方法:使用浏览器开发者工具

1. 打开课程视频页面
2. 按F12打开开发者工具
3. 切换到Network(网络)标签
4. 筛选器输入:m3u8
5. 播放视频,会看到.m3u8文件请求
6. 右键复制链接地址

获取M3U8链接

2. 解析M3U8文件#

小鹅通的M3U8文件格式:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-KEY:METHOD=AES-128,URI="https://.../key.key",IV=0x00000000000000000000000000000000
#EXTINF:10.0,
segment_0001.ts
#EXTINF:10.0,
segment_0002.ts
...

解析代码

import re
import binascii
from urllib.parse import urljoin
def parse_m3u8(content, base_url):
"""解析小鹅通M3U8文件"""
segments = []
key_url = None
iv = None
for line in content.split('\n'):
line = line.strip()
# 提取加密信息
if line.startswith('#EXT-X-KEY:'):
if 'METHOD=AES-128' in line:
# 提取密钥URL
uri_match = re.search(r'URI="([^"]+)"', line)
if uri_match:
key_url = urljoin(base_url, uri_match.group(1))
# 提取IV
iv_match = re.search(r'IV=0x([0-9A-Fa-f]+)', line)
if iv_match:
iv = binascii.unhexlify(iv_match.group(1))
# 提取片段URL
elif line and not line.startswith('#'):
segment_url = urljoin(base_url, line)
segments.append(segment_url)
return segments, key_url, iv

3. AES-128解密#

关键点:小鹅通的IV生成规则

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
def decrypt_segment(encrypted_data, key, base_iv, segment_index):
"""
解密小鹅通视频片段
重要:小鹅通每个片段的IV不同!
规则:IV的最后4字节替换为片段索引
"""
# 生成该片段的IV
segment_iv = base_iv[:-4] + segment_index.to_bytes(4, byteorder='big')
# AES CBC模式解密
cipher = AES.new(key, AES.MODE_CBC, segment_iv)
decrypted_data = cipher.decrypt(encrypted_data)
# 去除PKCS7填充
try:
decrypted_data = unpad(decrypted_data, AES.block_size)
except:
# 某些片段可能不需要去填充
pass
return decrypted_data

踩坑记录

  • ❌ 错误:直接使用M3U8中的固定IV → 解密失败,视频花屏
  • ✅ 正确:为每个片段生成独立IV → 解密成功

4. 并行下载加速#

from concurrent.futures import ThreadPoolExecutor, as_completed
def download_segments_parallel(segments, key, iv, max_workers=6):
"""6线程并发下载"""
successful_segments = []
with ThreadPoolExecutor(max_workers=max_workers) as executor:
# 提交所有下载任务
futures = {
executor.submit(download_and_decrypt_segment,
url, key, iv, index): index
for index, url in enumerate(segments)
}
# 收集结果
for future in as_completed(futures):
index = futures[future]
try:
filepath = future.result()
successful_segments.append((index, filepath))
print(f"进度: {len(successful_segments)}/{len(segments)}")
except Exception as e:
print(f"片段{index}下载失败: {e}")
# 按索引排序
successful_segments.sort(key=lambda x: x[0])
return successful_segments

性能对比

方法40分钟视频耗时速度提升
串行下载~20分钟基准
3线程~8分钟2.5倍
6线程(推荐)~3.5分钟5.7倍
10线程~3分钟6.7倍(可能被限速)

5. FFmpeg合并#

import subprocess
def merge_with_ffmpeg(segment_files, output_file):
"""使用FFmpeg合并为标准MP4"""
# 创建文件列表
with open('filelist.txt', 'w', encoding='utf-8') as f:
for filepath in segment_files:
f.write(f"file '{filepath}'\n")
# FFmpeg命令
cmd = [
'ffmpeg',
'-f', 'concat', # 连接模式
'-safe', '0', # 允许相对路径
'-i', 'filelist.txt', # 输入列表
'-c:v', 'libx264', # H.264视频编码
'-c:a', 'aac', # AAC音频编码
'-movflags', '+faststart',# 优化流媒体播放
'-y', # 覆盖输出
output_file
]
subprocess.run(cmd, timeout=600)

输出效果

  • ✅ 标准MP4格式
  • ✅ 兼容所有播放器
  • ✅ 支持快进快退
  • ✅ 音画同步完美

🎯 核心代码#

完整的下载器类#

import requests
import os
import time
import shutil
from concurrent.futures import ThreadPoolExecutor, as_completed
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import subprocess
import binascii
class XiaoeM3U8Downloader:
"""小鹅通M3U8视频下载器"""
def __init__(self, output_dir="downloads"):
self.output_dir = output_dir
self.ffmpeg_path = None
self.session = requests.Session()
# 设置请求头(模拟浏览器)
self.session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Referer': 'https://xiaoe-tech.com/',
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive'
})
def download_course(self, course):
"""下载单个课程"""
print(f"\n开始下载: {course['title']}")
print("=" * 60)
# 1. 下载M3U8
m3u8_content = self.session.get(course['m3u8_url']).text
# 2. 解析
base_url = '/'.join(course['m3u8_url'].split('/')[:-1]) + '/'
segments, key_url, iv = self.parse_m3u8(m3u8_content, base_url)
print(f"找到 {len(segments)} 个视频片段")
# 3. 获取密钥
key = self.session.get(key_url).content
print(f"密钥长度: {len(key)} 字节")
# 4. 并行下载
temp_dir = f"temp_{course['chapter']}"
os.makedirs(temp_dir, exist_ok=True)
segment_files = self.download_segments_parallel(
segments, key, iv, temp_dir
)
# 5. 合并
output_file = f"{course['chapter']:02d}_{course['title']}.mp4"
self.merge_with_ffmpeg(segment_files, output_file)
# 6. 清理
shutil.rmtree(temp_dir)
print(f"✅ 课程下载完成: {course['title']}")
return True
def parse_m3u8(self, content, base_url):
"""解析M3U8文件"""
segments = []
key_url = None
iv = None
for line in content.split('\n'):
line = line.strip()
if '#EXT-X-KEY:' in line and 'AES-128' in line:
# 提取密钥URL
start = line.find('URI="') + 5
end = line.find('"', start)
key_url = urljoin(base_url, line[start:end])
# 提取IV
iv_start = line.find('IV=0x')
if iv_start != -1:
iv_hex = line[iv_start + 5:iv_start + 37]
iv = binascii.unhexlify(iv_hex)
elif line and not line.startswith('#'):
segments.append(urljoin(base_url, line))
return segments, key_url, iv
def download_and_decrypt_segment(self, url, key, base_iv, index, temp_dir):
"""下载并解密单个片段"""
# 下载
response = self.session.get(url, timeout=30)
encrypted_data = response.content
# 解密(关键:生成片段专属IV)
segment_iv = base_iv[:-4] + index.to_bytes(4, byteorder='big')
cipher = AES.new(key, AES.MODE_CBC, segment_iv)
decrypted_data = cipher.decrypt(encrypted_data)
# 保存
filepath = os.path.join(temp_dir, f"segment_{index:04d}.ts")
with open(filepath, 'wb') as f:
f.write(decrypted_data)
return filepath
def download_segments_parallel(self, segments, key, iv, temp_dir, max_workers=6):
"""并行下载所有片段"""
segment_files = []
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {
executor.submit(self.download_and_decrypt_segment,
url, key, iv, i, temp_dir): i
for i, url in enumerate(segments)
}
for future in as_completed(futures):
filepath = future.result()
segment_files.append(filepath)
print(f"进度: {len(segment_files)}/{len(segments)}")
return sorted(segment_files)
def merge_with_ffmpeg(self, segment_files, output_file):
"""FFmpeg合并"""
with open('filelist.txt', 'w') as f:
for filepath in segment_files:
f.write(f"file '{filepath}'\n")
cmd = [
'ffmpeg', '-f', 'concat', '-safe', '0',
'-i', 'filelist.txt',
'-c:v', 'libx264', '-c:a', 'aac',
'-movflags', '+faststart',
'-y', output_file
]
subprocess.run(cmd)
os.remove('filelist.txt')

使用示例#

# 创建下载器
downloader = XiaoeM3U8Downloader(output_dir="downloads")
# 下载单个课程
course = {
'chapter': 1,
'title': '01课-初识软件',
'm3u8_url': 'https://encrypt-k-vod.xet.tech/.../v.m3u8?sign=...'
}
downloader.download_course(course)

⚠️ 重要注意事项#

法律合规#

⚠️ 严格遵守以下规则

  1. ✅ 允许的使用

    • 下载自己已购买的课程
    • 个人学习和备份
    • 离线学习使用
  2. ❌ 禁止的行为

    • 下载未购买的课程(侵权)
    • 商业使用或盈利
    • 二次分发或转售
    • 上传到公共平台分享
    • 破解他人账号下载
  3. 📜 免责声明

    本工具仅供技术学习和研究使用。
    用户应遵守小鹅通平台服务条款。
    下载内容仅限个人已购买的课程。
    不得用于任何侵权或违法行为。
    使用本工具造成的任何法律后果由用户自行承担。

🚀 后续优化方向#

功能扩展#

  1. GUI界面

    • 使用PyQt5或Tkinter
    • 拖拽添加课程
    • 可视化进度显示
  2. 自动化增强

    • 浏览器插件自动捕获M3U8
    • Selenium自动登录和提取
    • 批量课程自动发现
  3. 更多平台支持

    • 网易云课堂
    • 腾讯课堂
    • B站付费课程
    • 其他M3U8平台

性能优化#

  1. 断点续传

    • 记录下载进度
    • 支持中断后继续
    • 智能跳过已下载片段
  2. 智能重试

    • 根据错误类型调整策略
    • 自动降低并发数
    • 动态调整超时时间
  3. 缓存机制

    • 缓存M3U8内容
    • 缓存解密密钥
    • 减少重复请求

📦 项目文件#

完整项目包含以下文件:

xiaoe-downloader/
├── ultimate_m3u8_downloader.py # 核心脚本(500行)
├── xiaoe_course_data.json # 课程数据示例
├── requirements.txt # Python依赖
├── 一键下载全部课程.bat # Windows启动器
├── 测试单个课程.bat # 测试脚本
├── 安装依赖.bat # 依赖安装
└── docs/ # 完整文档
├── README.md
├── 快速参考卡.md
├── 使用指南.md
└── 系统架构图.md

📚 参考资料#


发布时间: 2025年11月1日
分类: 作品集
标签: 程序系、Python、小鹅通、视频下载、技术实战


如果这篇文章对你有帮助,欢迎:
👍 点赞支持 | 💬 评论交流 | ⭐ 收藏备用 | 🔗 分享给朋友

声明: 本文仅供技术学习交流,请遵守法律法规和平台规则

下载已购小鹅通视频
http://localhost:4322/portfolio/programming/xiaoe-downloader/
作者
Ayano
发布于
2025-11-01
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时