DevCourse TIL Day2 Week5

김태준·2023년 5월 2일
0

Data Enginnering DevCourse

목록 보기
19/93
post-thumbnail

✅ Youtube Comment Crawling

유튜브에 존재하는 인기 급상승 채널에 올라온 영상들에 한해 작성된 댓글 읽어와 데이터프레임으로 저장하기

코드로 구현하면 다음과 같다.

# 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 데이터를 크롤링하는게 난이도가 쉽지 않다고 느껴짐.

❗ 추가로 필요한 작업

  1. 각 영상 url 별 html소스를 따올 때 스크롤을 진행할때마다 html코드가 바뀜을 위 코드 작업을 prototype으로 진행하며 알게 됌.
  2. 결과적으로 local 상황에서 작업을 진행하므로 인기 급상승 채널에 올라온 모든 영상에 한해 데이터를 처리할 수 없을 거라는 판단이 들었다.
    -> 회의를 거쳐 영상 개수 제한 or 스크롤 횟수 제한을 걸어 한계점으로 가져가야 할듯..!
profile
To be a DataScientist

0개의 댓글