2 분 소요

ABC 부트캠프 [웹 데이터 수집 및 크롤링]

image

오늘은 뮤직차트 크롤링 및 시각화를 하려고 한다

멜론 차트 top 100위를 크롤링을 해보자

멜론 차트 100위 크롤링

1) 필요 라이브러리 설치

import requests
from bs4 import BeautifulSoup

2) 멜론 차트 URL

url = "https://www.melon.com/chart/index.htm"

헤더 변경

headers = {"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"}
res = requests.get(url, headers=headers)
res

파서(Parser) 사용

#html = res.text
html = res.text
print(repr(html[:100]))
soup = BeautifulSoup(html, "html.parser")
'<!DOCTYPE html>\r\n<html lang="ko">\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<head>\r\n\t\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\r\n\t'
​

3) 속성명 지정 방법

wrap_tag_list = soup.select("tr[data-song-no]")   # 3. 속성명 지정 방법
len(wrap_tag_list)

4) 노래 리스트 뽑기 / 컬럼 이름 변경, 추가

song_list = []

for wrap_tag in wrap_tag_list:
    song_no = wrap_tag["data-song-no"]

    # wrap_tag.select_one("[href^=playSong]")  # href.startswith("playSong")의 의미
    # wrap_tag.select_one("[href$=playSong]")  # href.endswith("playSong")의 의미
    song_title = wrap_tag.select_one("[href*=playSong]").text  # "playSong" in href 의 의미
    artist_name = wrap_tag.select_one("[href*=goArtistDetail]").text
    album_name = wrap_tag.select_one("[href*=goAlbumDetail]")["title"]
    
    cover_image_url = wrap_tag.select_one("[onerror*=defaultAlbumImg]")["src"]

    song_list.append({
        "곡일련번호": song_no,
        "앨범명": album_name,
        "곡명": song_title,
        "가수명": artist_name,
        "커버이미지_주소": cover_image_url,
    })
    
    # print(song_no, album_name, song_title, artist_name, cover_image_url)

len(song_list)   # 100

5) 데이터 프레임 확인

import pandas as pd
df = pd.DataFrame(song_list).set_index("곡일련번호")
df["순위"] = range(1, df.shape[0]+1)
print(df.shape)
df.head()

6) 좋아요 수 탐색

url = "https://www.melon.com/commonlike/getSongLike.json"   # 좋아요 수 탐색
params = {
    "contsIds": "36599950,36617841",
}

res = requests.get(url, params=params, headers=headers)
res
likes_dict = {}

for song in res.json()['contsLike']:
    print(song["CONTSID"], song["SUMMCNT"])
    likes_dict[song["CONTSID"]] = song["SUMMCNT"]

likes_dict

dict comprehension

# dict comprehension
{
    song["CONTSID"]: song["SUMMCNT"]
    for song in res.json()['contsLike']
}

list comprehension

numbers = [1,2,3,4,5]

[number ** 2 for number in numbers]  # 리스트(list comprehension)
[number ** 2 
 for number in numbers 
 if number % 2 == 0]  # list comprehension -> [4, 16]

set comprehension

# { number ** 2 for number in numbers }   # 집합
{ number % 3 for number in numbers } # 집합(set comprehension) -> {0, 1, 2}
{ number: number % 3 for number in numbers }  # 사전(dict comprehension) 
{1: 1, 2: 2, 3: 0, 4: 1, 5: 2}
(number ** 2 for number in numbers)   # 튜플(tuple comprehension)은 없다!!

7) 좋아요 요청하기

conts_ids = ",".join(df.index)

url = "https://www.melon.com/commonlike/getSongLike.json"   # 좋아요 수 탐색
params = {
    "contsIds": conts_ids,
}

res = requests.get(url, params=params, headers=headers)
res

# dict comprehension
likes_dict = {
    str(song["CONTSID"]): int(song["SUMMCNT"])
    for song in res.json()['contsLike']
}
len(likes_dict)
df["좋아요수"] = likes_dict
print(df.shape)
df.head()

image

노래 시리즈 종류

song_count_series = df.groupby("가수명").size().sort_values(ascending=False)
song_count_series

image

시스템 기본 폰트 설정(한글)


import matplotlib.pyplot as plt
import platform

if platform.system() == "Darwin":
    plt.rc("font", family="AppleGothic")  # macOS 시스템 기본 폰트

elif platform.system() == "Windows":
    plt.rc("font", family="Malgun Gothic")  # Windows 시스템 기본 폰트

8) 노래 리스트 그래프로 시각화

song_count_series.plot(kind="bar", figsize=(20, 5))

image

9) 곡의 수가 1인 곡들은 삭제

mask = song_count_series > 1  # boolean mask
chart_series = song_count_series[mask]
chart_series
mask = song_count_series == 1
chart_series["Others"] = song_count_series[mask].sum()   # sum() / count() 로 합계 
chart_series

image

10) pie 그래프로 시각화

pie 그래프로 시각화

chart_series.plot(kind="pie")

image

댓글남기기