본문 바로가기
공대생/무작정 해보는 파이썬

Python으로 Mp3 파일 속성 편집하기 - 자동화

by 흔한 공대생 2020. 4. 10.
728x90
반응형

본 포스팅은 Python을 이용하여 Mp3 파일의 속성을 편집하는 내용을 다룬다. 파이썬 3.7, 윈도우 10 기반에서 이루어졌다.


 

01. 들어가며

이전 포스팅에서는 파이썬의 tkinter와 eyed3 모듈을 이용하여 Mp3 파일의 속성을 편집하는 프로그램을 만들었다.

하지만 우리가 만든 프로그램은 타이틀과 아티스트, 앨범 정보를 직접 타이핑하여 입력해주어야 했다.

음원에 대한 정보를 자동으로 입력되도록 만들 방법을 고민하다가, 음원 사이트를 크롤링해보자는 생각이 들었다.

이번 포스팅에서는 지난 포스팅의 결과를 바탕으로, 음원 정보를 크롤링하여 자동으로 넣어주고, 앨범 커버 이미지를 다운로드하는 프로그램을 만들 것이다.

(사실 커버를 씌우고 가사까지 넣어보려 했지만, 어째서인지 실패했다...)

 

기본적인 뼈대에 대해서는 지난 포스팅 참고

https://commonengineerr.tistory.com/15

 

Python으로 Mp3 파일 속성 편집하기

본 포스팅은 Python을 이용하여 Mp3 파일의 속성을 편집하는 내용을 다룬다. 파이썬 3.7, 윈도우 10 기반에서 이루어졌다. 01. 들어가며, Mp3 파일을 재생시키다 보면, 파일 이름이 깔끔하지 않다거나 앨범, 아티..

commonengineerr.tistory.com


 

02. 뼈대

먼저 기본적인 틀은 다음과 같다.

 

정보를 편집할 파일의 이름과 크롤링해올 url을 입력 받는다.

정보 가져오기 버튼으로 크롤링을 실행, 아래에 있는 칸들에 정보를 채워준다.

다운로드 버튼으로 이미지 url을 읽어 이미지를 다운로드한다.

정보 저장버튼으로 파일 속성을 업데이트하고, 리셋 버튼으로는 엔트리를 비워준다.


 

03. 크롤링 정보 파악

가장 중요한 기능이 '정보 가져오기' 다. 음원 사이트를 크롤링하여 앨범, 아티스트, 제목의 정보와 다운로드할 이미지의 url을 가져와야한다.

사용한 사이트는 네이버 뮤직(https://music.naver.com/)이다. (어째서인지는 모르겠지만 네이버에서 크롤링해오는 것이 마음이 편한 느낌이다..)

이번 프로그램의 목적 자체가 타이핑을 최소로 하는 것이었다. 따라서 한 페이지 안에 정확히 원하는 정보만 있어야할 필요를 느꼈다. 예를 들면, 다음과 같이 앨범을 표시하는 페이지에서는 내가 편집하려는 음원의 제목이 무엇인지 직접 정해주어야 한다. 정말 많은 곡들이 함께 수록되어 있기 때문이다.

내가 좋아하는 임창정 형님의 10집 앨범 Bye다. 정보는 충분히 많지만, 정말 많은 곡이 포함되어 있음을 확인할 수 있다. 이런 화면에서 내가 원하는 곡만 뽑아내려면 추가 정보가 필요하다.

 

하지만 여기, 가사를 클릭해주면 얘기는 달라진다.

 

정확히 원하는 곡의 제목과 앨범, 아티스트는 물론 앨범 커버와 함께 (나중에 쓰일지도 모를) 가사 정보까지 있다.

필요한 거 다 있는 만능 창

 

따라서 우리는, 네이버 뮤직에서 원하는 곡의 가사 링크의 url을 크롤링하도록 하자.


 

04. 크롤링

크롬으로 창을 열어 f12를 눌러 소스를 확인해보면 어느 부분을 긁어와야할 지 알 수 있다.

혹시 모르겠다면, 이전 포스팅 참고

https://commonengineerr.tistory.com/3

 

Python Beautifulsoup 네이버 뉴스 크롤링 (1)

본 포스팅에서는 python Beautifulsoup 모듈을 이용하여 작일 네이버 랭킹 뉴스의 제목을 크롤링하는 법을 다룬다. 저작권을 침해하지 않는 범위에서 이용해야함을 밝힌다. 01. 들어가며 이번 주제는 입대한 친구..

commonengineerr.tistory.com

 

소스코드는 다음과 같다.

import requests
from bs4 import BeautifulSoup

def getInfo():
    try:
        url = e2.get()

# http://www.useragentstring.com/  서버에서 봇으로 인지하고 차단하는 경우에 대비
        header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36'}
        r = requests.get(url, headers = header)
        html = r.content
        soup = BeautifulSoup(html, 'html.parser')
    
        info_t = soup.select('.dsc > h2 > span > a')[0]
        info_a = soup.select('.dsc > span')
        img = soup.select('.section_info > div > a > img')[0]

        albumcover = img['src']
        album = info_a[2].text.strip()
        artist = info_a[0].text.strip()
        title = info_t.text.strip()
    
        e3.delete(0, len(e3.get()))
        e4.delete(0, len(e4.get()))
        e5.delete(0, len(e5.get()))
        e6.delete(0, len(e6.get()))

        e3.insert(0, albumcover)
        e4.insert(0, album)
        e5.insert(0, artist)
        e6.insert(0, title)

    except Exception as e:
        e7.delete(0, len(e7.get()))
        e7.insert(0, 'error '+str(e))

 

에러를 잡기 위해 try except 구문을 이용했다.

이전과 달라진 점은 헤더를 추가했다는 것이다(9번 줄). 이유는 다음과 같다. 크롤링을 하다보니 가끔씩 정보를 가져오지 못하고 빈 리스트만 제공되는 경우를 마주할 수 있었다. 열심히 구글링한 결과, 사이트가 크롤링 시도를 봇의 공격으로 인지할 수 있어 그렇다고 한다. 이를 방지하기 위해 헤더를 지정하여 인간인 내가 시킨 일이라고 알려줘야한다.

다음 사이트에서 User Agent String을 복사하여 헤더로 지정해주면 된다.

http://www.useragentstring.com/

 

UserAgentString.com - unknown version

 

www.useragentstring.com


 

05. 앨범 커버 이미지 다운로드

앨범 커버 다운로드는 url만 있으면 된다.

파이썬3에 내장된 urllib.request 모듈의 urlretrieve()를 이용하면 저장할 파일 명과 url만 입력하는 것으로 이미지를 다운로드할 수 있다.

 

위에서 크롤링해온 이미지 url에 들어가보면 썸네일용 작은 이미지가 뜬다.

이때 링크에서 .jpg 뒤의 ?부터 맨 끝까지를 지워주면, 고화질의 원본 이미지가 나온다.

 

용량 차이도 별로 없는데 썸네일 이미지보다는 원본 이미지가 좋지 않을까.

 

위를 참고하면 크롤링해온 url을 ?를 기준으로 나누어 앞부분만 사용하면 된다. 그리고 urlretrieve()를 이용하여 이미지를 다운로드해준다. 이미지 파일 명은 앨범 이름으로 하겠다. (앨범 커버 이미지이기 때문)

소스코드는 다음과 같다.

import urllib.request

def download():
    try:
        name = e4.get()
        albumcover = e3.get()
        coverUrl = albumcover.split('?')
        albumcover = coverUrl[0]

        urllib.request.urlretrieve(albumcover, (os.getcwd())+"/"+name+".jpg")
        e7.delete(0, len(e7.get()))
        e7.insert(0, 'download complete')
        
    except Exception as e:
        e7.delete(0, len(e7.get()))
        e7.insert(0, 'error '+str(e))

 

06. 마무리

정보 저장이나 리셋 등 나머지 부분은 이전 포스팅에서와 동일하다.

전체 소스코드는 다음과 같다.

import os
import eyed3
import requests
import urllib.request
from tkinter import *
from bs4 import BeautifulSoup


def getInfo():
    try:
        url = e2.get()

# http://www.useragentstring.com/  서버에서 봇으로 인지하고 차단하는 경우에 대비
        header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36'}
        r = requests.get(url, headers = header)
        html = r.content
        soup = BeautifulSoup(html, 'html.parser')
    
        info_t = soup.select('.dsc > h2 > span > a')[0]
        info_a = soup.select('.dsc > span')
        img = soup.select('.section_info > div > a > img')[0]

        albumcover = img['src']
        album = info_a[2].text.strip()
        artist = info_a[0].text.strip()
        title = info_t.text.strip()
    
        e3.delete(0, len(e3.get()))
        e4.delete(0, len(e4.get()))
        e5.delete(0, len(e5.get()))
        e6.delete(0, len(e6.get()))

        e3.insert(0, albumcover)
        e4.insert(0, album)
        e5.insert(0, artist)
        e6.insert(0, title)

    except Exception as e:
        e7.delete(0, len(e7.get()))
        e7.insert(0, 'error '+str(e))        

def download():
    try:
        name = e4.get()
        albumcover = e3.get()
        coverUrl = albumcover.split('?')
        albumcover = coverUrl[0]

        urllib.request.urlretrieve(albumcover, (os.getcwd())+"/"+name+".jpg")
        e7.delete(0, len(e7.get()))
        e7.insert(0, 'download complete')
        
    except Exception as e:
        e7.delete(0, len(e7.get()))
        e7.insert(0, 'error '+str(e))

    
def reset():
    e1.delete(0, len(e1.get()))
    e2.delete(0, len(e2.get()))
    e3.delete(0, len(e3.get()))
    e4.delete(0, len(e4.get()))
    e5.delete(0, len(e5.get()))
    e6.delete(0, len(e6.get()))
    e7.delete(0, len(e7.get()))
    
def save():
    try:
        name = e1.get()
        album = e4.get()
        artist = e5.get()
        title = e6.get()
        
        tag = eyed3.load((os.getcwd())+"/"+name+".mp3")
        tag.tag.album = album
        tag.tag.artist = artist
        tag.tag.title = title

        tag.rename(title+' - '+artist)
        tag.tag.save()

        e7.delete(0, len(e7.get()))
        e7.insert(0, 'save complete')

    except Exception as e:
        e7.delete(0, len(e7.get()))
        e7.insert(0, 'error '+str(e))


window = Tk()
window.geometry('450x300')
window.resizable(width=False, height=False)


l0 = Label(window, text = 'mp3 속성 편집 및 앨범커버 다운로드', fg = 'orange', font = 'helvetica 16 bold')
l1 = Label(window, text = '파일 이름: ')
l2 = Label(window, text = '뮤직 url: ')
l3 = Label(window, text = '이미지 url: ')
l4 = Label(window, text = '앨범: ')
l5 = Label(window, text = '아티스트: ')
l6 = Label(window, text = '제목: ')

l0.place(x=5, y=10)
l1.place(x=10, y=50)
l2.place(x=10, y=80)
l3.place(x=10, y=150)
l4.place(x=10, y=180)
l5.place(x=10, y=210)
l6.place(x=10, y=240)


e1 = Entry(window)
e2 = Entry(window)
e3 = Entry(window)
e4 = Entry(window)
e5 = Entry(window)
e6 = Entry(window)
e7 = Entry(window)

e1.place(x=75, y=50)
e2.place(x=75, y=80)
e3.place(x=75, y=150)
e4.place(x=75, y=180)
e5.place(x=75, y=210)
e6.place(x=75, y=240)
e7.place(x=220, y=275)


b1 = Button(window, text = "정보 가져오기", command=getInfo)
b2 = Button(window, text = "앨범 커버 다운로드", command=download)
b3 = Button(window, text = "정보 저장", command=save)
b4 = Button(window, text = "리셋", command=reset)

b1.place(x=15, y=110)
b2.place(x=105, y=110)
b3.place(x=130, y=270)
b4.place(x=400, y=270)

window.mainloop()

 

실제로 이용해보면 상당히 잘 작동한다. 임창정의 소주 한잔 파일을 편집해보았다.

 

여전히 가사는 등록하지 못하고, 앨범 커버를 다운로드할 뿐 씌우지 못하는 단점이 있다. 이 부분에 대해서는 차차 공부하며 개선시켜보도록 하겠다.

728x90
반응형

댓글