공모전 및 프로젝트

[음악 데이터 분석(1)] 멜론 음악 가사 크롤링/ selenium(셀레니움) 크롤링 / 크롬드라이버 다운없이 크롤링

재온 2023. 10. 26. 23:00

0. Intro 

프로젝트 백업용 기록이다. KPOP의 광팬으로서 늘 음악 분석을 해보고 싶어했고, 노래 가사 분석 사이드 프로젝트를 진행하였다. (코드 복습 겸 .. 공부겸.. 개인 사심으로 세븐틴 컴백 기념.. )

처음은 당연히 데이터 수집부터 ! 웹 페이지상의 데이터를 수집하기 위해서는 크롤링을 활용할 수 있다. 그 중, beautifulsoup만으로는 크롤링할 수 없는 경우가 대다수이다!

  • 스크래핑 과정 중 웹 상에서의 동작이 필요할 때: 로그인이 필요한 경우 등등
  • 페이지를 이리저리 돌아다녀도 주소가 변하지 않는 경우: 웹페이지가 iframe형식일때 ex) 네이버 카페

따라서 이번 포스팅에서는 beautifulsoup과 selenium을 통해 멜론 홈페이지 에서곡명, 가사, 기타 정보 수집하는 과정을 다루려고 한다. 

(나는 세븐틴 팬이므로 세븐틴 노래 정보를 수집하였다 ^_^ (요즘 한국어 없는 노래를 내는 그룹이 많아서 슬프다)

*깃헙 코드는 실행 시 가수를 입력받아 해당 가수의 가사를 수집하도록 만들어 놓았다.)

https://github.com/HwangJae-won/Music_Data/tree/main

 

GitHub - HwangJae-won/Music_Data

Contribute to HwangJae-won/Music_Data development by creating an account on GitHub.

github.com

1. 필요한 모듈 설치 

## Melon Music crawling 

import math
import time
import datetime
start = time.time()
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver import Chrome
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
import time 
## for processing
from konlpy.tag import Hannanum
import re
import pandas as pd
import numpy as np
import csv
import sys
## visualization
import matplotlib.pyplot as plt
import seaborn as sns
from collections import Counter
from wordcloud import WordCloud
from PIL import Image

## interactiveshell
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
#ignore warnings
import warnings
warnings.filterwarnings(action="ignore")

 

2. 크롤링 함수 

여기서 뒤집어지는 점... 나때는 말야 ..... (약 2년,, 전) 크롬드라이버를 크롬 버전에 맞게 다운 받고, 경로를 맞게 저장해준 후 크롬드라이버를 불러와주어야 했다. 

    #chrome driver 
    driver = webdriver.Chrome('/Users/chromedriver')
    driver.get(url)

그런데 최신 버전 셀레니움은 크롬드라이버 경로 지정 없이 그냥 바로 호출이 된다 ,,,,,,,!

driver = webdriver.Chrome()
driver.get(url)

크롬이랑 크롬드라이버 버전을 맞추는 귀찮은 과정을 이제는 하지 않아도 되는 것.. 

다들 pip install selenium --upgrade 혹은 pip uninstall selenium -> pip install selenium 을 통해 꼭꼭 업데이트하고 사용하셨으면 좋겠다.

업데이트되면서 사용하는 함수도 조금씩 바뀌었으니 셀레니움 공식 문서를 참고하면 좋을 것 같다. 

https://www.selenium.dev/

 

Selenium

Selenium automates browsers. That's it!

www.selenium.dev

url = "https://www.melon.com/"
print("가수 이름을 입력하세요")
singer = str(input())


#수집할 정보 리스트 
titles =[]
infos =[]
lyricses=[]

def melon_crawling(url, singer):
    
    ##크롬 드라이버 호출
    driver = webdriver.Chrome()
    driver.get(url) 
    
    ##페이지 이동
    #검색창
    driver.find_element(By.XPATH,'//*[@id="top_search"]').click()
    #가수명 입력
    driver.find_element(By.XPATH,'//*[@id="top_search"]').send_keys(singer)
    #검색창 클릭
    driver.find_element(By.XPATH,'//*[@id="gnb"]/fieldset/button[2]').click() 
    #곡 선택
    driver.find_element(By.XPATH,'//*[@id="divCollection"]/ul/li[3]/a').click() 
    #아티스트명에서
    driver.find_element(By.XPATH,'//*[@id="conts"]/div[3]/div[1]/a[2]').click()
    driver.execute_script("window.scrollTo(0, 500)") 
    time.sleep(3) 
    
    # 곡정보 크롤링
    try:
        for page in range(1,6):
            for i in range(1, 51): #50개씩 있음
                #곡정보 칸으로 이동
                sing_css= f'#frm_defaultList > div > table > tbody > tr:nth-child({i}) > td:nth-child(3) > div > div > a.btn.btn_icon_detail'
                driver.find_element(By.CSS_SELECTOR,sing_css).click() 
                driver.execute_script("window.scrollTo(0, 500)") 
                time.sleep(3) 
                ##페이지 파싱 및 정보 수집
                html_source = driver.page_source 
                soup = BeautifulSoup(html_source, 'lxml')
                try:                     
                    title =driver.find_element(By.CSS_SELECTOR, "#downloadfrm > div > div > div.entry > div.info > div.song_name").text 
                    titles.append(title)
                    #세부 정보 
                    info  = driver.find_element(By.CSS_SELECTOR, "#downloadfrm > div > div > div.entry > div.meta").text
                    infos.append(info)
                    
                    #가사
                    lyrics =driver.find_element(By.CSS_SELECTOR, "#d_video_summary").text
                    lyricses.append(lyrics)
                    print(f'{i}번째 곡 크롤링 완료, 곡 제목:', title)
                except:
                    print("가사 정보가 없습니다.")
                    lyricses.append("unknown")
                    print(f'{i}번째 곡 크롤링 완료, 곡 제목:', title, "* 특이사항: 가사 없음")
                driver.back()
            print(f"{page}번째 페이지 크롤링 완료")
            print("====================")
            n= page+1
            print(f'{n}번째 페이지 크롤링 시작 ')
            last_page_height = driver.execute_script("return document.documentElement.scrollHeight") 
            driver.execute_script("window.scrollTo(0, document.documentElement.scrollHeight);") 
            driver.find_element(By.XPATH,  f'//*[@id="pageObjNavgation"]/div/span/a[{page}]').click()
            time.sleep(3)
    except:
        print("더 이상 수집할 수 있는 정보가 없습니다. ")
        pass
    
    driver.close()
    return titles, lyricses, infos


titles, lyricses, infos = melon_crawling(url, singer)

코드 짜면서 처리해야했던 점

- 가사가 없는 경우가 있어 예외처리를 해주어야했음

- 각 페이지는 50개씩 곡이 있었지만 마지막 페이지의 경우 개수가 맞지 않으므로 예외처리 

- 페이지 파싱 과정을 반복문 안에 넣어주지 않아 중복 수집되었다. 해당 부분 주의할 것 

 

3. 전처리하여 최종 결과물 저장

import pickle
print(len(titles), len(lyricses), len(infos))
with open("titles.pkl", 'wb') as f:
    pickle.dump(titles, f)
    
with open("lyricses.pkl", 'wb') as f:
    pickle.dump(lyricses, f)

with open("infos.pkl", 'wb') as f:
    pickle.dump(infos, f)

print("저장 완료")

#전처리
df= pd.DataFrame({"title": titles, 
              "lyrics":lyricses, 
              "information":infos})
album = []
date= []
genre =[]

for i in range(len(df)):
    album.append(df.information[i].split('\n')[1])
    date.append(df.information[i].split('\n')[3])
    genre.append(df.information[i].split('\n')[5])
df['album'] = album
df['date'] = date
df['genre'] = genre

df.drop(["information"], axis =1, inplace=True )

df.to_csv("Melon_Crawling.csv", index=False)

end = time.time()
sec = (end - start)
result = datetime.timedelta(seconds=sec)
result_list = str(datetime.timedelta(seconds=sec)).split(".")
print("총 수행시간 :", result_list[0])

- 각 정보는 pickle 파일로 저장, 데이터 프레임으로 최종 데이터 반환, 수행시간 보여주기 

 

4. py 파일로 정리, 실행 화면 

 

추후 진행할, 해보고싶은 사항 

- 전처리 코드 정리: 일본, 중국곡, 피쳐링 곡 제외, 영어 노래 분리

- 앨범별로 데이터정리, 장르별

- 한국 가사 추출: 시각화 (앨범별)

- NLP 기초 모델: LDA, 감성 분석 , CBOW, LSTM=> 앨범이 주는 컨샙 분석 

- 감성 분석 + 추천시스템

-Lyrics-Based Music Genre Classification Using a Hierarchical Attention Network 논문 구현

https://github.com/alexTsaptsinos/lyricsHAN

 

GitHub - alexTsaptsinos/lyricsHAN: Music Genre Classification by Lyrics using a Hierarchical Attention Network

Music Genre Classification by Lyrics using a Hierarchical Attention Network - GitHub - alexTsaptsinos/lyricsHAN: Music Genre Classification by Lyrics using a Hierarchical Attention Network

github.com

 

NLP 복습하면서 천천히 굴러갑니다~

300x250