유튜브 크롤링

셀레니움과 유튜브 API활용 크롤링을 해보자

※ 게시글의 전체적인 내용은 카페 회원가입을 해야지만 가능하다.

1. 날짜 지정하여 유튜브 영상의 업로드 날짜, 비디오id, 조회수 수집 및 데이터프레임 생성(use Selenium)

def Selenium_Youtube(start_date, end_date, start_date_2, end_date_2, frq): # date_2 = date + 6day
       # Ytb_df = Selenium_Youtube('2021-09-06', '2021-09-13', '2021-09-12', '2021-09-19', '7D')
    # Web Crawling from fandom with Selenium
    from bs4 import BeautifulSoup as bs # 17 line
    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from selenium.webdriver.chrome.service import Service as ChromeService
    from webdriver_manager.chrome import ChromeDriverManager
    from selenium.webdriver.common.keys import Keys
    import time
    import pandas as pd
    from googleapiclient.discovery import build # 88 line
    import warnings # 경고창 무시
    warnings.filterwarnings('ignore')
    
    # 셀레니움 옵션 설정
    options = webdriver.ChromeOptions()
    # options.add_argument('headless') # 크롬 띄우는 창 없애기
    options.add_argument('window-size=1920x1080') # 크롬드라이버 창크기
    options.add_argument("disable-gpu") #그래픽 성능 낮춰서 크롤링 성능 쪼금 높이기
    options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36") # 네트워크 설정
    options.add_argument("lang=ko_KR") # 사이트 주언어
    options.add_argument('disable-infobars')
    options.add_argument('--disable-extensions')
    options.add_argument('--mute-audio')
    options.add_argument('--blink-settings=imagesEnabled-false')
    options.add_argument('incognito')
    
    driver = webdriver.Chrome('C:/Users/opqrs/Desktop/selenium/chromedriver.exe',options=options)

    rng = len(pd.date_range(start_date, end_date, freq = frq))
    day = 'day_'
    
    for i in range(rng):
        
        start_day = str(pd.date_range(start_date, end_date, freq = frq)[i])[:10] # 유튜브 검색 시작 날짜 지정
        end_day = str(pd.date_range(start_date_2, end_date_2, freq = frq)[i])[:10] # 유튜브 검색 종료 날짜 지정
        location = "https://www.youtube.com/results?search_query=%EC%98%A4%EC%A7%95%EC%96%B4%EA%B2%8C%EC%9E%84+after%3A"+start_day+"+before%3A"+end_day  # 유튜브 검색 url
        driver.get(location)
        
        while True: # 마지막 게시물까지 스크롤
            last_page_height = driver.execute_script("return document.documentElement.scrollHeight")
            driver.execute_script("window.scrollTo(0, document.documentElement.scrollHeight);")
            time.sleep(2)
            new_page_height = driver.execute_script("return document.documentElement.scrollHeight")
            if new_page_height == last_page_height:
                break
            
        page = driver.page_source
        soup = bs(page,'html.parser')
        globals()[day + start_day.replace('-','_')] = soup.find_all('a',"yt-simple-endpoint style-scope ytd-video-renderer")

    df = pd.DataFrame()
    for i in range(rng): # 수집 데이터 날짜, Video_id, 조회수로 구분
        j=0
        day_check = str(pd.date_range(start_date, end_date, freq = frq)[i])[:10]    
        while True:
            df = df.append(pd.DataFrame([ [day_check, globals()[day + day_check.replace('-','_')][j]['href'][-11:], globals()[day + day_check.replace('-','_')][j]['aria-label'].split('조회수 ')[1]] ]))
            j += 1
            if j == len(globals()[day + day_check.replace('-','_')]):
                break
    return df



Ytb_df = Selenium_Youtube('2021-08-09', '2022-01-17', '2021-08-15', '2022-01-23', '7D')

# 전역변수 제거 
s = '2021-08-09'; e = '2021-08-30'; frq = '7D'
rng = len(pd.date_range(s, e, freq = frq))
for i in range(rng):
    del globals()['day_'+ str(pd.date_range(s, e, freq = frq)[i])[:10].replace('-','_')]
del i, rng, s, e, frq

# 조회수 처리
Ytb_df.columns=['Date','Video_id','View_c'] # colnames 지정 
for i in range(0,len(Ytb_df)):  # 조회수 처리 및 숫자형변환
    Ytb_df.iloc[i,2] = int(Ytb_df.iloc[i,2].split('회')[0].replace(',',''))
# Date 오름 View 내림차순 정렬
Ytb_df.sort_values(['Date','View_c'],ascending = [True,False], inplace=True)
# Set index
Ytb_df.set_index('Date', inplace=True)

1.1 결과

Ytb_df = {'date' : ['2021-08-11','2021-08-16','2021-09-02'],
        'video_id' : ['CZpl1qHXFqI', 'lFMobna17QU', 'b96oSVw75lA'],
        'type' : ['공개발표', '티저예고', '공식예고']}
Ytb_df = pd.DataFrame(Ytb_df) # date, video_id, type의 열 순서는 고정

1.1 결과와 같은 형식대로 데이터프레임이 생성된다.

2. 셀레니움으로 생성한 데이터프레임에서 비디오 id 추출하여 유튜브 API로 댓글 수집

'''
Ytb_df = {'date' : ['2021-08-11','2021-08-16','2021-09-02'],
        'video_id' : ['CZpl1qHXFqI', 'lFMobna17QU', 'b96oSVw75lA'],
        'type' : ['공개발표', '티저예고', '공식예고']}
Ytb_df = pd.DataFrame(Ytb_df) # date, video_id, type의 열 순서는 고정
'''
def Youtube_API(Ytb_df):
		import time
		import pandas as pd
		from googleapiclient.discovery import build # 88 line
		import warnings # 경고창 무시
		warnings.filterwarnings('ignore')
    result = pd.DataFrame()
    for i in range(0,len(Ytb_df)): # Youtube API로 추출 
        api_key = '' 
        video_id = Ytb_df.iloc[i,1] # video id 선택
        comments = list()
        api_obj = build('youtube', 'v3', developerKey=api_key)
        response = api_obj.commentThreads().list(part='snippet,replies', videoId=video_id, maxResults=100).execute()
        
        while response:
            for item in response['items']:
                comment = item['snippet']['topLevelComment']['snippet']
                comments.append([comment['textDisplay'], comment['authorDisplayName'], comment['publishedAt'], comment['likeCount'], Ytb_df.iloc[i,2]]) # video id 선택]) # 텍스트, 작성자명, 날짜, 좋아요수
                if item['snippet']['totalReplyCount'] > 1:
                    for reply_item in item['replies']['comments']:
                        reply = reply_item['snippet']
                        comments.append([reply])
                        comments.append([reply['textDisplay'], reply['authorDisplayName'], reply['publishedAt'], reply['likeCount'], Ytb_df.iloc[i,2]])
         
            if 'nextPageToken' in response:
                response = api_obj.commentThreads().list(part='snippet,replies', videoId=video_id, pageToken=response['nextPageToken'], maxResults=100).execute()
            else:
                break            
        result = result.append(pd.DataFrame(comments))
        
    result.columns = ['comment','author','date','nums_like','type']
    idx_1 = result[result['comment'].str.contains('videoId', na=True)].index  # 중복 대댓글 삭제 
    result.drop(idx_1, inplace =True)
    result = result.reset_index().drop('index', axis=1)
    
    return result
result = Youtube_API(Ytb_df)

3. 전처리

def Preprocessing(df): # 컬럼명 'comment', 'date' 필수
    import string
    import re
    # 댓글에 영상시간을 링크로 걸어둔 부분 제거    
    a = df[df['comment'].str.contains('a href')] 
    a.reset_index(inplace=True)
    a = a.drop('index', axis=1)
    for i in range(0,len(a)): # 안 돌아갈 때까지 반복 2번이면 대체로 완료됨
        a['comment'][i] = a['comment'][i].replace(re.search('<(.+?)>', a['comment'][i]).group(0),'')
        
    for i in range(0,len(a)): 
        a['comment'][i] = a['comment'][i].replace(re.search('<(.+?)>', a['comment'][i]).group(0),'')
        
    df.loc[a.index] = a

    # 특수문자 제거
    for i in range(0,len(df)):
        df['comment'][i] = df['comment'][i].translate(str.maketrans('', '', string.punctuation))
    
    # 날짜 변환
    for i in range(0,len(df)):
        df['date'][i] = df['date'][i][:10]
    df['date'] = pd.to_datetime(df['date'], format='%Y-%m-%d')
    
    return df

result = Preprocessing(result)

def Div_week(df, start_date, end_date, frq): # date = 'yyyy-mm-dd' / frq = '7D' / 월요일로 설정
    
    rng = len(pd.date_range(start_date, end_date, freq = frq)) - 1
    
    for i in range(rng): # 주별 댓글 수
        idx = df[(df['date']>=str(pd.date_range(start_date, end_date, freq = frq)[i])[:10]) & (df['date']<=str(pd.date_range(start_date, end_date, freq = frq)[i+1])[:10])].index
        df.loc[idx,'dvd_week'] = str(pd.date_range(start_date, end_date, freq='7D')[i])[:10]
    
Div_week(result, '2021-06-28', '2022-10-17', '7D')

3. 크롤링 결과

정의

Leave a comment