데이터사이언스 대학원 생활/TextAnalytics

파이썬 유튜브(Youtube)API 활용 /원하는 키워드 영상 댓글 추출

데욱 2023. 7. 14. 17:00

내가 '닌텐도'에 대해서 관심이있는데 닌텐도와 관련된 영상들의 댓글 데이터를 가져오고 싶다면?

방법은 생각보다 간단하다.

Youtube Data API를 활용해서 닌텐도와 관련있는 영상 검색 + 그 영상의 댓글 데이터를 가져오면 된다! 

차근차근 따라해보자.


1. Google API 프로젝트 생성

API 및 서비스 – API 및 서비스 – My First Project – Google Cloud Console

 

Google 클라우드 플랫폼

로그인 Google 클라우드 플랫폼으로 이동

accounts.google.com

사이트에 들어가 로그인을 완료하면, 다음과 같은 화면이 있을 것이다.

프로젝트 선택 - 새 프로젝트

새 프로젝트를 눌러 프로젝트를 생성해준다.


2. YouTube Data API 활성화

API 및 서비스의 라이브러리 페이지로 이동한다.

 

youtube를 검색하고 youtube data api v3를 클릭하고 사용을 눌러 활성화 한다.


3.  사용자 인증 정보 탭에서 API키 발급 

아래와 같은 과정을 거쳐 키를 발급 받는다.


키 발급이 완료 되었으면 직접 사용해보자!

과정은 다음과 같다.

 

1. 유튜브 검색 api를 사용해서 원하는 토픽과 관련된 동영상의 ID를 수집

2. 동영상 ID의 댓글을 수집

 

 

1. 유튜브 검색 api를 사용해서 원하는 토픽과 관련된 동영상의 ID를 수집

 

먼저 필요 패키지를 설치한다.

pip install google-api-python-client
pip install oauth2client

 

그 후 필요한 모듈을 import 한다.

from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from oauth2client.tools import argparser

 

발급받은 api키를 입력해주고 실행한다.

DEVELOPER_KEY = "api_key" # 발급 받은 API 키 삽입
YOUTUBE_API_SERVICE_NAME="youtube" # 이용할 API
YOUTUBE_API_VERSION="v3" #이용할 API version
youtube = build(YOUTUBE_API_SERVICE_NAME,YOUTUBE_API_VERSION,developerKey=DEVELOPER_KEY)
# build() 함수로 YouTube API 클라이언트 객체 생성

 

그리고 api로 원하는 키워드를 검색한다!

search_response = youtube.search().list(q = "닌텐도",  # 검색어
                                        order = "relevance", # 정확도순 
                                        part = "snippet",  # 필수매개변수(API 응답이 포함하는 search 리소스 속성 하나 이상의 쉼표로 구분된 목록을 지정)
                                        maxResults = 5).execute() # 결과 집합에 반환해야 하는 최대 항목 수를 지정 0~50
                                        # execute()로 수행

그러면 저렇게 videoId 가 나오는데

저것이 핵심이다!!!! 이는 뒤에서 설명하겠다!

타이틀과 videoId를 추출해보자!

 

타이틀을 깔끔하게 정리할 간단한 코드를 작성해주고

import re

def remove_tag(my_str):
    ## 태그를 지우는 함수
    p = re.compile('(<([^>]+)>)')
    return p.sub('', my_str)

def sub_html_special_char(my_str):
    ## 특수문자를 나타내는 &apos;, &quot를 실제 특수문자로 변환
    p1 = re.compile('&lt;')
    p2 = re.compile('&gt;')
    p3 = re.compile('&amp;')
    p4 = re.compile('&apos;')
    p5 = re.compile('&quot;')

    result = p1.sub('\<', my_str)
    result = p2.sub('\>', result)
    result = p3.sub('\&', result)
    result = p4.sub('\'', result)
    result = p5.sub('\"', result)
    return result

이제 item들을 가져온다!

## 아이템 목록을 가져옴
items = search_response['items']

## 아이템을 순회하며 제목과 비디오 ID를 추출
for item in items:
    if item['id']['kind'] == 'youtube#video': # 채널에 대한 검색 정보는 제외
        title = sub_html_special_char(remove_tag(item['snippet']['title'])) # 함수적용
        video_id = item['id']['videoId'] # 비디오 ID 그대로 추출
        print('title:', title)
        print('video_id:', video_id)
        print()

후후... 잘 되는 것을 확인했으니 코드를 한번에 정리하고 최대로 가져와보자!

 

from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from oauth2client.tools import argparser
import re

def remove_tag(my_str):
    ## 태그를 지우는 함수
    p = re.compile('(<([^>]+)>)')
    return p.sub('', my_str)

def sub_html_special_char(my_str):
    ## 특수문자를 나타내는 &apos;, &quot를 실제 특수문자로 변환
    p1 = re.compile('&lt;')
    p2 = re.compile('&gt;')
    p3 = re.compile('&amp;')
    p4 = re.compile('&apos;')
    p5 = re.compile('&quot;')

    result = p1.sub('\<', my_str)
    result = p2.sub('\>', result)
    result = p3.sub('\&', result)
    result = p4.sub('\'', result)
    result = p5.sub('\"', result)
    return result

DEVELOPER_KEY = "your_api"
YOUTUBE_API_SERVICE_NAME="youtube"
YOUTUBE_API_VERSION="v3"
youtube = build(YOUTUBE_API_SERVICE_NAME,YOUTUBE_API_VERSION,developerKey=DEVELOPER_KEY)


search_response = youtube.search().list(q = "닌텐도",  # 검색어
                                        order = "relevance", # 정확도순 
                                        part = "snippet",  # 필수매개변수(API 응답이 포함하는 search 리소스 속성 하나 이상의 쉼표로 구분된 목록을 지정)
                                        maxResults = 50).execute() # 결과 집합에 반환해야 하는 최대 항목 수를 지정 0~50

items = search_response['items']

## 아이템을 순회하며 제목과 비디오 ID를 추출
title = []
video_id = []

for item in items:
    if item['id']['kind'] == 'youtube#video': # 채널에 대한 검색 정보는 제외
        title.append(sub_html_special_char(remove_tag(item['snippet']['title']))) # 함수적용 후 title list에 저장
        video_id.append(item['id']['videoId']) # 비디오ID를 video_id list에 저장

 

위 결과를 시각화 하고 싶다면 title과 video_id를 merge하면 된다.

근데 난 저게 중요한게 아니다. 

videoId가 왜 중요하다고 했느냐...!

바로 특정 동영상의 videoId를 기반으로 댓글을 가져오기 때문이다.

 

후후 그럼 이제 댓글을 추출해보자.

 

2. 동영상 ID의 댓글을 수집

먼저 특정 video_id의 댓글을 수집해보자.

import pandas
from googleapiclient.discovery import build
 
api_key = 'yourapicode'
api_obj = build('youtube', 'v3', developerKey=api_key)

# 특정 videoID를 사용해서 그 동영상의 정보 가져오기
video_id = '3q-wccutH6M'
comments = []
response = api_obj.commentThreads().list(part='snippet,replies', # part=snippet을 설정하는 경우 API 응답은 하위 속성도 모두 포함 
                                         videoId=video_id, 
                                         maxResults=5).execute()
response['items']

오호호호 잘나온다.

위 결과에는 없지만, 댓글의 대댓글은 따로 또 설정을 해줘야한다.

그래서... 아래 코드를 실행해준다면 다 가져올 수 있도록 한다!!!

## 응답에 대한 반복문 시작
while response:
    
    ## API로부터 돌아온 응답 중 'items'내에 필요 데이터 존재
    for item in response['items']:
        
        ## 댓글에 대한 데이터가 있는 곳으로 이동
        comment = item['snippet']['topLevelComment']['snippet']
        ## comments에 댓글내용, 댓글 쓴 사람의 닉네임, 쓴 날짜, 좋아요 추천수 데이터 가져오기
        comments.append([comment['textDisplay'], comment['authorDisplayName'], comment['publishedAt'], comment['likeCount']])
        
        ## 대댓글을 위함(댓글아래에 다른 사람의 댓글)
        if item['snippet']['totalReplyCount'] > 0:
            
            print('닉네임 \'',comment['authorDisplayName'],'\' 에 대한 대댓글이 존재합니다.')  #확인을 위한 코드
            
            for reply_item in item['replies']['comments']:
                reply = reply_item['snippet']
                comments.append([reply['textDisplay'], reply['authorDisplayName'], reply['publishedAt'], reply['likeCount']])
    
    ## 더 많은 댓글존재할 경우, 다음 페이지를 가져오기
    if 'nextPageToken' in response:
        #다음 페이지의 댓글을 요청
        response = api_obj.commentThreads().list(part='snippet,replies', 
                                                 videoId=video_id, 
                                                 pageToken=response['nextPageToken'], 
                                                 maxResults=50).execute()
    else:
        break

df = pandas.DataFrame(comments)
df


최종 코드

 

## 특정 키워드 검색하여 키워드와 관련된 동영상의 댓글 추출 최종 코드

## 필요 모듈 import
import pandas
import re
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError

## title 데이터의 태그 지우는 함수 설정(title은 분석할 데이터는 아니기 때문에 생략해도됌)
def remove_tag(my_str):
    # 태그를 지우는 함수
    p = re.compile('(<([^>]+)>)')
    return p.sub('', my_str)

def sub_html_special_char(my_str):
    # 특수문자를 나타내는 &apos;, &quot를 실제 특수문자로 변환
    p1 = re.compile('&lt;')
    p2 = re.compile('&gt;')
    p3 = re.compile('&amp;')
    p4 = re.compile('&apos;')
    p5 = re.compile('&quot;')

    result = p1.sub('<', my_str)
    result = p2.sub('>', result)
    result = p3.sub('&', result)
    result = p4.sub("'", result)
    result = p5.sub('"', result)
    return result

## API 사용을 위한 키 및 정보입력

DEVELOPER_KEY = 'your code'
YOUTUBE_API_SERVICE_NAME = "youtube"
YOUTUBE_API_VERSION = "v3"
api_obj = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, developerKey=DEVELOPER_KEY)

## 특정 키워드를 검색했을 때 (검색 API 활용)
search_response = api_obj.search().list(
    q="닌텐도",
    order="relevance",
    part="snippet",
    maxResults=50
).execute()

## API로부터 돌아온 응답 중 'items'내에 필요 데이터 존재
items = search_response['items']

## 빈 리스트 생성
title = []
video_id = []

## title과 videoid 데이터 가져오기 
for item in items:
    if item['id']['kind'] == 'youtube#video':
        title.append(sub_html_special_char(remove_tag(item['snippet']['title'])))
        video_id.append(item['id']['videoId'])
'''
검색 API 사용은 여기까지
'''

## 검색완료된 특정 키워드 videoID에서 정보 가져오기 (videos api활용)              

comments = []

## video_id 반복하며 인덱스 추출
for vid in video_id:
    try:
        ## 데이터 요청
        response = api_obj.commentThreads().list(
            part='snippet,replies',
            videoId=vid,
            maxResults=50
        ).execute()

        ## 받은 데이터 중에서 필요한 데이터만 저장
        while response:
            for item in response['items']:
                comment = item['snippet']['topLevelComment']['snippet']
                comments.append([
                    comment['textDisplay'],
                    comment['authorDisplayName'],
                    comment['publishedAt'],
                    comment['likeCount']
                ])

                ## 대댓글을 위함
                if item['snippet']['totalReplyCount'] > 0:
                    for reply_item in item['replies']['comments']:
                        reply = reply_item['snippet']
                        comments.append([
                            reply['textDisplay'],
                            reply['authorDisplayName'],
                            reply['publishedAt'],
                            reply['likeCount']
                        ])

            ## 다음페이지 댓글을 위함
            if 'nextPageToken' in response:
                response = api_obj.commentThreads().list(
                    part='snippet,replies',
                    videoId=vid,
                    pageToken=response['nextPageToken'],
                    maxResults=50
                ).execute()
            else:
                break
    ## 해당 동영상의 댓글이 비활성화되어 있는 경우 발생하는 오류를 제어
    except HttpError as e:
        if e.resp.status == 403:
            ## 댓글 비활성화 된 동영상의 경우 문구 발생
            print(f"Comments disabled for video: {vid}")
        else:
            raise e

## 데이터프레임으로 저장 및 csv파일로 이동
df = pandas.DataFrame(comments)
df.to_csv('YoutubeAPI_nintendo.csv', header=['comment', 'author', 'date', 'num_likes'], encoding='utf-8-sig', index=None)

여러가지 오류 수정도 하고... 해서 

위 코드를 돌린다면...

우리는 원하는 키워드 영상의 댓글을 싹 다 가져올 수 있게된다!!!