ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 파이썬 공부 #1 - 크롤링으로 멜론 실시간 Top100 긁어오기
    Programming/Python 2020. 1. 22. 20:16

    시작하기에 앞서...

    천리길도 한걸음부터,,, 크롤링은 멜론차트에서 부터 시작하는것이 국룰.

     

     

     

    한번 긁어와봅시다

    일단 준비물부터 챙겨야지

    크롤링을 하기에 앞서 크롤링을 위한 도구들을 준비해 봅시다.

    저의 경우 requests와 Beautifulsoup을 사용했습니다.

    parser의 경우 파이썬 내장인 html.parser보다 성능이 좋은 lxml을 사용했습니다.

    1
    2
    3
    4
    5
    headers = {'User-Agent''Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57'}
     
    url = "https://www.melon.com/chart/index.htm"
    res = requests.get(url, headers=headers).text
    soup = BeautifulSoup(res, "lxml")
    cs

    멜론의 경우 User-Agent를 바꿔줘야 크롤링이 가능합니다. 귀찮게시리
    User-Agent는 구글에서 검색해서 아무거나 가져다 쓰면 됩니다.

     

     

    멜론 실시간 차트를 한번 봅시다.

    1월 22일 오후 6시 기준의 멜론 실시간차트

    차트에서 필요한 정보는 곡 제목, 부른 가수 정도? 부가적으로 앨범명까지 같이 긁어보도록 합시다.

     

     

    HTML 구조 파악하기

    우리가 정보들을 긁어오기 위해선 각 요소들이 어떤 태그로 이루어져있고 클래스명은 어떤지 등등을 알아야하겠죠. 

    지코의 "아무노래" 를 예시로 한번 알아보겠습니다.

    각 요소의 구조. 개발자 도구를 사용합시다.

    제목의 경우 div.ellipsis.rank01 -> span -> a

    가수의 경우 div.ellipsis.rank02 -> a

    앨범명의 경우 div.ellipsis.rank03 -> a 의 구조를 가집니다.

     

    자, 이를 바탕으로 Beautifulsoup을 이용해 뽑아내봅시다.

    1
    2
    3
    song_name = soup.select("#lst50 > td > div > div > div.ellipsis.rank01 > span > a")
    artist_name = soup.select("#frm > div > table > tbody > tr > td > div > div > div.ellipsis.rank02 > a")
    album_name = soup.select("#lst50 > td > div > div > div.ellipsis.rank03 > a")
    cs

     

     

     

    그런데, 이 코드를 실행하려 하는데 한가지 문제점을 발견합니다.

     

    "아마두" 의 경우입니다.

    그냥 가수명을 "다모임"을 해주면 좀 좋을까;; 무려 div태그 안에 5개의 a태그를 갖습니다.

    딩고놈들

     

     

    soup.select() 는 결과를 리스트로 저장하기 때문에 한개씩 출력한다면 아티스트 이름과 곡제목이 밀릴 가능성이 있죠.

    "흔들리는 꽃들 속에서 네 샴푸향이 느껴진거야" 를 딥플로우가 부른게 될수도 있다는 말입니다. 근데 상구형도 샴푸를 쓸까요? 갑자기 궁금하네.

     

     

    그래서 artist_name을 일단 div태그까지만 범위를 좁혀서 긁어오도록 합시다. 그 다음 그 결과물에서 각각 a태그를 뽑아내면 되겠죠?

    말로는 설명이 어렵네요. 다음과 같이 코드를 작성해봤습니다.

    1
    2
    3
    4
    5
    6
    artist_name = soup.select("#frm > div > table > tbody > tr > td > div > div > div.ellipsis.rank02")
     
    for i in range(len(artist_name)):
        artist_name[i] = artist_name[i].select("div > a")
        for j in range(len(artist_name[i])):
            artist_name[i][j] = artist_name[i][j].text
    cs

     

    제목, 앨범명을 깔끔하게 텍스트만 뽑아내 봅시다.

    1
    2
    3
    4
    5
    for i in range(len(song_name)):
        song_name[i] = song_name[i].text
        
    for i in range(len(album_name)):
        album_name[i] = album_name[i].text
    cs

     

    모든 준비는 끝났다,,,,,?

    모든 요소들을 다 긁어서 텍스트만 추출했으니 이제 출력만 해보면 되겠죠? 한번 출력해 봅시다.

    1
    2
    for i in range(len(album_name)):
        print(f'{song_name[i]} / {artist_name[i]} / {album_name[i]}')
    cs
    성공?

    결과는 잘 나옵니다.

    근데 가수명이 리스트로 출력되는 문제가 있습니다. 아마도 위의 코드 때문에 가수명을 저장하는 리스트가 이차원 리스트가 되서 그런것 같습니다.

     

    아래의 코드를 통해서 가수명을 리스트에서 단순 문자열로 바꿔주도록 합시다.

    1
    2
    for i in range(len(artist_name)):
        artist_name[i] = ', '.join(artist_name[i])
    cs

     

    join() 함수는 리스트의 모든 요소를 모두 합쳐 한개의 문자열로 만듭니다. 저의 경우는 콤마(,) 로 각 요소를 구분하였습니다.

     

    성공!

    문제없이 잘 나오네요. 각 리스트에 순수 문자열 형태로 제목/가수명/앨범명이 들어갔으니 적당히 이쁘게 출력해서 사용하면 끝입니다.

     

     

     

    마치면서 한마디

    멜론의 경우 크롤링을 방지하기 위해 태그의 구조를 주기적으로 바꿔준다고 합니다. 따라서 제가 작성한 코드는 지금 당장은 잘 동작해도 어느 순간에 갑자기 안될 가능성이 농후합니다. 뭐 그래도 큰 문제는 아닙니다. 단지 바뀐 태그 구조에 따라 select의 인자를 바꿔주기만 하면 됩니다.

     

     

     

     

     

     

    개발환경

    Python3@VS Code

    Windows 10 1903 Build 18362.592 19H1

Designed by Tistory.