2203 字
11 分钟
下载已购小鹅通视频
📖 前言
最近购买了小鹅通平台上的付费课程,想下载到本地方便用BiliNote总结视频。但发现小鹅通使用了M3U8+AES-128加密技术,普通的视频下载工具完全无法使用。
基本上是AI做的,给大家个参考吧。网上的软件大都要收费,CSDN上求软件的基本上都是课程的10%收费,然后设置个上限让你付款。
🔗 参考方案: 🧠 技术干货|如何免费下载小鹅通视频?(纯开源方案,无需付费软件)
项目成果:
- ✅ 支持小鹅通AES-128加密视频
- ✅ 6线程并行下载,速度快3-6倍
- ✅ 自动FFmpeg处理,生成标准MP4格式
- ✅ 完整的错误恢复机制
- ✅ 实战验证:28节课程,100%成功率
🎯 小鹅通视频保护机制
技术分析
小鹅通平台采用了多重视频保护技术:
-
M3U8分段技术
- 将完整视频切分成数百个小片段(.ts文件)
- 每个片段通常10秒左右
- 通过.m3u8索引文件记录所有片段
-
AES-128加密
- 每个视频片段都经过AES-128加密
- 需要密钥(key)才能解密
- 每个片段使用独立的IV(初始化向量)
-
动态链接
- 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. 筛选器输入:m3u85. 播放视频,会看到.m3u8文件请求6. 右键复制链接地址
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 reimport binasciifrom 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, iv3. AES-128解密
关键点:小鹅通的IV生成规则
from Crypto.Cipher import AESfrom 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 requestsimport osimport timeimport shutilfrom concurrent.futures import ThreadPoolExecutor, as_completedfrom Crypto.Cipher import AESfrom Crypto.Util.Padding import unpadimport subprocessimport 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)⚠️ 重要注意事项
法律合规
⚠️ 严格遵守以下规则:
-
✅ 允许的使用:
- 下载自己已购买的课程
- 个人学习和备份
- 离线学习使用
-
❌ 禁止的行为:
- 下载未购买的课程(侵权)
- 商业使用或盈利
- 二次分发或转售
- 上传到公共平台分享
- 破解他人账号下载
-
📜 免责声明:
本工具仅供技术学习和研究使用。用户应遵守小鹅通平台服务条款。下载内容仅限个人已购买的课程。不得用于任何侵权或违法行为。使用本工具造成的任何法律后果由用户自行承担。
🚀 后续优化方向
功能扩展
-
GUI界面
- 使用PyQt5或Tkinter
- 拖拽添加课程
- 可视化进度显示
-
自动化增强
- 浏览器插件自动捕获M3U8
- Selenium自动登录和提取
- 批量课程自动发现
-
更多平台支持
- 网易云课堂
- 腾讯课堂
- B站付费课程
- 其他M3U8平台
性能优化
-
断点续传
- 记录下载进度
- 支持中断后继续
- 智能跳过已下载片段
-
智能重试
- 根据错误类型调整策略
- 自动降低并发数
- 动态调整超时时间
-
缓存机制
- 缓存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、小鹅通、视频下载、技术实战
如果这篇文章对你有帮助,欢迎:
👍 点赞支持 | 💬 评论交流 | ⭐ 收藏备用 | 🔗 分享给朋友
声明: 本文仅供技术学习交流,请遵守法律法规和平台规则
部分信息可能已经过时