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()