[PYTHON]-(03) 크롤링[유튜브]
유튜브 크롤링
셀레니움과 유튜브 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. 크롤링 결과
/1.png)
Leave a comment