나른한 코딩 생활

[14일차] ABC 부트캠프 이미지 크롤링 본문

ABC 부트캠프

[14일차] ABC 부트캠프 이미지 크롤링

GerHerMo 2024. 7. 13. 15:27

어제 멜론차트 크롤링 이후 각 조별로 원하는 연도의 랭킹을 조사한 후

해당 자료를 가지고 데이터 분석 후 발표하는 준비를 했다.

 

오전에는 전 시간에 조사한 데이터를 각 조별로 발표하는 시간을 가졌다.

우리 조는 10년 단위로 유행한 장르를 분석 했다.


연대별 유행한 장르 분석

크롤링을 위한 로컬 py 코드는 13일차를 확인해주길 바라며

간단하게 시각화 파트만 보고 넘어가도록 하자

 

임포트도 동일하게 진행하였다.

!pip install koreanize-matplotlib

import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots

import pandas as pd

from google.colab import files
from collections import Counter
import re


import matplotlib.pyplot as plt
import koreanize_matplotlib
from wordcloud import WordCloud

 

년도는 각각 1990, 2000, 2010이며, 각 연도대의 TOP 50 노래의 장르를 분석했다.

 

df_90=pd.read_csv('/content/멜론 1990년대 Top50_20240710.csv')

 

각각의 csv 파일 명은 ~1990~ 부분만 바뀐다  

변수 명도 마찬가지로 df_90 / df_00 / df_10 으로 설정하였다.

song_df_90['장르'].unique()
song_dict_90 = dict(Counter(song_df_90['장르']))
new_df_90 = pd.DataFrame(song_dict_90.items(), columns=['장르', 'count'])
new_df_90

new_df_90

 

각 장르의 중복 제거 -> unique()

song_dict_90 변수는 장르 개수를 센 후 딕셔너리 형태로 만들었다.

이후 DataFrame 으로 변환 하였다. -> new_df_90

 

2000년대 2010년대 또한 동일하게 진행

color_map = {'발라드': 'springgreen',
             '댄스':'crimson',
             '랩/힙합':'navy',
             '랩':'navy',
             'R&B/Soul':'aqua',
             'R&B':'aqua',
             '인디음악':'darksalmon',
             '성인가요/트로트':'pink',
             '록/메탈': 'fuchisia',
             '록':'fuchisia',
             '트로트':'cadetblue',
             '포크/블루스':'orange'
             }

 

이후 3개의 차트를 비교할때 pie 차트를 사용하는데 각 장르별 색깔을 지정하여

비교 분석에 용이하도록 color_map 딕셔너리 변수를 만들었다.

 

이상이 없는지 pie 차트를 하나 그려보자

px.pie(new_df_90, values='count', names='장르',color='장르',color_discrete_map=color_map,title='1990년대 유행한 음악 장르')

1990년대 유행한 음악 장르의 Pie차트

 

이젠 3가지의 pie 차트를 비교하기 위해 한 영역에 3개의 pie 차트를 그려보자

 

아래 코드는 3개의 pie 차트를 한 영역에 그리기 위한 코드구문이다

# 함수 정의: color_map을 사용하여 색상 리스트 생성
def get_colors(labels, color_map):
    return [color_map[label] for label in labels]
# labels 는 리스트 형태 // color_map 은 딕셔너리 형태
# color_map 의 색상을 키값으로 labels 안에서 뽑는다 -> 이를 리스트화 [] 시켜 return 한다 (반환)

# 각각의 파이 차트를 생성하고 색상 지정
colors_90 = get_colors(new_df_90['장르'], color_map)
colors_00 = get_colors(new_df_00['장르'], color_map)
colors_10 = get_colors(new_df_10['장르'], color_map)
# color_map[label] = > '발라드' label -> color_map['발라드']
# 해당 연도에 맞는 색깔 리스트를 반환한다

# make_subplots를 사용하여 틀 생성
fig = make_subplots(rows=1, cols=3, specs=[[{'type': 'domain'}, {'type': 'domain'}, {'type': 'domain'}]],
                    subplot_titles=["1990년대", "2000년대", "2010년대"])

# add_trace : 각 파이 차트를 fig 에 추가한다 // labels 는 요소(names) , values는 똑같이 값
fig.add_trace(go.Pie(labels=new_df_90['장르'], values=new_df_90['count'], marker=dict(colors=colors_90), name="1990s"), 1, 1)
# colors 에 리스트값을 삽입해야되기 때문에 한번 정제했다
fig.add_trace(go.Pie(labels=new_df_00['장르'], values=new_df_00['count'], marker=dict(colors=colors_00), name="2000s"), 1, 2)
fig.add_trace(go.Pie(labels=new_df_10['장르'], values=new_df_10['count'], marker=dict(colors=colors_10), name="2010s"), 1, 3)

# 전체 레이아웃 업데이트 - 제목 설정
fig.update_layout(
    title_text="10년 단위의 멜론 TOP50 음악 장르 양상",
)

fig.show()

각 년대 별 멜론 TOP50 음악 장르 양상


이미지 크롤링

그동안의 크롤링은 text 형식의 데이터를 크롤링 하는데 그쳤지만

오늘은 구글 이미지 검색을 통한 image 파일을 크롤링 해보자

# 1. 필요 모듈 임포트
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 selenium.webdriver.common.keys import Keys
import urllib.request

import time
import os

 

기본적인 모듈을 import 해주자

# 2. 폴더 생성 함수 선언
# 특정 폴더를 지정하지 않으면 바탕화면이나 지정되지 않은 폴더에 이미지가 다운로드
def createFolder (directory):
    try:
        if not os.path.exists(directory):
            os.makedirs(directory)
    except OSError:
        print('Error : Creating directory...' + directory)

 

이미지를 담을 폴더를 생성하는 함수 createFolder 이다

try except 를 사용하여 예상치 못한 오류로 폴더가 생성될 수 없는 경우를 예외처리 한다

# 3. 키워드 입력 및 폴더 생성
keyword = '꽃'
createFolder('./'+keyword+'_img_download')
print('1. 키워드 설정 및 폴더 생성 완료...')

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.implicitly_wait(3)

 

이번에는 예시를 위해 keyword 변수에 '꽃'을 입력하여 꽃 이미지를 추출하였지만

어떤 이미지를 크롤링(keyword 값을 변경)하더라도 상관없다.

createFolder를 통해 폴더 생성 후, 기존 키롤링 방식과 동일 하게 service, driver 설정 후 대기시간을 가진다.

# 4. 키워드 검색
print('2. 키워드 검색 ', keyword)
driver.get('https://www.google.co.kr/imghp?hl=ko')

# 검색창 element 찾기  // name 이 중요하다 // 구글 이미지 검색은 textarea name = 'q'
input_keyword = driver.find_element(By.NAME, 'q') # q : 쿼리의 약자
input_keyword.send_keys(keyword)

# 입력 값 전송
input_keyword.send_keys(Keys.RETURN)

 

driver 를 통해 구글 이미지 검색 창으로 이동, 이후  element 를 찾고 keyword값을 입력한다

# 5. 스크롤 내리기
SCROLL_PAUSE_TIME = 2
last_height = driver.execute_script('return document.body.scrollHeight')

print('3. 스크롤 중...')
while True:
    
    driver.execute_script('window.scrollTo(0,document.body.scrollHeight);')
    time.sleep(SCROLL_PAUSE_TIME)
    
    new_height = driver.execute_script('return document.body.scrollHeight')
    
    if new_height == last_height:
        break
    
    last_height = new_height
    time.sleep(SCROLL_PAUSE_TIME)

 

유튜브 댓글 크롤링과 유사하게 모든 이미지가 렌더링 된 이후 크롤링을 하기 위해 스크롤 작업을 한다

# 6. 이미지 검색 개수 확인 및 다운로드
links = []
images = []

# img 태그를 감싼 div 찾기 <div class = 'H8Rx8c'
div = driver.find_elements(By.CLASS_NAME, 'H8Rx8c')

for i in div :
    img_tag = i.find_element(By.CLASS_NAME,'YQ4gaf')
    images.append(img_tag)

print('4. 이미지 개수 확인...')
for image in images:
    if image.get_attribute('src') !=None:
        links.append(image.get_attribute('src'))

print(keyword + '찾은 이미지 개수: ', len(links))
time.sleep(SCROLL_PAUSE_TIME)

print('5. 이미지 다운로드 시작...')

for i, v in enumerate(links):
    # 기본적으로 리스트의 for 문은 값만 주는 방식인데
    # enumerate 로 감싼 리스트를 건네면 리스트의 인덱스와 값 두가지를 넘김
    try:
        url = v 
        start = time.time()
        urllib.request.urlretrieve(url,
                                   './'+keyword+'_img_download/'+keyword+'_'+str(i)+'.jpg')
        #urlreterieve ->  경로를 주면 해당 링크(경로)의 파일을 다운로드하는 패키지함수
        print(str(i+1) + '/' + str(len(links)) + ' ' + keyword + ' 다운로드... Download time : ' + str(time.time()-start)[0:5] + '초' )
    except:
        print(str(i+1) + '/' + str(len(links)) + ' ' + keyword + ' 다운로드 실패')
print(keyword + '-----다운로드 종료-----')
driver.close()

 

이미지 링크와 해당 이미지의 태그를 저장할 빈 리스트 linksimages 를 선언한다.

이후 div 변수 안에 div class를 저장, 해당 이미지태그를 images 안에 추가한다

 

urlreterieve 함수를 사용하여 경로 설정시 파일을 다운로드 한다

try except 문을 사용해 다운로드가 불가한 경우를 예외처리 해주고

다운로드가 끝나면 driver를 닫는다( close() )