작성일: 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 |