PyTube 的 get_by_resolution() 方法在部分视频中会返回 None,导致调用 .download() 时抛出 'NoneType' object has no attribute 'download' 错误;正确做法是使用 .filter(res=...) + .first() 安全获取流对象。
pytube 的 `get_by_resolution()` 方法在部分视频中会返回 `none`,导致调用 `.download()` 时抛出 `'nonetype' object has no attribute 'download'` 错误;正确做法是使用 `.filter(res=...)` + `.first()` 安全获取流对象。
在使用 PyTube 下载 YouTube 视频时,许多开发者会误以为 streams.get_by_resolution(resolution="720p") 总能返回一个可用的流对象。但实际上,该方法仅匹配「视频+音频合一」(progressive)流,而 YouTube 自 2020 年起已对高清(≥ 720p)视频普遍采用「分离式流」(adaptive streams):即视频轨道(video/mp4)和音频轨道(audio/mp4)独立存在,不合并为单一流。因此,当调用 get_by_resolution("480p") 或 "720p" 时,若该分辨率下无 progressive 流(常见于较新或高码率视频),就会返回 None,进而触发 AttributeError。
✅ 正确且健壮的解决方案是使用 streams.filter() 配合 .first():
from pytube import YouTube
def VideoDownload_mp4(url: str, res: str, folder: str = "YouTube Videos"):
try:
yt = YouTube(url)
# 安全过滤:支持 progressive 和 adaptive 流(优先匹配 video-only)
stream = yt.streams.filter(res=res, mime_type="video/mp4").first()
if stream is None:
# 回退尝试:查找同分辨率的 progressive 流(含音视频)
stream = yt.streams.filter(res=res, progressive=True).first()
if stream is None:
raise ValueError(f"No stream found for resolution '{res}'")
stream.download(output_path=folder)
print(f"✅ Downloaded '{yt.title}' ({res}) to '{folder}'")
except Exception as e:
print(f"❌ Error: {e}")
? 关键说明与最佳实践:
- filter(res="720p") 比 get_by_resolution() 更可靠,它返回 StreamSet,可链式调用 .first()、.last() 或 .all();
- 显式指定 mime_type="video/mp4" 可避免意外匹配 WebM 或其他格式;
- 建议添加回退逻辑(如先查 adaptive video-only 流,再查 progressive 合一流),提升兼容性;
- 若需最高质量,推荐下载 video+audio 分离流后用 ffmpeg 合并(PyTube 不内置合并功能);
- 注意:yt.streams 默认包含大量无效/重复流,建议结合 .order_by("resolution").desc() 或 .distinct() 进行去重优化。
? 补充:你提供的 GetVideoQuality() 函数逻辑较复杂且易出错(如 fixed_list.remove(...) 中的嵌套列表推导可能索引越界)。推荐改用更简洁、安全的方式枚举可用分辨率:
def list_available_resolutions(url: str) -> list:
yt = YouTube(url)
resolutions = set()
for stream in yt.streams.filter(mime_type="video/mp4", type="video"):
if stream.resolution:
resolutions.add(stream.resolution)
return sorted(list(resolutions), key=lambda x: int(x[:-1]), reverse=True) # 如 ['1080p', '720p', '480p']
这样既能准确获取真实可用的分辨率选项,又避免了手动去重和比特率比较的潜在风险。











