[Python] - youtube comment crawler [유튜브 댓글 크롤러]
in Python on Python, Youtube, Crawler, Crawling
이번에 그냥 개인 공부로 youtube
의 댓글들을 크롤링
해서 단어 빈도수 분석
을 진행해보려고한다. 먼저 계획은
유튜브 크리에이터
의 동영상 리스트를 순회하며URL
을 수집한다.- 수집한
URL
을 들어가 스크롤링하며 댓글을 전부csv
파일로 저장한다. - 저장된
csv
파일 전부를merge
한다. merge
된 파일을 읽어단어 빈도수
를 분석한다.
순서대로 진행해본 결과를 설명하겠다.
0. 준비
먼저 Python
으로 크롤링 하기위해서 beautifulsoup
와 selenium
을 설치해주자. 참고로 youtube
를 beautifulsoup
으로 크롤링하려면 lxml
도 설치해주어야한다.
pip install bs4
pip install selenium
pip install lxml
1. 동영상 리스트 크롤링
나는 WERACLE
의 박위씨 채널을 대상으로 진행하기로 결정하였다.
이렇게 많은 동영상들의 URL
을 다 가져와야한다. 여기서 문제는 동영상들이 한번에 로드되는 것이 아니라 무한스크롤
방식으로 스크롤을 내려야 새로운 동영상이 로드된다. 그렇기에 스크롤 관련 코드를 작성해주어야한다.
스크롤과 관련된 코드는 매우 간단하다. 하지만 여기서 또 발생한 문제는 바로 맨아래로 스크롤을 해주면 로딩이 되지 않는 문제가 발생한다… 그래서 스크롤이 천천히 내려가게 코드를 작성해주었다.
def scroll(driver, scroll_time, wait_time):
last_page_height = driver.execute_script(
"return document.documentElement.scrollHeight"
)
# 기존 위치
stand_height = 0
# 이전과 차이값
sub_height = last_page_height
while True:
# 현재 높이
current_height = driver.execute_script(
"return document.documentElement.scrollHeight"
)
for i in range(10):
driver.execute_script(
f"window.scrollTo(0, {stand_height + (sub_height/10 * i)});"
)
time.sleep(scroll_time)
time.sleep(wait_time)
# 스크롤 내린 페이지의 높이
new_page_height = driver.execute_script(
"return document.documentElement.scrollHeight"
)
stand_height = last_page_height
sub_height = new_page_height - last_page_height
# 현재높이와 내린 높이가 같으면 스크롤 끝
if new_page_height == last_page_height:
break
last_page_height = new_page_height
time.sleep(wait_time)
스크롤
이 새로 생성된height
를10번
끊어서 이동한다.유튜브 동영상 리스트
는 괜찮을지 몰라도댓글
창에서는 맨아래로 이동하면댓글
이 존재하지 않는 부분이 있어서 끊어서 이동하지 않으면댓글
이 새로 생성되지 않는다.
# 채널 동영상 탭을 주면 동영상별 url 다 가져오기
url = "https://www.youtube.com/channel/UCT8l_qvhkgTBu8-7wz1hZ0Q/videos"
driver.get(url)
time.sleep(3.0)
# 위에 만든 스크롤 함수
# 맨 아래 페이지까지 이동
scroll(driver, 0.3, 1)
# youtube는 lxml로 분석
req = driver.page_source
soup = BeautifulSoup(req, "lxml")
# 동영상 전체 링크 가져오기
links = soup.select("div#dismissable > ytd-thumbnail > a#thumbnail")
# 링크별 댓글 가져오기 (이제 구현해보자)
for link in links:
get_comments(driver, "https://www.youtube.com" + link.attrs['href'])
이제 모든 링크는 가져왔고 이제 comment
를 읽어보자
2. 동영상 댓글 읽어오기
driver.get(url)
time.sleep(3)
# 스크롤링
scroll(driver, 0.3, 1)
time.sleep(2)
req = driver.page_source
soup = BeautifulSoup(req, "lxml")
똑같이 스크롤
을 전부 내려주고 내용을 가져와 lxml
로 분석을 진행하자.
youtube_title = soup.find(
"yt-formatted-string", class_="style-scope ytd-video-primary-info-renderer"
).text
유튜브
동영상의 title
을 가져오고
youtube_comments_IDs = soup.select("div#header-author > a > span")
youtube_comments = soup.select("yt-formatted-string#content-text")
유튜브
동영상 댓글
들의 아이디
와 내용
을 가져온다.
# 문장에서 쓸모없는 데이터 삭제
def get_str(str_tmp):
str_tmp = str_tmp.replace("\n", " ")
str_tmp = str_tmp.replace("\t", " ")
str_tmp = str_tmp.replace(" ", " ")
str_tmp = str_tmp.strip()
return str_tmp
그런데 여기에 쓸모없는 문자
가 포함된 경우가 많다. 제거해는 함수
를 만들자.
str_IDs = []
str_comments = []
for i in range(len(youtube_comments)):
str_comments.append(get_str(str(youtube_comments[i].text)))
str_IDs.append(get_str(str(youtube_comments_IDs[i].text)))
이제 가져온 데이터
들을 list
에 저장해주자.
이제 데이터들을 저장해보자!
file_ = open(f"{youtube_title}_comments.csv", "w", encoding="utf-8", newline="")
wr = csv.writer(file_)
num = 1
wr.writerow(["NUM", "ID", "COMMENT"])
for i in range(len(str_comments)):
wr.writerow([num, str_IDs[i], str_comments[i]])
num += 1
file_.close()
csv
파일로 title
을 파일 이름으로 설정하여서 저장하였다.
이제 기본적인 크롤러
는 완상되었다. 한번 구동해보자. 스크롤 하는데 오랜시간이 걸린다… 켜 놓고 커피마시며 책이나 읽자…
자 아주 성공적으로 크롤링이 완성되었다!! 이제 분석을 진행해보자!!
3. csv파일 merge하기
일단 위의 크롤러는 동영상 갯수만큼 csv를 생성하였다. 이제 하나로 합쳐보자.
input_path = './'
IDs = []
comments = []
for input_file in glob.glob(os.path.join(input_path, '*.csv')):
row_counter = 1
with open(input_file, 'r', newline='') as csv_in_file:
freader = csv.reader(csv_in_file)
for row in freader:
IDs.append(row[1].strip())
comments.append(row[2].strip())
row_counter += 1
with open('merge.csv', 'w', encoding='utf-8', newline='') as merge_file:
wr = csv.writer(merge_file)
num = 1
wr.writerow(["NUM", "ID", "COMMENT"])
for i in range(len(IDs)):
wr.writerow([num, IDs[i], comments[i]])
num += 1
나는 경로에 있는 모든 csv
파일을 합치도록 코드를 작성하였다. 실행해보면 성공적으로 merge.csv
파일이 생성된다. 내용을 확인해보자
댓글이 어마무시하게 많다… 이제 단어 빈도수를 체크해보자
4. 단어 빈도수 체크하기
단어 빈도수를 체크하기위해 konlpy
사용하였다. 먼저 설치를 진행해주자.
pip install konlpy
나는 여기서 Hannanum
을 사용하였고 또 Counter
를 사용하여 갯수를 계산하였다.
comments = []
with open("merge.csv", "r", newline="") as csv_in_file:
freader = csv.reader(csv_in_file)
row_num = 1
for row in freader:
text = re.sub("[0-9]+", "", row[2])
text = re.sub("[-=+,#/\?:^$.@*\"※~&%ㆍ·!』\\‘’|\(\)\[\]\<\>`'…》]", "", text)
comments.append(text)
row_num += 1
먼저 merge.csv
파일에 있는 comment
들을 전부 합쳤다. 정규표현식을 통해 쓸모없는 특수기호
는 삭제하였다.
hannanum = Hannanum()
words_frequency = Counter()
count = 0
for comment in comments:
comment = comment.strip()
if comment != "":
word_list = hannanum.nouns(comment)
words_frequency += Counter(word_list)
count += 1
이제 Hannanum
을 통해서 단어의 명사를 뽑아내고
hannanum.nouns(comment)
그리고 Counter
를 통해서 중복 갯수를 쉽게 계산하였다. 참고로 Hannanum
에 공백 문자열
을 넣으니 에러가 발생해서 제외하는 코드를 추가했다.
이제 csv
파일로 저장해보자
with open('analysis.csv', 'w', encoding='utf-8', newline='') as result_file:
wr = csv.writer(result_file)
num = 1
wr.writerow(["NUM", "NOUNS", "COUNT"])
for n,c in words_frequency.most_common():
wr.writerow([num, n, c])
num += 1
정상적으로 결과를 확인 할 수 있다!!