유튜브에 존재하는 인기 급상승 채널에 올라온 영상들에 한해 작성된 댓글 읽어와 데이터프레임으로 저장하기
코드로 구현하면 다음과 같다.
# scraping에 필요한 라이브러리 호출
# 웹 드라이버 제어
from selenium import webdriver
# 크롬 드라이버 자동 설치, 업데이트
from webdriver_manager.chrome import ChromeDriverManager
# 크롬 웹 드라이버 실행
from selenium.webdriver.chrome.service import Service
# 선택자 체킹
from selenium.webdriver.common.by import By
# 웹 페이지 wait 용도 위해 사용
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# html, xml 파싱 위해 사용
from bs4 import BeautifulSoup
import pandas as pd
import time
# 반복문 작업 진행 시각화
from tqdm import tqdm
# 정규 표현식 사용
import re
# 경고 메시지 무시
import warnings
warnings.filterwarnings('ignore')
warnings.filterwarnings(action = 'ignore')
# user-agent, 크롤링할 웹 사이트 url 변수 저장
user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36'
# 인기 급상승 채널 - 최신
url = "https://www.youtube.com/feed/trending"
# 크롬 드라이버 실행 시 options 설정
options = webdriver.ChromeOptions()
# 크롬창 띄우기 X
# options.add_argument('headless')
options.add_argument('window-size=1920*1080')
# 크롤링 성능 향상
options.add_argument('disable-gpu')
# 읽어올 언어 설정
options.add_argument('lang=ko_KR, en_US')
options.add_argument('user-agent=' + user_agent)
driver = webdriver.Chrome(service = Service(ChromeDriverManager().install()), options = options)
driver.get(url)
# 로딩 대기
driver.implicitly_wait(3)
# 카테고리별 클릭 버튼 추가하기
# ~~~ code
# 페이지 스크롤 내리기 기능 설정 (모든 동영상 로딩)
last_height = driver.execute_script("return document.documentElement.scrollHeight")
while True:
driver.execute_script("window.scrollTo(0, document.documentElement.scrollHeight);")
# 내릴 때마다 댓글 창 표시 위해 5초 대기 걸기
time.sleep(5)
new_height = driver.execute_script("return document.documentElement.scrollHeight")
if new_height == last_height:
break
last_height = new_height
time.sleep(1.5)
# 팝업 제거 (유튜브 프리미엄 광고 창으로 comment crawling 안되는 경우 방지)
try:
driver.find_element_by_css_selector('#dismiss-button > a').click()
except:
pass
# 모든 동영상 링크 수집
video_links = []
for video in driver.find_elements(By.XPATH, "//a[@id='video-title']"):
video_links.append(video.get_attribute("href"))
# 상위 영상 n개 추출
video_links = video_links[:2]
# 모든 url의 html리스트로 저장
html_sources = []
# 각 url 별로
for video_link in video_links:
# 드라이버 실행
driver.get(video_link)
# continue
# driver.maximize_window()
# 댓글 로딩 대기
driver.implicitly_wait(10)
# 페이지 스크롤 다운 (모든 동영상 로딩) 하면 너무 오래걸림.. (문제 발생)
last_height = driver.execute_script("return document.documentElement.scrollHeight")
while True:
driver.execute_script("window.scrollTo(0, document.documentElement.scrollHeight);")
time.sleep(3)
new_height = driver.execute_script("return document.documentElement.scrollHeight")
if new_height == last_height:
break
last_height = new_height
time.sleep(2)
# 팝업 제거
try:
driver.find_element_by_css_selector("#dismiss-button > a").click()
except:
pass
time.sleep(2)
# html 소스 변수 저장
html_source = driver.page_source
html_sources.append(html_source)
# 빈 데이터프레임 만들어 변수로 저장한 후 해당 df에 ID, COMMENT 저장
df = pd.DataFrame(columns = ['Youtube_ID', 'Comment'])
# 각 HTML 소스 마다
for html_source in html_sources:
# html 코드 파싱
soup = BeautifulSoup(html_source, 'html.parser')
# id 리스트
id_list = soup.select('div#header-author > h3 > #author-text > span')
# comment 리스트
comment_list = soup.select('yt-formatted-string#content-text')
# 파싱해서 넣을 실제 데이터 리스트
youtube_id = []
youtube_comment = []
# 각 comment 별 id, comment 출력
for i in range(len(comment_list)):
id_ = id_list[i].text
id_ = id_.replace('\n', '').replace('\t', '').replace(' ', '').strip()
# 댓글 작성자 저장
youtube_id.append(id_)
comment_ = comment_list[i].text
comment_ = comment_.replace('\n', '').replace('\t', '').replace('\r', '').strip()
# 댓글 내용 저장
youtube_comment.append(comment_)
# DIC으로 값들 저장하며 DataFrame화
youtube_data = {'Youtube_ID' : youtube_id, 'Comment' : youtube_comment}
youtube_df = pd.DataFrame(youtube_data)
# 만들어 둔 df에 concat하여 데이터 적재
df = pd.concat([df, youtube_df], axis=0)
df.reset_index(drop=True, inplace=True)
df
# 결과
실제 youtube에 업로드 된 영상 내 댓글 크롤링을 실제로 진행해보면서 느낀 문제점들이 몇가지 있다.
1. 코드 구현 난이도 上 (있는 편..)
2. 모든 댓글을 크롤링하기 위해 스크롤을 끝까지 진행해야 함. (댓글 많을수록 시간 증가)
3. 그렇다고 댓글을 다 읽어오지도 않음....
-> implicitly_wait를 10초나 걸어두었는데도 무슨 이유에서인지 작성된 모든 댓글을 가져오지 못하는 문제가 발견되었다.
음.. 내 생각엔 현 페이지의 html 소스를 다 따온 이후, 스크롤을 진행해야하는데 그러려면 태그 이름을 활용해 조건을 걸어주는 Explicit Wait를 추가로 활용해 주어야 할 것으로 보인다..
-> 확실히 naver에 비해 google 데이터를 크롤링하는게 난이도가 쉽지 않다고 느껴짐.
- 각 영상 url 별 html소스를 따올 때 스크롤을 진행할때마다 html코드가 바뀜을 위 코드 작업을 prototype으로 진행하며 알게 됌.
- 결과적으로 local 상황에서 작업을 진행하므로 인기 급상승 채널에 올라온 모든 영상에 한해 데이터를 처리할 수 없을 거라는 판단이 들었다.
-> 회의를 거쳐 영상 개수 제한 or 스크롤 횟수 제한을 걸어 한계점으로 가져가야 할듯..!