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

如何用Python的youtube-dl获取YouTube播放列表首个视频并解决报错

问题描述

我是Python新手,已经实现了YouTube视频转换功能,但输入播放列表URL时会出现JSON解析错误,添加--playlist-start 1参数后问题仍存在,而且YouTube短视频也无法正常运行。以下是我的代码:

App.py代码

from flask import Flask, request, render_template, session, url_for, redirect, send_file
from extractor import extract_video_data_from_url

app = Flask(__name__)

@app.route("/")
def index():
      return render_template('index.html')

@app.route("/download", methods=["POST"])
def download():
   video_url = request.form["video_url"]
   video_data = extract_video_data_from_url(video_url)
   return render_template("Download.html",video_data=video_data)

extractor.py代码

import os
import json

def extract_format_data(format_data):
    extension = format_data["ext"]
    format_name = format_data["format"]
    url = format_data["url"]
    return {
        "extension": extension,
        "format_name": format_name,
        "url": url
    }
        
def extract_video_data_from_url(url):
    command = f'youtube-dl "{url}" -j'
    output = os.popen(command).read()
    video_data = json.loads(output)
    title = video_data["title"]
    formats = video_data["formats"]
    thumbnail = video_data["thumbnail"]
    formats = [extract_format_data(format_data) for format_data in formats]
    return {
        "title": title,
        "formats": formats,
        "thumbnail": thumbnail
    }

解决方案

核心问题分析

  1. 播放列表JSON解析失败:youtube-dl处理播放列表时,-j参数会输出每行一个独立JSON对象(对应播放列表里的每个视频),你的代码用json.loads()直接解析全部输出,多个JSON连在一起必然导致解析错误。
  2. 短视频支持不足:youtube-dl已停止维护,对YouTube Shorts这类新特性支持差,换成活跃分支yt-dlp可解决。
  3. 命令执行不安全:os.popen无法有效捕获错误,改用subprocess模块更可靠。

具体修改步骤

1. 安装yt-dlp

替换youtube-dl为yt-dlp,执行:

pip install yt-dlp

2. 修改extractor.py

import subprocess
import json

def extract_format_data(format_data):
    extension = format_data["ext"]
    format_name = format_data["format"]
    url = format_data.get("url", "")  # 避免部分格式无下载链接导致报错
    return {
        "extension": extension,
        "format_name": format_name,
        "url": url
    }
        
def extract_video_data_from_url(url):
    # 使用yt-dlp替代youtube-dl,支持Shorts和播放列表
    command = ["yt-dlp", "-j", url]
    try:
        # 捕获命令输出,按行处理多JSON内容
        result = subprocess.run(command, capture_output=True, text=True, check=True)
        output_lines = result.stdout.strip().split("\n")
        
        video_list = []
        for line in output_lines:
            if line:
                video_data = json.loads(line)
                title = video_data["title"]
                formats = video_data["formats"]
                thumbnail = video_data.get("thumbnail", "")
                # 过滤无下载链接的格式
                formatted_formats = [extract_format_data(fd) for fd in formats if fd.get("url")]
                video_list.append({
                    "title": title,
                    "formats": formatted_formats,
                    "thumbnail": thumbnail
                })
        
        # 单个视频返回对象,播放列表返回列表
        return video_list[0] if len(video_list) == 1 else video_list
    except subprocess.CalledProcessError as e:
        raise Exception(f"获取视频数据失败: {e.stderr}")
    except json.JSONDecodeError as e:
        raise Exception(f"解析JSON失败: {str(e)}")

3. 修改App.py添加错误处理

from flask import Flask, request, render_template, session, url_for, redirect, send_file
from extractor import extract_video_data_from_url

app = Flask(__name__)

@app.route("/")
def index():
      return render_template('index.html')

@app.route("/download", methods=["POST"])
def download():
   video_url = request.form["video_url"]
   try:
       video_data = extract_video_data_from_url(video_url)
       # 标记是否为播放列表
       is_playlist = isinstance(video_data, list)
       return render_template("Download.html", video_data=video_data, is_playlist=is_playlist)
   except Exception as e:
       # 传递错误信息到首页显示
       return render_template("index.html", error=str(e))

4. 调整Download.html模板(处理播放列表)

{% if error %}
    <p style="color: red;">{{ error }}</p>
{% endif %}

{% if is_playlist %}
    <h1>播放列表内容</h1>
    {% for video in video_data %}
        <div style="margin: 20px 0; padding: 10px; border: 1px solid #eee;">
            <h2>{{ video.title }}</h2>
            {% if video.thumbnail %}
                <img src="{{ video.thumbnail }}" alt="{{ video.title }}" style="max-width: 300px;">
            {% endif %}
            <div style="margin-top: 10px;">
                {% for fmt in video.formats %}
                    {% if fmt.url %}
                        <a href="{{ fmt.url }}" download="{{ video.title }}.{{ fmt.extension }}" style="margin-right: 10px;">
                            {{ fmt.format_name }} ({{ fmt.extension }})
                        </a><br>
                    {% endif %}
                {% endfor %}
            </div>
        </div>
    {% endfor %}
{% else %}
    <h1>{{ video_data.title }}</h1>
    {% if video_data.thumbnail %}
        <img src="{{ video_data.thumbnail }}" alt="{{ video_data.title }}" style="max-width: 500px;">
    {% endif %}
    <div style="margin-top: 20px;">
        {% for fmt in video_data.formats %}
            {% if fmt.url %}
                <a href="{{ fmt.url }}" download="{{ video_data.title }}.{{ fmt.extension }}" style="margin-right: 10px;">
                    {{ fmt.format_name }} ({{ fmt.extension }})
                </a><br>
            {% endif %}
        {% endfor %}
    </div>
{% endif %}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 19:40:29