유튜브 영상 다운로드
유튜브 영상을 다운로드하다가 내가 원하는 음악을 다운 받기는 요원하다는 생각이 들었다.
왜냐하면 K-pop이나 music 같은 검색 결과는 범위가 너무 넓기 때문이다.
그리고 아이돌 혹은 아티스트 명으로 검색하더라도 커버나 댄스 영상들을 구분할 수 없기 때문이다.
이에 목표를 다소 변경하여 아티스트 명을 받아 채널을 검색하고 해당 아티스트가 발표한 곡들을 다운받기로 하였다.
def get_channel_id(youtube, artist_name):
search_response = youtube.search().list(
q=artist_name,
part='snippet',
type='channel',
maxResults=1
).execute()
if search_response['items']:
return search_response['items'][0]['id']['channelId']
else:
return None
이것이 해당 채널을 호출하는 함수이다.
이름을 받아와 검색하고 그것을 해당 채널의 이름과 채널 식별 번호로 완료하는 식이다.
이 다음에는 발표곡에 해당하는 재생목록을 받아오면 youtube-dl 명령을 활용하여 곡을 받을 수 있었지만 몇 가지 문제점이 있었다.
1. API로는 발매곡이라는 기준을 가지고 하는 검색의 어려움
2. 재생목록이 꼭 해당 채널의 영상은 아님
3. 영상 중 노래가 아닌 영상이 많음, API는 이를 구분 못함
위와 같은 문제 때문에 해당 채널의 영상을 전부 가져오거나 그러면서도 음악은 가져오지 못했기 때문이다.
이 때문에 API를 대신하여 웹 크롤링 기법을 사용하였다.
웹 크롤링 기법을 사용하기 위해서는 Selenium 라이브러리를 활용하였다.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
from bs4 import BeautifulSoup
import time
chrome_options = Options()
# 필요한 경우, Chrome을 headless 모드로 실행하기 위한 옵션을 추가
# chrome_options.add_argument("--headless")
driver = webdriver.Chrome(options=chrome_options)
# 유튜브 아티스트 채널로 이동
driver.get('https://www.youtube.com/@LESSERAFIM_official/releases')
# "발표곡" 섹션을 찾아 클릭 이벤트 시뮬레이션
time.sleep(5) # 페이지 로딩 대기
tab = driver.find_element(By.CSS_SELECTOR, '[tab-title="발표곡"]')
tab.click()
# 클릭 후 콘텐츠 로딩을 위해 충분한 시간 대기
time.sleep(5)
# 변경된 페이지 소스로 BeautifulSoup 객체 생성
soup = BeautifulSoup(driver.page_source, 'html.parser')
# 변경된 페이지에서 원하는 정보 추출
# 예를 들어, 동적으로 로드된 비디오 제목들을 가져오기
# 정확한 선택자는 페이지 구조에 따라 다를 수 있으므로, 실제 페이지를 검사하여 확인 필요
videos = soup.find_all('a', class_='yt-simple-endpoint focus-on-expand style-scope ytd-rich-grid-media')
for video in videos:
print(video.text.strip())
driver.quit()
이것이 처음 크롤링을 통해 만들은 내용이다.
클래스를 통해 제목 타이틀을 찾아 리스트화 하여 준다.
이게 그 결과이다.
문제는 이 제목만으로는 영상을 찾을 수 없다는 것이다.
그래서 여기서 이를 좀 더 활용하여 지정된 class를 찾은 다음 해당 클래스나 그 부모 태그에서 href 속성을 찾아 링크를 따오는 것을 목표로 하였다.
from selenium import webdriver
from bs4 import BeautifulSoup
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
import time
# Selenium 설정
chrome_options = Options()
# 필요한 경우 Chrome을 headless 모드로 실행하기 위한 옵션을 추가
# chrome_options.add_argument("--headless")
driver = webdriver.Chrome(options=chrome_options)
# 웹 페이지 로딩
driver.get('https://www.youtube.com/@LESSERAFIM_official/releases')
# 충분한 로딩 시간 대기
time.sleep(5)
# 페이지의 HTML 소스 가져오기
page_source = driver.page_source
# BeautifulSoup 객체 생성
soup = BeautifulSoup(page_source, 'html.parser')
# 'style-scope ytd-playlist-thumbnail' 클래스를 포함하는 모든 요소 찾기
elements = soup.find_all(class_='style-scope ytd-rich-grid-media')
# 중복 링크를 방지하기 위한 집합
found_hrefs = set()
# 링크 추출
for element in elements:
# 각 요소의 부모에서 <a> 태그를 찾습니다.
a_tag = element.find_parent('a')
if a_tag:
href = a_tag.get('href')
if href and href not in found_hrefs:
found_hrefs.add('https://www.youtube.com/' + href) # 새로운 링크를 집합에 추가
for href in found_hrefs:
print(href)
# 드라이버 종료
driver.quit()
이것이 해당 내용이다.
처음에 당면한 문제는 해당 채널의 홈에서는 원하는 결과를 얻을 수 없다는 점이었다.
왜냐하면 채널 메인은 동적으로 구성되어 있었으며 클릭하지 않으면 표시되지 않았다.
그렇기에 발표곡을 확인하기 위하여 발표곡을 cs 탐색하여 클릭해준 다음 탐색하는 방법을 사용하였다.
그런데 코드를 짜다보니 /releases라는 것을 주소 후방에 붙이면 처음부터 링크가 발매곡이 선택된 상태로 표현되는 것을 알게되었다.
이를 API 방식에서도 활용할 수 있을까?
만약 가능하다면 웹 크롤링을 하지 않아도 되기 때문에 드라이버에 의지하지 않아도 되는 장점이 있다.
하지만 API 방식과 웹 크롤링의 도구가 다르기 때문에 동일한 방식으로 한다고 하여 API가 해당 주소에 있는 재생목록을 유의미하게 파악할 수 있는지는 더욱 확인해 볼 사안이다.