Youtubeの複数動画を1つにまとめる方法 Python

この記事ではPythonを使用してYoutubeの動画を1つにまとめる方法を紹介します。

ソースコード

必要に応じてライブラリはインストールして下さい。

import yt_dlp
from moviepy.editor import VideoFileClip, concatenate_videoclips
import os
import re

# ダウンロードする動画のURLリスト
video_urls = [
    "https://youtu.be/video1",
    "https://youtu.be/video2",
    "https://youtu.be/video3",
    "https://youtu.be/video4"
]

# 保存先のディレクトリを指定
output_dir = "/Users/your_directory"
if not os.path.exists(output_dir):
    os.makedirs(output_dir)

# ファイル名から特殊文字を削除する関数
def clean_filename(filename):
    return re.sub(r'[\\/*?:"<>|]', "", filename)

# 動画をダウンロードし、ファイル名のリストを作成
video_files = []
ydl_opts = {
    'format': 'bestvideo+bestaudio/best',
    'merge_output_format': 'mp4',
    'outtmpl': os.path.join(output_dir, '%(title)s.%(ext)s'),
    'force_overwrite': True
}

with yt_dlp.YoutubeDL(ydl_opts) as ydl:
    for url in video_urls:
        info = ydl.extract_info(url, download=True)
        clean_title = clean_filename(info['title'])
        old_filename = os.path.join(output_dir, f"{info['title']}.{info['ext']}")
        new_filename = os.path.join(output_dir, f"{clean_title}.{info['ext']}")
        if os.path.exists(old_filename) and old_filename != new_filename:
            try:
                os.rename(old_filename, new_filename)
            except FileNotFoundError:
                print(f"警告: ファイル '{old_filename}' が見つかりません。リネームをスキップします。")
        video_files.append(new_filename)

# ダウンロードした動画をClipオブジェクトのリストに変換
clips = []
for file in video_files:
    if os.path.exists(file):
        clips.append(VideoFileClip(file))
    else:
        print(f"警告: ファイル '{file}' が見つかりません。スキップします。")

# 動画を連結
if clips:
    final_clip = concatenate_videoclips(clips, method="compose")

    # 結合した動画を保存
    output_file = os.path.join(output_dir, "video_name.mp4")
    final_clip.write_videofile(output_file, codec="libx264", audio_codec="aac")

    # 一時ファイルを削除
    for clip in clips:
        clip.close()
    for file in video_files:
        try:
            os.remove(file)
        except FileNotFoundError:
            print(f"警告: ファイル '{file}' が見つかりません。削除をスキップします。")

    print("動画の結合が完了しました。")
else:
    print("結合する動画がありません。")

保存先のディレクトリにmp4形式のファイルで1まとまりになった動画が保存されます。

video_urlsに好きな動画のURLを追加していって下さい。

# ダウンロードする動画のURLリスト
video_urls = [
    "https://youtu.be/video1",
    "https://youtu.be/video2",
    "https://youtu.be/video3",
    "https://youtu.be/video4"
]

クライアントにビデオ保存の容量が必要なのと動画が長すぎたりするとタイムアウトになったり上手く保存できなくなるので注意して下さい。

使用用途、開発経緯

私は趣味で好きなバンドのライブなどによく行くのですが、ライブの前は公開されているセットリストにある曲を聞き込んでから行きたいのでプレイリストを作るのが目的でした。

Amazon MusicやSpotifyの課金ユーザーであれば同様の事ができますが、ケチなので自分で作ることにしました笑

勿論、好きなYoutuberの動画を集めて1本の動画にしたり色々な使い道は他にもあると思います。

ただURLを打ち込むのが面倒なので、自動でURLを入れられるようなプログラミングもついでに書いてみる事にしました。

まずはセットリストの取得に私がよく使用しているサイトからアーティストの曲名だけを取得するコードです。

import requests
from bs4 import BeautifulSoup
import re

def get_setlist_songs(url):
    # Send a request to fetch the content of the page
    response = requests.get(url)
    response.raise_for_status()

    # Parse the page content
    soup = BeautifulSoup(response.text, 'html.parser')

    # Find the setlist songs
    setlist_songs = soup.find_all('li', class_='setlistParts song')

    # Extract and clean the song titles
    songs = []
    for song in setlist_songs:
        song_text = song.get_text(strip=True)
        # Remove "(...)" and "Play Video"
        song_text = re.sub(r'\s*\(.*?\)\s*', '', song_text)
        song_text = song_text.replace('Play Video', '').strip()
        songs.append(song_text)
    
    return songs

# Main function to get the URL input from the user
def main():
    url = input("Please enter the URL of the setlist page: ")
    songs = get_setlist_songs(url)
    for song in songs:
        print(song)

if __name__ == "__main__":
    main()

こちらのサイト用のコードなので他のサイト等で使用する場合はコードの改変が必須になるので注意してください。

それを元にスクレイピングやseleniumを駆使してYoutubeからURLの取得を試みましたが、アクセスできませんでした…

試したコード


import requests
from bs4 import BeautifulSoup
import re
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
import time

def get_setlist_songs(url):
    response = requests.get(url)
    response.raise_for_status()
    soup = BeautifulSoup(response.text, 'html.parser')
    setlist_songs = soup.find_all('li', class_='setlistParts song')
    songs = []
    for song in setlist_songs:
        song_text = song.get_text(strip=True)
        song_text = re.sub(r'\s*\(.*?\)\s*', '', song_text)
        song_text = song_text.replace('Play Video', '').strip()
        songs.append(song_text)
    return songs

def search_youtube_video_url(song_title):
    query = f"{song_title} lyrics"
    search_url = f"https://www.youtube.com/results?search_query={query}"
    
    # Set up Selenium
    chrome_options = Options()
    chrome_options.add_argument("--headless")
    driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=chrome_options)
    driver.get(search_url)
    time.sleep(2)  # Wait for the page to load
    
    videos = driver.find_elements(By.ID, 'video-title')

    video_url = None
    for video in videos:
        try:
            video_url = video.get_attribute('href')
            if video_url and '/watch?v=' in video_url:
                driver.get(video_url)
                time.sleep(2)  # Wait for the video page to load
                
                try:
                    view_count_text = driver.find_element(By.CLASS_NAME, 'view-count').text
                    view_count = int(re.sub(r'\D', '', view_count_text))
                    if view_count >= 100000:
                        driver.quit()
                        return video_url
                except:
                    continue
        except StaleElementReferenceException:
            continue

    driver.quit()
    return None

def main():
    url = input("Please enter the URL of the setlist page: ")
    songs = get_setlist_songs(url)
    
    for song in songs:
        video_url = search_youtube_video_url(song)
        if video_url:
            print(f"{song}: {video_url}")
        else:
            print(f"{song}: No suitable video found")

if __name__ == "__main__":
    main()

このような場合はローカルクライアントの座標をベースにマウスを動かせば取得できない事もないのですが、安定性に欠けるのとこのプログラムにそこまでの価値はないので諦めます。

Googleが提供しているAPIを使用すれば実現が可能のようです。

サンプルコード

from googleapiclient.discovery import build

# YouTube Data API key
API_KEY = 'YOUR_API_KEY'

# YouTube API クライアントを作成
youtube = build('youtube', 'v3', developerKey=API_KEY)

def search_youtube(query):
    # 検索リクエストを実行
    search_response = youtube.search().list(
        q=query,
        type='video',
        part='id,snippet',
        maxResults=50
    ).execute()

    # 検索結果から動画IDを取得
    video_ids = [item['id']['videoId'] for item in search_response['items']]

    # 動画の詳細情報を取得
    videos_response = youtube.videos().list(
        id=','.join(video_ids),
        part='statistics'
    ).execute()

    # 再生回数が10万回以上の動画を探す
    for item in videos_response['items']:
        view_count = int(item['statistics']['viewCount'])
        if view_count >= 100000:
            video_id = item['id']
            return f'https://www.youtube.com/watch?v={video_id}'

    return None

# 使用例
search_query = input("検索したい文字列を入力してください: ")
result = search_youtube(search_query)

if result:
    print(f"検索結果の中で最初に見つかった10万回以上再生されている動画のURL: {result}")
else:
    print("条件に合う動画が見つかりませんでした。")

Follow me!

関連記事

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

PAGE TOP