본문 바로가기
프로그래밍/AI

유튜브 인기동영상 시각화

by slowin 2024. 11. 24.

작성일: 24.11.24(일)

개요

  • 유튜브 인기동영상 데이터 스크래핑
  • 데이터로 다양하게 시각화

스크래핑

모듈 import

import requests
from bs4 import BeautifulSoup
from selenium import webdriver
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import pandas as pd
import seaborn as sns
import numpy as np

셀레니움 활용하여 스크래핑

url = "https://www.youtube.com/feed/trending"
driver = webdriver.Chrome()
driver.implicitly_wait(3)
html = driver.get(url)
page = driver.page_source

bs_obj = BeautifulSoup(page, "html.parser")

thumb_nodes = bs_obj.find_all("div", {"class": "text-wrapper style-scope ytd-video-renderer"})

데이터 전처리

channel_infos = []
for index, node in enumerate(thumb_nodes):
    # 채널명
    channel_node = node.find("a", {"class": "yt-simple-endpoint style-scope yt-formatted-string"})
    view_count_node = node.find("span", {"class": "inline-metadata-item style-scope ytd-video-meta-block"})
    title_node = node.find("a", {"id": "video-title"})

    clean_title = title_node.text.strip().replace('\n', '')

    channel_info = {
        'index': index,
        'name':channel_node.text,
        'viewCount':parse_view_count(view_count_node.text),
        'title':title_node.text,
        'cleanTitle': clean_title
    }
    channel_infos.append(channel_info)

channel_infos 샘플

# 샘플
[{'index': 0,
  'name': 'Mnet K-POP',
  'viewCount': 6460000,
  'title': '\n\n[#2024MAMA] G-DRAGON - 무제(Untitled, 2014)+POWER+HOME SWEET HOME+뱅뱅뱅+FANTASTIC BABY | Mnet 241123 방송\n',
  'cleanTitle': '[#2024MAMA] G-DRAGON - 무제(Untitled, 2014)+POWER+HOME SWEET HOME+뱅뱅뱅+FANTASTIC BABY | Mnet 241123 방송'},
 {'index': 1,
  'name': 'Mnet K-POP',
  'viewCount': 8130000,
  'title': '\n\n[#2024MAMA] ROSÉ (로제), Bruno Mars - APT. | Mnet 241122 방송\n',
  'cleanTitle': '[#2024MAMA] ROSÉ (로제), Bruno Mars - APT. | Mnet 241122 방송'},

DataFrame으로 만들기

df = pd.DataFrame(channel_infos)

산포도

sns.set_theme(style="whitegrid")
plt.figure(figsize=(8, 5))
sns.scatterplot(x="viewCount", y="index", data=df, color="blue", s=100)
plt.title("scatter plot", fontsize=16)
plt.xlabel("VIEWS")
plt.ylabel("RANK")
plt.xticks([1e7, 2e7, 2.5e7], ['1K','1M', '1.5M'])

plt.show()

날짜별로 시청수

# 날짜별로 시청수
df = pd.DataFrame(channel_infos)
# 날짜 변환
dates = [datetime.strptime(date, '%Y-%m-%d') for date in df['uploadDate']]
views = df['viewCount']

# 산포도 그리기
plt.scatter(dates, views, color='red', label='Views')
plt.title('View Count Distribution')
plt.xlabel('Upload Date')
plt.ylabel('View Count')
plt.xticks(rotation=45)

plt.legend()
plt.show()

워드클라우드

from wordcloud import WordCloud

word_freq = {info['name']: info['viewCount'] for info in channel_infos}

white_wordcloud = WordCloud(font_path="AppleGothic",
                            width=480, height=480,
                            background_color='white').generate_from_frequencies(word_freq)

black_wordcloud = WordCloud(font_path="AppleGothic",
                            width=480, height=480,
                            background_color='black').generate_from_frequencies(word_freq)

# 시각화
fig, ax = plt.subplots(1, 2, figsize=(12, 6))
fig.tight_layout()

# White background 워드클라우드
ax[0].imshow(white_wordcloud, interpolation='bilinear')
ax[0].axis('off')
ax[0].set_title('White Background')

# Black background 워드클라우드
ax[1].imshow(black_wordcloud, interpolation='bilinear')
ax[1].axis('off')
ax[1].set_title('Black Background')

plt.show()

 

Log Scale Distribution

# 로그 스케일 변환
df['log_viewCount'] = np.log10(df['viewCount'])

# KDE 계산
kde = sns.kdeplot(x=df['log_viewCount'])
x, y = kde.get_lines()[0].get_data()

# 최대 밀도값과 해당 로그 값 찾기
max_idx = np.argmax(y)  # 최대 밀도값의 인덱스
max_density = y[max_idx]
max_log_value = x[max_idx]
max_actual_value = 10**max_log_value  # 로그 값에서 실제 값으로 변환

# 커스텀 축 포맷 설정
def log_to_actual(x, pos):
    actual = 10**x
    if actual >= 1_000_000:
        return f"{int(actual/1_000_000)}M"
    elif actual >= 1_000:
        return f"{int(actual/1_000)}K"
    return f"{int(actual)}"

formatter = FuncFormatter(log_to_actual)
plt.gca().xaxis.set_major_formatter(formatter)

# 최대값 점선과 텍스트 추가
plt.axvline(x=max_log_value, color='red', linestyle='--', label=f"Peak: {int(max_actual_value)}")
plt.text(max_log_value, max_density, f"{int(max_actual_value):,}", color='red', ha='center', va='bottom')

# 제목과 축 레이블 추가
plt.xlabel('ViewCount (Log Scale)')
plt.ylabel('Density')
plt.title('ViewCount Distribution (Log Scale)')
plt.legend()
plt.show()

Top 10 채널 시청수

df2 = pd.DataFrame(channel_infos)
df_agg = df2.groupby('name', as_index=False)['viewCount'].sum()


# 시청수 기준 상위 N개 추출
top_n = 20
df_top = df_agg.nlargest(top_n, 'viewCount').sort_values(by='viewCount', ascending=True)

# 시각화
plt.barh(df_top['name'], df_top['viewCount'])
plt.xlabel('viewCount')
plt.ylabel('name')
plt.xticks([100000, 5000000, 10000000, 20000000], [ '100만', '500만', '1000만', '2000만'])
plt.title('Top N 인기 채널 시청수')

plt.show()

 

마치며

데이터를 통해 시각화를 해보았다. 시각화를 통해 데이터의 특성을 파악하고 이를 바탕으로 좋은 인사이트를 도출하는데 좋은 접근이라는 것을 경험했다. 다양한 시각화를 시도해 보면서 숨겨진 패턴이나 관계를 탐구하는 경험을 더 많이 해봐야겠다.

 

'프로그래밍 > AI' 카테고리의 다른 글

통계학(2) 확률론(Probability Theory)  (0) 2024.12.02
통계학(1) 순열과 조합  (0) 2024.12.02
아나콘다란?  (1) 2024.11.22
서울시 범죄현황 통계자료 분석 및 시각화  (0) 2024.11.19
Pandas 기본 문법  (1) 2024.11.19