
pytube 异常处理完全指南用 VideoUnavailable 体系优雅处理 YouTube 视频不可用场景【免费下载链接】pytubeLightweight, dependency-free Python library and CLI for downloading YouTube videos, playlists, and captions.项目地址: https://gitcode.com/GitHub_Trending/py/pytube本篇技术指南围绕 pytube当前仓库版本 15.0.0见 pytube/version.py的官方文档 docs/user/exceptions.rst 展开系统讲解其面向用户设计的异常体系从统一的VideoUnavailable基类到AgeRestrictedError、LiveStreamError、VideoPrivate、MembersOnly、VideoRegionBlocked等细分异常并结合 pytube/exceptions.py 源码与 tests/test_exceptions.py 测试用例给出可落地的异常捕获代码。读完本文你将掌握如何在批量下载播放列表、处理私享/地区限制/直播等场景中编写健壮的 pytube 程序做到该跳过的跳过、该报错的报错、程序永不因单条视频失败而崩溃。为什么 pytube 需要一套专属异常体系YouTube 上有大量视频对第三方库并不友好私有视频、会员专属视频、地区限制、年龄限制、直播流、下架视频……pytube 无法也不可能魔法般访问所有这些内容因此官方文档明确指出pytube 实现了一批有用的异常用于处理程序流程program flow在无法访问视频时依赖用户调用方来处理这些异常。从源码 pytube/exceptions.py 可以看到设计动机所有异常统一继承自自定义的PytubeError而非内置Exception其 docstring 说明这是为了不污染内置异常避免实现方代码中笼统的except Exception意外捕获到与业务无关的错误从而错误地被处理或漏处理。Exception └── PytubeError所有 pytube 异常的基类 ├── MaxRetriesExceeded ├── HTMLParseError ├── ExtractError │ └── RegexMatchError └── VideoUnavailable视频不可用基类 ├── AgeRestrictedError ├── LiveStreamError ├── VideoPrivate ├── RecordingUnavailable ├── MembersOnly └── VideoRegionBlocked异常体系逐类详解以下每个异常类的定义、构造函数与消息格式均以 pytube/exceptions.py 为准其可捕获关系被 tests/test_exceptions.py 的pytest.raises断言逐一验证。PytubeError统一的根基类所有 pytube 专属异常的父类pytube/exceptions.py。如果你希望要么完全不处理 pytube 错误、要么统一兜底可以只捕获PytubeError它会覆盖下文的全部异常。VideoUnavailable最常用的泛型视频不可用异常这是官方文档建议作为第一道防线的异常pytube/exceptions.py。构造函数接收video_id字符串参数并将该 ID 保存在实例属性e.video_id上便于日志记录与后续重试其str()输出格式为{video_id} is unavailable。核心价值在于多态捕获所有细分不可用异常都继承自它因此except VideoUnavailable可以一次性捕获私有、地区限制、会员专属、直播等全部不可用场景。官方文档特别指出这可用于跳过播放列表中的私有视频、地区限制视频等详见下文实战。六个细分不可用异常异常类触发语义消息格式str(e)AgeRestrictedError年龄受限未登录/OAuth 无法访问三级限制{video_id} is age restricted, and cant be accessed without logging in.LiveStreamError视频是正在直播的流无法加载{video_id} is streaming live and cannot be loadedVideoPrivate私有视频需登录验证{video_id} is a private videoRecordingUnavailable直播录像不可用{video_id} does not have a live stream recording availableMembersOnly会员专属视频需订阅频道{video_id} is a members-only videoVideoRegionBlocked当前地区不可观看{video_id} is not available in your region上表中每个类均接收video_id并暴露同名属性AgeRestrictedError的 docstring 强调无法在不登录的情况下访问MembersOnly则对应 YouTube 面向订阅用户的专属内容机制。需要精细区分原因时可以按具体类型分别捕获只想跳过时捕获基类即可。ExtractError 与 RegexMatchError数据提取层异常RegexMatchError继承自ExtractErrorpytube/exceptions.py是最常见的解析类异常。它的构造函数签名与VideoUnavailable不同接收caller调用函数名与pattern未匹配上的正则表达式两个参数消息格式为{caller}: could not find match for {pattern}。这类异常表示 pytube 在从页面 HTML / JS 中解析数据时失败通常意味着 YouTube 页面结构变化或网络返回异常内容。例如 pytube/helpers.py 的regex_search在正则无匹配时抛出RegexMatchError(callerregex_search, patternpattern)pytube/cipher.py 中多处解密失败也会抛出RegexMatchError。测试 tests/test_exceptions.py 验证了str(e) hello: could not find match for *的格式。HTMLParseErrorHTML 解析层异常由 pytube/parser.py 抛出pytube/exceptions.py表示无法从 HTML 中解析出预期的 JS 对象。parser 模块在找不到前置正则、对象起点非法或对象解析失败时抛出该异常pytube/parser.py部分场景下会先尝试跳过单个匹配失败项pytube/parser.py。相应测试见 tests/test_parser.py。MaxRetriesExceeded网络层重试耗尽异常在下载流式内容时pytube 支持自动重试超过上限则抛出MaxRetriesExceededpytube/exceptions.py。其逻辑位于 pytube/request.py仅对socket.timeout与http.client.IncompleteRead这类可重试错误进行重试其他URLError直接向上抛出当尝试次数超过1 max_retries时抛出该异常。测试见 tests/test_request.py。核心实战用 VideoUnavailable 跳过播放列表中的不可用视频官方文档给出了本文最重要的实战示例遍历播放列表 URL 时用try/except VideoUnavailable跳过无法下载的视频。下面保留原示例的完整结构并补充注释 from pytube import Playlist, YouTube from pytube.exceptions import VideoUnavailable playlist_url https://youtube.com/playlist?listspecial_playlist_id p Playlist(playlist_url) for url in p.video_urls: ... try: ... yt YouTube(url) ... except VideoUnavailable: ... print(fVideo {url} is unavailable, skipping.) ... else: ... print(fDownloading video: {url}) ... yt.streams.first().download()这段代码的行为要点Playlist(...)与播放列表解析相关实现见 pytube/contrib/playlist.pyp.video_urls按列表顺序产出每个视频的 URLtry块只包裹YouTube(url)构造这符合官方文档意图——VideoUnavailable在对象初始化/访问属性阶段详见下文异常从哪里来就可能抛出else子句仅当try块未抛异常时才执行下载避免在失败分支里继续调用yt.streams造成二次异常文档明确说明这样做会自动跳过因 pytube 库限制而无法下载的视频即私有视频、地区限制视频等。需要更强的健壮性时还可以把except拆细、分别处理 from pytube import Playlist, YouTube from pytube.exceptions import ( ... VideoUnavailable, VideoPrivate, MembersOnly, VideoRegionBlocked, ... ) p Playlist(playlist_url) for url in p.video_urls: ... try: ... yt YouTube(url) ... except VideoPrivate: ... print(f[SKIP] {url} is private, skipping.) ... except MembersOnly: ... print(f[SKIP] {url} is members-only, skipping.) ... except VideoRegionBlocked: ... print(f[SKIP] {url} is region-blocked, skipping.) ... except VideoUnavailable: ... print(f[SKIP] {url} unavailable for unknown reason, skipping.) ... else: ... print(f[OK] Downloading {url}) ... yt.streams.get_highest_resolution().download()异常从哪里来check_availability 与播放状态判定VideoUnavailable家族异常并非凭空抛出其源头是YouTube.check_availability()pytube/main.py。该方法解析页面中的playabilityStatus根据状态与原因文案映射到具体异常status UNPLAYABLE若原因包含 Join this channel... 则抛MembersOnly若原因是 This live stream recording is not available. 则抛RecordingUnavailable否则抛通用VideoUnavailablestatus LOGIN_REQUIRED原因包含 This is a private video... 时抛VideoPrivatestatus ERROR原因 Video unavailable 时抛VideoUnavailablestatus LIVE_STREAM抛LiveStreamError。而访问yt.streams如fmt_streams属性pytube/main.py会先调用check_availability()这就是为什么不可用异常常在访问.streams或调用.download()时出现。此外ExtractError在签名解密失败时会被 pytube/main.py 捕获并清理 JS 缓存后重试一次AgeRestrictedError则在绕过年龄门失败时抛出pytube/main.pyLiveStreamError也可能在提取层因缺少签名 URL 而抛出pytube/extract.py。测试用例如何验证异常行为仓库的 tests/test_exceptions.py 从两个层面保证异常体系正确性纯单元验证直接raise各异常断言video_id属性与str(e)消息格式如test_video_unavailable、test_live_stream_error、test_private_error、test_region_locked_error等多态验证每个细分异常都断言可以用except VideoUnavailable捕获例如with pytest.raises(exceptions.VideoUnavailable): raise exceptions.LiveStreamError(...)从测试层面固化泛型捕获的契约端到端验证通过 mock 页面 HTML复用 tests/mocks 中的yt-video-m8uHb5jIGN8-html.json.gz、yt-video-5YceQ8YqYMc-html.json.gz等夹具驱动真实YouTube(...).streams调用断言抛出VideoPrivate、RecordingUnavailable。编写健壮下载脚本的最佳实践结合源码与官方文档总结以下可复用的模式优先捕获VideoUnavailable覆盖全部不可用场景适合跳过即可的批量任务仅在需要差异化日志/重试策略时才细分捕获区分两类异常VideoUnavailable家族是视频本身的业务状态ExtractError/RegexMatchError/HTMLParseError是解析层面的库内部问题——前者通常应跳过后者往往意味着 YouTube 页面结构变化需要升级 pytube 或稍后重试细粒度捕获时注意顺序把VideoPrivate、MembersOnly等子类放在VideoUnavailable之前Python 异常匹配按except顺序进行先捕获子类才能拿到精确语义善用video_id属性所有不可用异常都携带video_id可直接用于日志与重试队列测试 tests/test_exceptions.py 验证了该属性网络层单独兜底批量下载时可用try/except MaxRetriesExceeded单独处理网络重试耗尽pytube/request.py避免与视频业务异常混淆批量任务单条失败不影响整体官方文档的播放列表示例是最佳范式——try只包裹最小必要操作except记录并跳过else执行下载。常见问题速查Q访问YouTube(url).streams时报VideoUnavailable说明什么A视频确实不可用私有、地区限制、已删除等由check_availability()判定抛出。捕获它即可优雅跳过。Qexcept Exception能捕获 pytube 异常吗A能但官方明确不建议所有 pytube 异常继承自自定义的PytubeError而非污染内置Exception用意就是让你精确捕获PytubeError或其子类避免误伤其他错误。QRegexMatchError和VideoUnavailable有什么区别A前者是解析层失败ExtractError的子类携带caller与pattern信息说明数据提取失败通常是页面结构问题后者是视频业务状态不可用两者处理策略不同。Q如何获取被跳过视频的 IDA捕获VideoUnavailable后读取e.video_id即可所有不可用异常均提供该属性。更多异常模块细节可继续阅读 pytube/exceptions.py、播放列表相关文档 docs/user/playlist.rst 以及 YouTube 对象主入口 pytube/main.py。【免费下载链接】pytubeLightweight, dependency-free Python library and CLI for downloading YouTube videos, playlists, and captions.项目地址: https://gitcode.com/GitHub_Trending/py/pytube创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考