Python/데이터분석(ABC 부트캠프)

[12일차] ABC 부트캠프 / konlpy 라이버리 및 동적 크롤링(유튜브 댓글 수집하기)

않새준 2024. 7. 10. 10:37

konlpy 라이브러리

 

11일차에 워드 클라우드로 시각화하기 위해 데이터를 텍스트(데이터 뭉치) 형태로 변환하였다.

 

위 방식으로 텍스트를 변환하면 공백을 기준으로 나누기 때문에 단어가 아닌 글자수로 텍스트가 쪼개지는 상황이 발생한다.

 

그렇기에 konlpy 라이브러리를 사용하여 자연어 처리를 통해 단어를 처리해야 할 필요가 있다.

 

자연어를 처리하기 위한 형태소 분석기는 3가지가 존재하는데 종류는 이러하다.

1. Kkma   2. Komoran  3. okt

 

1번과 2번은 신문과 뉴스같이 맞춤법이 정확하게 지켜지는 매체를 대상으로 사용한다.

 

3번은 맞춤법이 주로 자주 틀리는 SNS에서 사용되는 분석이이다.

 

그중에서도 Komoran을 사용해서 형태소를 분석하는 방법을 알아보자.

 

# 명사 단어 추출
# konlpy 한국어 자연어 처리를 위한 패키지
komoran = konlpy.tag.Komoran() # Kkma, Komoran, okt 형태소 분석기

변수명 = komoran.nouns(텍스트_데이터명)

 

위 방식을 사용하면 명사 단어를 추출하여 변수에 저장할 수 있다.

 

 


 

 

동적 크롤링

 

두 사이트의 페이지 소스코드를 확인해보자.

 

왼쪽의 소스코드는 HTML 코드가 정렬되어 있고, 코드 안에서 내가 원하는 정보가 들어있음을 확인할 수 있다.

 

그러나 왼쪽의 코드에서는 코드가 뒤죽박죽 섞여있으며, 내가 원하는 정보가 들어있음을 확인할 수 없다.

 

왼쪽 페이지에서는 효율적으로 페이지를 관리하기 위해 동적으로 할당하여 데이터를 전달하기에 내가 원하는 정보를 소스코드에서 찾을 수 없다.

 

이럴 경우에 사용되는 크롤링 방식이 동적 크롤링이다.

좌 : 정적 크롤링이 가능한 페이지 소스 / 우 : 정적 크롤링이 불가능한 페이지 소스

 

동적 크롤링은 자동화 코드를 통해 브라우저를 띄우고 해당 주소로 접속하여 사람이 행동하는 것과 비슷한 행동을 통해 데이터를 긁어오는 방식이다.

 

 


 

 

라이브러리 호출

 

 

동적 크롤링 및 시각화에 필요한 라이브러리이다.

 

크롤링 전 호출해주어야 한다.

from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.common.by import By
from bs4 import BeautifulSoup
import time

import pandas as pd

import warnings
warnings.filterwarnings('ignore')

 

 

 

웹 브라우저 자동화 설정

 

크롤링을 하기 위해서는 우선 창을 띄우는걸 가장 먼저 해야한다.

 

그렇기 위해서는 ChromeDriver를 사용하여 창을 화면에 띄우는 코드가 필요하다.

 

아래와 같이 코드를 작성해서 웹 브라우저 자동화 설정을 마쳐야 한다.

options = webdriver.ChromeOptions() # 크롬 설정 옵션 지정
options.add_argument('--no-sandbox') # 보안 기능인 샌드박스 비활성화
options.add_argument('--disable-dev-shm-usage') # dev/shm 디렉토리 사용 안함

# ChromeDriver 설치 및 서비스 설정
service = ChromeService(executable_path=ChromeDriverManager().install())

# driver 객체 설정
driver = webdriver.Chrome(service = service, options = options)
driver.set_window_size(0, 800) # 브라우저 창 크기 설정

# 영상 주소 드라이버에 저장
driver.get('영상 주소')

 

 

 

사람인 척 행동하기

 

크롤링에서는 사람같이 행동하는게 가장 중요하다.

 

코드를 이용하여 빠르게 데이터를 수집하면 좋겠지만, 일정 딜레이 없이 빠르게 행동한다면 IP가 차단당하기 때문이다.

 

그렇기에 행동과 행동 사이에 일정 딜레이를 고의로 만들어 주어야 한다.

 

이제는 사람인 척을 하기 위해 필요한 코드에 대해서 알아보자.

 

# 특정 초 딜레이
time.sleep('딜레이(초)')

# 스크롤 내리기
driver.execute_script('window.scrollTo(x-coord, y-coord)')

 

위 코드를 활용하여 일정 딜레이를 만들어주고, 스크롤을 내려주는 행동을 취할 수 있다.

 

 

 

댓글 크롤링

 

크롤링 방식은 정적 크롤링과 동일하게 Beautiful Soup를 이용해야 한다.

 

# 데이터 불러오기
html_source = driver.page_source

# 데이터 파싱
soup = BeautifulSoup(html_source, 'html.parser')

 

자동화 설정 때 사이트 url을 driver에 저장하였으므로 데이터를 불러올 때 driver객체의 page_source를 활용하여 가져오면 된다.

 

또한 데이터 파싱작업 또한 수행하여 크롤링 할 준비를 마친다.

 

 

 

BeautifulSoup select 메소드

 

 

크롤링 시에 알아야 할 BeautifulSoup의 메소드는 태그로 찾을 수 있는 find와 CSS Selector의 tag로 찾을 수 있는 select가 있다.

 

11일차에 find를 사용했다면 유튜브 댓글을 수집할 때는 select를 활용해야 한다.

 

select는 CSS 태그명과 id를 이용하여 원하는 데이터를 수집할 수 있다.

 

#ID값으로 태그 찾기
soup.select("CSS_이름#id_값")

 

위 같이 코드를 작성하여 CSS 태그와 id를 사용하여 원하는 데이터를 수집할 수 있다.

 

 

 

크롤링 데이터 저장

 

크롤링 후에는 데이터 프레임을 csv파일로 저장할 수도 있다.

# 데이터프레임 저장 to csv
데이터프레임_변수명.to_csv('파일이름.csv', encoding='인코딩_방식', index='인덱스_포함_여부')

 

 

 

 

파일 닫기

 

이제 파일에서 필요한 작업이 끝났으니 파일을 닫아주어야 한다.

 

파일을 닫는 방법은 아래와 같다.

 

# 파일 닫기
driver.close()

 

 


 

 

실습) 유튜브 댓글 수집하기

 

이제 내가 원하는 사이트의 댓글을 수집하는 실습을 해 볼 것이다.

 

크롤링 할 유튜브 영상은 아래와 같다.

# https://www.youtube.com/watch?v=teO9XFn9H3w&t=104s

 

우선 사이트에 들어가서 사람인 척 하기 위해 스크롤을 한번 내리는 과정까지의 코드는 위의 예제랑 내용이 거의 같다.

 

 

 

자동화 설정 및 사람인척 수행하기

from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.common.by import By
from bs4 import BeautifulSoup
import time

import pandas as pd

import warnings
warnings.filterwarnings('ignore')

options = webdriver.ChromeOptions()
options.add_argument('--no-sandbox') # 보안 기능인 샌드박스 비활성화
options.add_argument('--disable-dev-shm-usage') # dev/shm 디렉토리 사용 안함

service = ChromeService(executable_path=ChromeDriverManager().install())

driver = webdriver.Chrome(service = service, options = options)
driver.set_window_size(0, 800)

# 유튜브 영상 접속 / https://www.youtube.com/watch?v=teO9XFn9H3w&t=104s
driver.get('https://www.youtube.com/watch?v=teO9XFn9H3w&t=104s')
driver.implicitly_wait(10) # 화면 렌더링 기다리기

# 사람인 척 하기
time.sleep(10)
driver.execute_script('window.scrollTo(0, 800)')
time.sleep(5)

 

라이브러리를 호출하고 자동화 설정까지 끝낸 후 정상적으로 브라우저를 화면에 띄워 사람인 척 스크롤를 한번 내려주었다.

 

이때 중요한건 딜레이를 반드시 발생시키도록 하자.

 

이제 유튜브 영상 스크롤을 가장 아래까지 내려서 댓글을 모두 확인할 수 있게 만들어주는 작업이 필요하다.

 

# 댓글 수집을 위한 스크롤 내리기
last_height = driver.execute_script('return document.documentElement.scrollHeight')

while True:
    print('스크롤 중...')
    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(3)

 

반복문에 들어가기 전 변수에 웹 페이지의 전체 높이를 저장해주어야 한다.

 

반복문의 탈출 조건을 생성하기 위해서인데 스크롤을 내리고, 내릴 때마다 높이를 last_height 변수에 저장해주었다.

 

그리고 더이상 내릴 수 없을때까지 내리면 사이트의 높이는 더이상 변수에 다른 값이 저장되지 않을 것이고, 높이 값이 동일하다면 반복문 탈출이 가능하도록 탈출 조건을 지정해주었다.

 

 

 

댓글 크롤링

 

스크롤을 가장 아래까지 내렸으니 이제는 댓글을 수집해 보려고 한다.

# 댓글 크롤링
html_source = driver.page_source
soup = BeautifulSoup(html_source, 'html.parser')

 

우선 html을 읽어오고 파싱과정을 수행해야 한다.

 

# 댓글 태그 리스트 가져오기
comment_list = soup.select('ytd-expander#expander')
comment_final = []

 

CSS Selector의 id를 이용하여 댓글 정보를 comment_list에 저장하였다.

 

또한 comment_list에서 추출한 텍스트를 저장하기 위한 comment_final 변수를 선언하였다.

 

# 댓글 텍스트 추출
for i in range(len(comment_list)):
    temp_comment = comment_list[i].text
    temp_comment = temp_comment.replace('\n', '').replace('\t', '').replace('\r', '').strip()
    print(temp_comment)
    comment_final.append(temp_comment) # 댓글 내용 리스트에 담기

 

이제 댓글 정보가 담긴 리스트의 길이만큼 반복문을 돌며 텍스트 정보만을 담을 것이다.

 

텍스트 정보를 임시로 담을 변수에 넣어주고, 임시 변수에서 줄바꿈과 탭, 띄어쓰기를 필터링 하여 텍스트 데이터를 담을 변수에 최종적으로 담게 된다.

 

 

 

데이터 프레임으로 변경 및 파일 저장

 

# 데이터프레임 만들고 저장 (list -> dict -> df)
youtube_dic = {'댓글 내용' : comment_final}
youtube_df = pd.DataFrame(youtube_dic)

# csv 저장
youtube_df.to_csv('주파수_크롤링_2024.csv', encoding='utf-8-sig', index=False)

 

데이터 프레임으로 저장하기 위해 '댓글 내용' 과 댓글 텍스트 정보를 딕셔너리화 시켰다.

 

이제 Pandas에서 제공하는 DataFrame() 함수를 사용해서 해당 딕셔너리를 매개변수로 전달하여 데이터프레임으로 저장하였다.

 

또한, 데이터프레임을 csv 파일로 변경하기 위해 to_csv 메서드를 사용하여 파일로 저장하였다.

 

 

 

파일 닫기

 

이제 파일에서 수행할 작업이 끝났으니 파일을 닫아주도록 하였다.

driver.close()

 

 

 

코드 전문

 

from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.common.by import By
from bs4 import BeautifulSoup
import time

import pandas as pd

import warnings
warnings.filterwarnings('ignore')

options = webdriver.ChromeOptions()
options.add_argument('--no-sandbox') # 보안 기능인 샌드박스 비활성화
options.add_argument('--disable-dev-shm-usage') # dev/shm 디렉토리 사용 안함

service = ChromeService(executable_path=ChromeDriverManager().install())

driver = webdriver.Chrome(service = service, options = options)
driver.set_window_size(0, 800)

# 유튜브 영상 접속 / https://www.youtube.com/watch?v=teO9XFn9H3w&t=104s
driver.get('https://www.youtube.com/watch?v=teO9XFn9H3w&t=104s')
driver.implicitly_wait(10) # 화면 렌더링 기다리기

# 사람인 척 하기
time.sleep(10)  
driver.execute_script('window.scrollTo(0, 800)')
time.sleep(5)

# 댓글 수집을 위한 스크롤 내리기
last_height = driver.execute_script('return document.documentElement.scrollHeight')

while True:
    print('스크롤 중...')
    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(3)
    
# 댓글 크롤링
html_source = driver.page_source
soup = BeautifulSoup(html_source, 'html.parser')

# 댓글 태그 리스트 가져오기
comment_list = soup.select('ytd-expander#expander')
comment_final = []

print('댓글 수 : ', str(len(comment_list)))

# 댓글 텍스트 추출
for i in range(len(comment_list)):
    temp_comment = comment_list[i].text
    temp_comment = temp_comment.replace('\n', '').replace('\t', '').replace('\r', '').strip()
    print(temp_comment)
    comment_final.append(temp_comment) # 댓글 내용 리스트에 담기
    
# 데이터프레임 만들고 저장 (list -> dict -> df)
youtube_dic = {'댓글 내용' : comment_final}
youtube_df = pd.DataFrame(youtube_dic)

print('==' * 30)
print('크롤링 종료')
print('==' * 30)

# 수집된 데이터 확인하기
print(youtube_df.info())

youtube_df.to_csv('주파수_크롤링_2024.csv', encoding='utf-8-sig', index=False)

print('==' * 30)
print('파일 저장 완료...')

# 브라우저 닫기
driver.close()