You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决pytube循环下载YouTube播放列表时误将URL存为文件的报错

问题根因

这个报错和「把URL当文件名保存」没有关系,是三个问题叠加导致的:

  • 代码用了无差别的裸except:捕获,会吞掉所有前置错误:包括视频下载失败、文件名含Windows非法字符、pytube返回的默认文件名和实际落盘文件名不一致等,这些真实错误被隐藏后,后续音频提取逻辑拿不到正确的本地文件路径,才触发后续报错
  • 你当前使用的旧版moviepy(适配Python3.7的版本普遍存在该问题)在ffmpeg_audiowriter的close方法有已知缺陷:如果传入的输出路径无效、或者ffmpeg进程因为前置错误提前退出,此时强制关闭stdin管道就会抛出OSError: [Errno 22] Invalid argument。报错信息里夹带的URL是pytube对象的字符串表示被异常栈错误带出的内容,不是你代码主动传入的文件名
  • 前58次循环没触发是因为这些视频的文件名刚好合法、下载过程也没出现异常,到第59次时要么视频标题包含Windows不允许的文件名字符(如: ? |这类YouTube标题常见符号),要么刚好碰到网络波动导致下载失败,错误被裸except吞掉后就触发了moviepy的这个bug
修复方案

不需要改成「全部下载完再提取音频」的逻辑,按下面调整代码即可实现边下载、边提取、边播放已完成音频的需求:

  • 去掉裸异常捕获,明确打印错误信息,不要吞掉真实报错
  • 下载前主动过滤文件名里的非法字符,同时使用pytube download方法返回的真实落盘路径,不要手动拼接路径
  • 用上下文管理器加载视频文件,处理完自动释放文件句柄,避免长时间运行句柄泄漏
  • 针对旧版moviepy的已知bug加兼容判断,只要音频文件实际生成成功就忽略该关闭报错

核心修复后代码:

import re
import os
import pytube
from moviepy import VideoFileClip

# 过滤Windows系统不允许的文件名字符
def sanitize_filename(filename):
    return re.sub(r'[\\/*?:"<>|]', "_", filename)

def Youtube_playlist_downloader():
    playlist_url = input("input playlist url here (note if it is a url with &index=number at the end that's a song url not a playlist url):")
    playlist = pytube.Playlist(playlist_url)
    # 提前创建目录避免路径不存在报错
    os.makedirs("./videos", exist_ok=True)
    os.makedirs("./audio", exist_ok=True)
    
    for v in playlist.videos:
        print(f"Processing: {v.watch_url}")
        video_path = ""
        safe_name = ""
        try:
            video_obj = pytube.YouTube(v.watch_url)
            stream = video_obj.streams.get_lowest_resolution()
            safe_name = sanitize_filename(stream.default_filename)
            # 直接拿download返回的真实落盘路径,不要手动拼接
            video_path = stream.download(output_path="./videos", filename=safe_name)
        except Exception as e:
            print(f"Failed to download video, err: {str(e)}")
            continue

        audio_path = ""
        try:
            audio_filename = f"{os.path.splitext(safe_name)[0]}{Audio_type}"
            audio_path = os.path.join("./audio", audio_filename)
            # 用with自动释放资源,避免句柄泄漏
            with VideoFileClip(video_path) as clip:
                clip.audio.write_audiofile(audio_path)
            # 到这里音频已经写入完成,可以直接调用播放逻辑,不需要等待后续下载
        except OSError as e:
            # 兼容旧版moviepy的stdin关闭误报:文件存在且大小正常就跳过报错
            if e.errno == 22 and os.path.exists(audio_path) and os.path.getsize(audio_path) > 0:
                pass
            else:
                print(f"Failed to extract audio, err: {str(e)}")
        except Exception as e:
            print(f"Failed to extract audio, err: {str(e)}")
    
    Choice_choosing()

补充:如果后续还偶发该报错,可以把moviepy升级到1.0.3以上版本,官方已经修复了ffmpeg进程提前退出时强制关闭stdin抛错的问题。

内容的提问来源于stack exchange,提问作者Kurtis Gibson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 00:57:19