본문 바로가기

Data

[python] 멜론차트 크롤링 ( Selenium, BeautifulSoup )

오늘은 저번 포스팅에 이어 크롤링을 해볼것이다!

이번에는 BeautifulSoup뿐만 아니라 Selenium을 같이 활용할 것이다

 

 

Selenium을 사용해야 하는 이유?

셀레니움은 웹을 자동으로 동작시키는 역할을 한다. 즉, 우리가 마우스로 하는 일들(검색창 누르기, 필터 설정하기, 스크롤 내리기 등등)을 코딩을 통해 자동으로 해준다는 뜻이다.

 

그렇다면 마우스로 직접 설정하면 되는 것을 왜 굳이 셀레니움을 이용해서 자동으로 해야할까?

우리 눈에 보이는 url이 변하지 않기 때문이다!! 

 

저번 BeautifulSoup만을 사용해 크롤링해왔던 네이버 뉴스의 url을 살펴보겠다.

search.naver.com/search.naver where=news&query=%EC%95%84%EB%8F%99%ED%95%99%EB%8C%80&sm=tab_opt&sort=0&photo=0&field=0&reporter_article=&pd=3&ds=2021.02.01&de=2021.02.02&docid=&nso=so%3Ar%2Cp%3Afrom20210201to20210202%2Ca%3Aall&mynews=0&refresh_start=0&related=0

 

기간 필터를 2021.02.01~2021.02.02로 설정한 첫페이지의 url이다. url에 정보가 자세히 담긴 것을 확인할 수 있다.

즉, 기간 필터를 변경하고 싶으면 날짜가 들어간 자리만 바꿔주면 된다. 페이지를 넘기고 싶다면 start 자리에 10을 더해주면 된다.

 

네이버 뉴스 페이지 url의 규칙성은 이전 포스팅을 참조하면 된다!

 

[python] 네이버 뉴스 기사 작성일, 제목, url 크롤링 ( BeautifulSoup )

오늘은 크롤링 시리즈 첫번째로 BeautifulSoup을 사용해서 네이버 뉴스기사의 작성일, 제목, 주소를 크롤링해 데이터프레임으로 만드는 것까지 해보자! 1. 모듈 불러오기 import requests from bs4 import Beau

arehoow.tistory.com

하지만!! 멜론 페이지는 다르다!!

 

멜론 차트 url은 "www.melon.com/chart/index.htm"이다.

접속한 후 오른쪽 상단에 있는 차트파인더를 통해 필터를 설정할 수 있다.

 

차트파인더를 통해 필터를 설정하고 검색을 누른 후 화면이다.

하지만 여전히 url은 "www.melon.com/chart/search/index.htm" 이다..

즉 url에 필터에 대한 정보가 전혀 담겨있지 않다!!!

이말은 즉슨 url을 바꿈으로써 내가 원하는 페이지로 이동할 수 없다는 뜻! 마우스로 직접 누르는 수 밖에 없다..

 

정리하자면, 필터가 달라진다 해도 url은 전혀 바뀌지 않으므로(url의 규칙성 파악 불가) request 모듈만을 이용해서 원하는 페이지의 원하는 설정을 할 수 없음!

따라서, 마우스의 역할이 필요하고 이를 수행해주는 것이 바로 Selenium!!

 

서두가 길었는데, 멜론 차트를 크롤링하기 위해 Selenium을 통해 웹을 동작시킨 후, 그 이후 전과 같이 BeautifulSoup을 통해 html 정보를 parsing하고 원하는 데이터를 가져올 것이다!

 

1. 모듈 불러오기

import selenium
from selenium import webdriver as wd
import time
import pandas as pd
from bs4 import BeautifulSoup
import requests
from itertools import repeat

2. 크롬드라이버가 url 접속하도록 하기

# 크롬드라이버 열기
driver = wd.Chrome('C:\chromedriver') # 크롬드라이버 경로
driver.maximize_window() # 크롬창 크기 최대

# 드라이버가 해당 url 접속
url = 'https://www.melon.com/chart/index.htm' # 멜론차트 페이지
driver.get(url)

이때, 크롬창을 최대로 키우는 이유는 크롬드라이버 창이 작은 경우 화면에 정보가 다 안담겨 오류나는 경우가 종종 있기 때문!

3. 크롬드라이버가 필터를 설정하도록 하자

# 차트파인더 클릭
driver.find_element_by_xpath('//*[@id="gnb_menu"]/ul[1]/li[1]/div/div/button/span').click()

# 연대선택, 연도선택, 월선택, 장르선택

# 월간차트 클릭
driver.find_element_by_xpath('//*[@id="d_chart_search"]/div/h4[2]/a').click()
time.sleep(2)

# 연대선택 2000년 클릭
driver.find_element_by_xpath('//*[@id="d_chart_search"]/div/div/div[1]/div[1]/ul/li[3]/span/label').click()
time.sleep(2)

# 연도선택 2008년 클릭
driver.find_element_by_xpath('//*[@id="d_chart_search"]/div/div/div[2]/div[1]/ul/li[2]/span/label').click()
time.sleep(2)

# 월선택 8월 클릭
driver.find_element_by_xpath('//*[@id="d_chart_search"]/div/div/div[3]/div[1]/ul/li[8]/span/label').click()
time.sleep(2)

# 장르선택 종합 클릭
driver.find_element_by_xpath('//*[@id="d_chart_search"]/div/div/div[5]/div[1]/ul/li[1]/span/label').click()
time.sleep(2)

# 검색버튼 클릭
driver.find_element_by_xpath('//*[@id="d_srch_form"]/div[2]/button/span/span').click()

우리가 마우스로 차트파인더를 클릭하고 연도, 월 등을 클릭하는 행동을 웹드라이버가 자동으로 수행하도록 한것!

이때, time.sleep(2)는 2초간 쉬어가라는 의미인데, 웹드라이버는 일반 크롬창보다 조금 느리다. 따라서 창이 다 펼쳐지기도 전에 무언가를 클릭하라는 명령을 내리면 오류가 나므로 단계별로 time.sleep을 꼭 해주자

 

이제 우리가 원하는 페이지가 완성되었다! 이제 BeautifulSoup을 통해 데이터를 가져오자!

 

4. html 정보 가져오기

html = driver.page_source # 드라이버 현재 페이지의 html 정보 가져오기 
                            # cf) requests.get(url)
soup = BeautifulSoup(html, 'lxml')

5. 곡명 가져오기

soup.find_all('div', attrs={'class': 'ellipsis rank01'})

<div class="ellipsis rank01">는 부모태그
soup.find_all('tag', attrs = {'속성':'속성명'}) 형식을 만족할 수 있는 부모태그를 먼저 찾을 것!

 

부모 태그 안의 'a'태그의 value에 원하는 데이터 들어가 있음을 확인

 

[title.find('a').get_text() for title in soup.find_all('div', attrs={'class': 'ellipsis rank01'})]

부모태그속에서 태그명 'a' 찾기 -> find 함수 다시 사용

value에 원하는 데이터 -> get.text() 함수 

이런식으로 100개의 곡명이 리스트에 담김

 

6. 가수명 가져오기

soup.find_all('span', attrs={'class':'checkEllipsis'})

가수명은 value에 있음 -> get.text()

[ singer.get_text() for singer in soup.find_all('span', attrs={'class':'checkEllipsis'}) ]

7. 순위 가져오기

soup.find_all('span', attrs={'class':'rank top'})

여기서 문제 발생!! 1,2,3위와 4~100위 순위가 html 정보가 다름

따라서 새롭게 순위 만들기로 함

song = [title.find('a').get_text() for title in soup.find_all('div', attrs={'class': 'ellipsis rank01'})]
rank = []
for i in range(len(song)):
    rank.append(i+1)

순위는 연속적인 숫자이므로 반복문 통해 간단히 만들었다

 

8. 연도와 월 가져오기

soup.find_all('span', attrs={'class':'datelk'})

soup.find_all('span', attrs={'class':'datelk'})[0].get_text() # 년
soup.find_all('span', attrs={'class':'datelk'})[1].get_text() # 월

 

자 지금까지, 2008년 8월 월간차트를 가져왔다. 이제는 2020년 8월 차트와 2008년 8월 차트를 한꺼번에 크롤링하고 비교해보자!!

 

9. 2020년 8월 VS 2008년 8월 멜론차트

저번 BeutifulSoup만을 이용해 네이버 뉴스 페이지 전체를 크롤링 하기 위해선 "url 규칙성 파악"이 중요했다

이번에는 "xpath 규칙성 파악"이 관건

 

selenium을 통해 월간차트 클릭 -> 연대 선택 -> 연도 선택 -> 월 선택 -> 장르 선택을 했다

2020년 8월과 2008년 8월의 차이는 연대와 연도이므로 이 두 부분의 규칙성을 파악해보자

 

연대 xpath 규칙성

2000년 //*[@id="d_chart_search"]/div/div/div[1]/div[1]/ul/li[3]/span/label
2010년 //*[@id="d_chart_search"]/div/div/div[1]/div[1]/ul/li[2]/span/label

2020년 //*[@id="d_chart_search"]/div/div/div[1]/div[1]/ul/li[1]/span/label

연도 xpath 규칙성

2009년 //*[@id="d_chart_search"]/div/div/div[2]/div[1]/ul/li[1]/span/label
2008년 //*[@id="d_chart_search"]/div/div/div[2]/div[1]/ul/li[2]/span/label
2020년 //*[@id="d_chart_search"]/div/div/div[2]/div[1]/ul/li[2]/span/label

2008년과 2020년 모두 위에서 2번째라 xpath 같음!

추가) 월 xpath 규칙성

1월 //*[@id="d_chart_search"]/div/div/div[3]/div[1]/ul/li[1]/span/label
12월 //*[@id="d_chart_search"]/div/div/div[3]/div[1]/ul/li[12]/span/label

 

period = 1
month = 8
result_df = pd.DataFrame()

while period < 4:
    try:
        # 크롬드라이버 열기
        driver = wd.Chrome('C:\chromedriver') # 크롬드라이버 경로
        driver.maximize_window() # 크롬창 크기 최대

        # 드라이버가 해당 url 접속
        url = 'https://www.melon.com/chart/index.htm' # 멜론차트 페이지
        driver.get(url)
        time.sleep(2)

        # 차트파인더 클릭
        driver.find_element_by_xpath('//*[@id="gnb_menu"]/ul[1]/li[1]/div/div/button/span').click()
        time.sleep(2)

        # 월간차트 클릭
        driver.find_element_by_xpath('//*[@id="d_chart_search"]/div/h4[2]/a').click()
        time.sleep(2)

        driver.find_element_by_xpath('//*[@id="d_chart_search"]/div/div/div[1]/div[1]/ul/li[{}]/span/label'.format(period)).click()
        time.sleep(2)

        # 연도선택(규칙 찾기!)
        driver.find_element_by_xpath('//*[@id="d_chart_search"]/div/div/div[2]/div[1]/ul/li[2]/span/label').click()
        time.sleep(2)

        # 월선택 8월 클릭
        driver.find_element_by_xpath('//*[@id="d_chart_search"]/div/div/div[3]/div[1]/ul/li[{}]/span/label'.format(month)).click()
        time.sleep(2)

        # 장르선택 종합 클릭
        driver.find_element_by_xpath('//*[@id="d_chart_search"]/div/div/div[5]/div[1]/ul/li[1]/span/label').click()
        time.sleep(2)
        
        # 검색버튼 클릭
        driver.find_element_by_xpath('//*[@id="d_srch_form"]/div[2]/button/span/span').click()
        time.sleep(2)
        
        # html 정보 가져오기
        html = driver.page_source 
        soup = BeautifulSoup(html, 'lxml')
        
        # 노래 제목 가져오기
        song_list = [title.find('a').get_text() for title in soup.find_all('div', attrs={'class': 'ellipsis rank01'})]
        
        # 가수명 가져오기
        singer_list = [ singer.get_text() for singer in soup.find_all('span', attrs={'class':'checkEllipsis'}) ]
        
        # 순위 만들기
        rank_list = []
        for i in range(len(song)):
            rank_list.append(i+1)
        # 년
        year_list = list(repeat(soup.find_all('span', attrs={'class':'datelk'})[0].get_text(), len(song_list)))
        
        # 월 
        # month = list(repeat(soup.find_all('span', attrs={'class':'datelk'})[1].get_text(), len(song))) # 08로 표기되어 안깔끔
        month_list = list(repeat(month, len(song_list)))
        
        # 데이터프레임 생성
        df = pd.DataFrame({'연도':year_list,'월':month_list,'순위':rank_list,'곡명':song_list,'가수명':singer_list})
        result_df = pd.concat([result_df, df], ignore_index=True)
        period += 2
    
    except:
        print(period)
        break

 

10. 엑셀 파일로 저장

result_df.to_csv('멜론차트크롤링.csv', encoding='ANSI')

'utf-8'이 글자가 깨질때는 만능 인코딩인 'ANSI'를 활용하자

 

멜론차트크롤링.csv
0.01MB

 


이렇게 차트 완성!!

2008년 8월 노래는 거의 빠짐없이 다 아는데, 2020 8월 노래는 뒤로 가면 잘 모르겠다..나 늙은건가...?

다음에는 더 수준을 높여서 이미지 크롤링을 포스팅 해야겠다