[PYTHON/YOUTUBE] 유튜브 동영상 자막 다운로드하기
■ 유튜브 동영상의 자막을 다운로드하는 방법을 보여준다. ▶ main.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
import os import json import csv from youtube_transcript_api import YouTubeTranscriptApi from datetime import datetime class YouTubeSubtitleExtractor: def __init__(self): self.outputDirectoryName = "subtitles" self.createOutputDirectory() def createOutputDirectory(self): """출력 디렉토리가 없으면 생성한다.""" if not os.path.exists(self.outputDirectoryName): os.makedirs(self.outputDirectoryName) def getVideoID(self, url): """유튜브 URL에서 비디오 ID를 반환한다.""" videoID = None if "youtube.com/watch?v=" in url: videoID = url.split("watch?v=")[1].split("&")[0] elif "youtu.be/" in url: videoID = url.split("youtu.be/")[1] return videoID def getAvailableLanguagesDictionary(self, videoID): """사용 가능한 자막 언어 딕셔너리를 반환한다.""" try: transcriptList = YouTubeTranscriptApi.list_transcripts(videoID) languageDictionary = {} for transcript in transcriptList: languageDictionary[transcript.language_code] = transcript.language return languageDictionary except Exception as exception: print(f"언어 목록을 가져오는 중 오류가 발생했습니다 : {str(exception)}") return None def get_subtitles(self, video_id, language = "ko"): """지정된 언어의 자막을 가져온다.""" try: transcript = YouTubeTranscriptApi.get_transcript(video_id, languages=[language]) return transcript except Exception as exception: print(f"자막을 가져오는 중 오류가 발생했습니다 : {str(exception)}") return None def getTimeString(self, secondCount): """초 단위 시간을 HH:MM:SS 형식으로 변환한다.""" hourCount = int(secondCount / 3600) minuteCount = int((secondCount % 3600) / 60) secondCount = secondCount % 60 return f"{hourCount:02d}:{minuteCount:02d}:{secondCount:06.3f}" def saveAsSRT(self, transcript, filePath): """자막을 SRT 형식으로 저장한다.""" try: with open(filePath, "w", encoding = "utf-8") as bufferedWriter: for index, item in enumerate(transcript, 1): startTimeString = self.getTimeString(item["start"]) endTimeString = self.getTimeString(item["start"] + item["duration"]) bufferedWriter.write(f"{index}\n") bufferedWriter.write(f"{startTimeString} --> {endTimeString}\n") bufferedWriter.write(f"{item['text']}\n\n") return True except Exception as exception: print(f"SRT 파일 저장 중 오류가 발생했습니다 : {str(exception)}") return False def saveAsTXT(self, transcript, filename): """자막을 TXT 형식으로 저장한다.""" try: with open(filename, "w", encoding = "utf-8") as bufferedWriter: for item in transcript: bufferedWriter.write(f"[{self.getTimeString(item['start'])}] {item['text']}\n") return True except Exception as exception: print(f"TXT 파일 저장 중 오류가 발생했습니다 : {str(exception)}") return False def saveAsJSON(self, transcript, filePath): """자막을 JSON 형식으로 저장한다.""" try: with open(filePath, "w", encoding = "utf-8") as bufferedWriter: json.dump(transcript, bufferedWriter, ensure_ascii = False, indent = 4) return True except Exception as exception: print(f"JSON 파일 저장 중 오류가 발생했습니다 : {str(exception)}") return False def saveAsCSV(self, transcript, filePath): """자막을 CSV 형식으로 저장한다.""" try: with open(filePath, "w", encoding = "utf-8", newline = "") as bufferedWriter: writer = csv.writer(bufferedWriter) writer.writerow(["시작 시간", "종료 시간", "자막"]) for item in transcript: writer.writerow( [ self.getTimeString(item["start"]), self.getTimeString(item["start"] + item["duration"]), item['text'] ] ) return True except Exception as exception: print(f"CSV 파일 저장 중 오류가 발생했습니다 : {str(exception)}") return False def extractSubtitleFiles(self, url, language = "ko"): """URL에서 자막을 추출하고 여러 형식으로 저장한다.""" videoID = self.getVideoID(url) if not videoID: print("올바른 유튜브 URL이 아닙니다.") return False languageDictionary = self.getAvailableLanguagesDictionary(videoID) if not languageDictionary: print("자막 언어 딕셔너리를 가져올 수 없습니다.") return False print("\n=== 사용 가능한 자막 언어 ===") for code, name in languageDictionary.items(): print(f"{code}: {name}") transcript = self.get_subtitles(videoID, language) if not transcript: print("자막을 가져올 수 없습니다.") return False timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") baseFilePath = f"{self.outputDirectoryName}/{videoID}_{language}_{timestamp}" resultDictionary = { "SRT" : self.saveAsSRT (transcript, f"{baseFilePath}.srt" ), "TXT" : self.saveAsTXT (transcript, f"{baseFilePath}.txt" ), "JSON" : self.saveAsJSON(transcript, f"{baseFilePath}.json"), "CSV" : self.saveAsCSV (transcript, f"{baseFilePath}.csv" ) } print("\n=== 저장 결과 ===") for formatName, success in resultDictionary.items(): status = "성공" if success else "실패" print(f"{formatName} : {status}") return True def main(): extractor = YouTubeSubtitleExtractor() while True: url = input("\n유튜브 URL을 입력하세요 (종료하려면 'q' 입력) : ") if url.lower() == "q": break language = input("자막 언어 코드를 입력하세요 (기본값 : ko): ").strip() or "ko" extractor.extractSubtitleFiles(url, language) print("\n자막 파일이 'subtitles' 폴더에 저장되었습니다.") if __name__ == "__main__": main() |
▶ requirements.txt
1 2 3 4 5 6 7 8 |
certifi==2024.8.30 charset-normalizer==3.4.0 idna==3.10 requests==2.32.3 urllib3==2.2.3 youtube-transcript-api==0.6.2 |
※ pip install youtube-transcript-api 명령을 실행했다.