1. 程式人生 > 程式設計 >python實現可下載音樂的音樂播放器

python實現可下載音樂的音樂播放器

本文例項為大家分享了tkinter+pygame+spider實現音樂播放器,供大家參考,具體內容如下

1.確定頁面

SongSheet------ 顯示歌單
MusicCtrl ------顯示音樂一些控制元件(播放,跳轉,音量調節)
SearchWindows ------搜尋欄(搜尋歌曲預設顯示20條,可下載)

songSheet.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Author: Minions
# @Date: 2019-11-24 19:51:16
# @Last Modified by: Minions
# @Last Modified time: 2019-12-17 10:01:53

import tkinter
import os
from tkinter import ttk
import time

class SongSheet(tkinter.Frame):
 def __init__(self,master):
  self.frame = tkinter.Frame(master,height=230,width=300,bd=1,bg="SkyBlue")
  self.frame.place(x=0,y=0)
  self.filePath = "C:\Musics"
  self.music = ""  # 點選歌曲獲得更新的路徑
  self.count = 0 # 計數,共多少歌曲

 def run(self):
  # 搜尋按鈕
  searchBtn = tkinter.Button(self.frame,text="更新",bg="SkyBlue",command=self.showSheet,width=10,height=1)

  searchBtn.place(x=0,y=200)

 # 顯示歌單
 def showSheet(self):
  self.count = 0
  musics = os.listdir(self.filePath)
  tree = ttk.Treeview(self.frame)
  # 定義列
  tree["columns"] = ("song")
  # 設定列,列還不顯示
  tree.column("song",width=95)

  # 設定表頭 和上面一一對應
  tree.heading("song",text="song")

  # 新增資料  往第0行新增
  for music in musics:
   # 去除空格
   music = "".join(music.split(" "))
   tree.insert("",text=self.count,values=(music))
   self.count += 1

  # 滑鼠選中一行回撥
  def selectTree(event):
   for item in tree.selection():
    item_text = tree.item(item,"values")
    self.music = "".join(item_text)
    # print(self.music)

  # 選中行
  tree.bind('<<TreeviewSelect>>',selectTree)
  tree.place(width=300,height=200,x=0,y=0)

  # 新增滾動條
  sy = tkinter.Scrollbar(tree)
  sy.pack(side=tkinter.RIGHT,fill=tkinter.Y)
  sy.config(command=tree.yview)
  tree.config(yscrollcommand=sy.set)

python實現可下載音樂的音樂播放器

2.寫出音樂控制元件

musicCtrl.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Author: Minions
# @Date: 2019-11-24 16:28:18
# @Last Modified by: Minions
# @Last Modified time: 2019-12-17 10:25:31

import tkinter
from tkinter import ttk
import os
import time
import pygame
from mutagen.mp3 import MP3
import random
from songSheet import SongSheet

class MusicCtrl(object):
 def __init__(self,height=150,width=700,bg="MediumSeaGreen")
  self.frame.place(height=150,y=250)
  self.nowPaly = True  # 是否正在播放音樂
  self.filePath = r"C:\Musics" # 從該資料夾讀取
  self.musicPath = ""  # 用於拼接音樂的路徑
  self.songSheet = SongSheet(master)
  self.songSheet.run()
  self.music = os.path.join(self.filePath,self.musicPath) # 音樂的路徑


 # 整合功能
 def run(self):
  self.playMusic()
  self.refreshName()
  self.pauseMusic()
  self.volume()
  try:
   self.songPos()
  except:
   print("暫無歌曲載入!")

 # 播放音樂按鈕
 def playMusic(self):
  playBtn = tkinter.Button(self.frame,text="播放",command=self.playFunc,height=2)
  playBtn.place(x=300,y=10)

 # 實現播放功能
 def playFunc(self):
  pygame.mixer.init()
  track = pygame.mixer.music.load(self.music) # 載入一個音樂檔案用於播放
  pygame.mixer.music.play() # 開始播放音樂流

 # 暫停播放按鈕
 def pauseMusic(self):
  pauseBtn = tkinter.Button(self.frame,text="暫停/繼續",command=self.pauseFunc,height=2)

  pauseBtn.place(x=400,y=10)

 # 暫停播放功能
 def pauseFunc(self):
  # pygame.mixer.music.get_busy() # 檢測是否正在播放音樂
  if self.nowPaly:
   pygame.mixer.music.pause()
   self.nowPaly = False
  else:
   pygame.mixer.music.unpause() # 恢復音樂播放
   self.nowPaly = True

 # 顯示歌曲名稱以及歌手
 def showName(self):
  songName = tkinter.Label(self.frame,fg="white",font=("華文行楷",10),bg="MediumSeaGreen",width=25,height=1)
  songName['text'] = self.songSheet.music.split('.')[0]
  songName.place(x=35,y=15)
  self.music = os.path.join(self.filePath,self.songSheet.music)

  # 更換音樂後應該繼續播放,並且更換音樂時長
  self.playFunc()
  self.songPos()

 # 音量調節
 def volume(self):
  volumeNum = tkinter.Label(self.frame,text="volume",fg="Aquamarine",width=5,height=1)

  volumeNum.place(x=500,y=70)

  volume = tkinter.Scale(self.frame,from_=0,to=100,orient=tkinter.HORIZONTAL)
  volume.place(x=550,y=50)

  def showNum():
   pygame.mixer.music.set_volume(volume.get()*0.01) # 引數值範圍為 0.0~1.0

  tkinter.Button(self.frame,text="設定",command=showNum,bg="Aqua").place(
    x=550,y=100)

 # 音樂絕對定位
 def songPos(self):
  # print(self.music.info.length)
  pos = tkinter.Scale(self.frame,to=round(
    MP3(self.music).info.length),orient=tkinter.HORIZONTAL,tickinterval=50,length=300)

  pos.place(x=180,y=60)
  def showNum():
   # 為了對一個 MP3 檔案的進行絕對定位,建議首先呼叫 rewind()函式,不然會一直往後走
   pygame.mixer.music.rewind()
   if pygame.mixer.music.get_busy():
    self.curDuration = pos.get()
    pygame.mixer.music.set_pos(self.curDuration)
   else:
    print("請先播放音樂!")

  tkinter.Button(self.frame,bg="Aqua").place(
    x=490,y=90)

 # 點選歌單的歌更新名稱
 def refreshName(self):
  refreshNameBtn = tkinter.Button(self.frame,text="update",command=self.showName,height=2)

  refreshNameBtn.place(x=45,y=50)

python實現可下載音樂的音樂播放器

3.核心爬取音樂

music.py

# -*- coding:utf-8 -*-
import requests,hashlib,sys,click,re,base64,binascii,json,os
from Cryptodome.Cipher import AES
from http import cookiejar

class Encrypyed():
 """
 解密演算法
 """

 def __init__(self):
  self.modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
  self.nonce = '0CoJUm6Qyw8W8jud'
  self.pub_key = '010001'

 # 登入加密演算法,基於https://github.com/stkevintan/nw_musicbox指令碼實現
 def encrypted_request(self,text):
  text = json.dumps(text)
  sec_key = self.create_secret_key(16)
  enc_text = self.aes_encrypt(self.aes_encrypt(text,self.nonce),sec_key.decode('utf-8'))
  enc_sec_key = self.rsa_encrpt(sec_key,self.pub_key,self.modulus)
  data = {'params': enc_text,'encSecKey': enc_sec_key}
  return data

 def aes_encrypt(self,text,secKey):
  pad = 16 - len(text) % 16
  text = text + chr(pad) * pad
  encryptor = AES.new(secKey.encode('utf-8'),AES.MODE_CBC,b'0102030405060708')
  ciphertext = encryptor.encrypt(text.encode('utf-8'))
  ciphertext = base64.b64encode(ciphertext).decode('utf-8')
  return ciphertext

 def rsa_encrpt(self,pubKey,modulus):
  text = text[::-1]
  rs = pow(int(binascii.hexlify(text),16),int(pubKey,int(modulus,16))
  return format(rs,'x').zfill(256)

 def create_secret_key(self,size):
  return binascii.hexlify(os.urandom(size))[:16]


class Song():
 """
 歌曲物件,用於儲存歌曲的資訊
 """

 def __init__(self,song_id,song_name,song_num,picUrl,singer_name,song_url=None):
  self.song_id = song_id
  self.song_name = song_name
  self.song_num = song_num
  self.singer_name = singer_name
  self.picUrl = picUrl
  self.song_url = '' if song_url is None else song_url


class Crawler():
 """
 網易雲爬取API
 """

 def __init__(self,timeout=60,cookie_path='.'):
  self.headers = {
   'Accept': '*/*','Accept-Encoding': 'gzip,deflate,sdch','Accept-Language': 'zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4','Connection': 'keep-alive','Content-Type': 'application/x-www-form-urlencoded','Host': 'music.163.com','Referer': 'http://music.163.com/search/','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/63.0.3239.132 Safari/537.36'
  }
  self.session = requests.Session()
  self.session.headers.update(self.headers)
  self.session.cookies = cookiejar.LWPCookieJar(cookie_path)
  self.download_session = requests.Session()
  self.timeout = timeout
  self.ep = Encrypyed()
  self.result =[]

 def post_request(self,url,params):
  """
  Post請求
  :return: 字典
  """

  data = self.ep.encrypted_request(params)
  resp = self.session.post(url,data=data,timeout=self.timeout)
  result = resp.json()
  if result['code'] != 200:
   click.echo('post_request error')
  else:
   return result

 def search(self,search_content,search_type,limit=9):
  """
  搜尋API
  :params search_content: 搜尋內容
  :params search_type: 搜尋型別
  :params limit: 返回結果數量
  :return: 字典.
  """

  url = 'http://music.163.com/weapi/cloudsearch/get/web?csrf_token='
  params = {'s': search_content,'type': search_type,'offset': 0,'sub': 'false','limit': limit}
  result = self.post_request(url,params)
  # print(result['result']['songs'][3]['ar'][0]['name'])

  return result

 def search_song(self,quiet=True,limit=20):
  """
  根據音樂名搜尋
  :params song_name: 音樂名
  :params song_num: 下載的歌曲數
  :params quiet: 自動選擇匹配最優結果
  :params limit: 返回結果數量
  :return: Song獨享
  """

  result = self.search(song_name,search_type=1,limit=limit)

  if result['result']['songCount'] <= 0:
   click.echo('Song {} not existed.'.format(song_name))
  else:
   songs = result['result']['songs']
   if quiet:
    self.result = [] # 更新result
    for song in songs:
     singers = []
     # """
     picUrl = song['al']['picUrl']
     # """
     for name in song['ar']:
      singers.append(name['name'])
     song_id,song_name = song['id'],song['name']
     singer_name = "_".join(singers)
     song = Song(song_id=song_id,song_name=song_name,song_num=song_num,singer_name=singer_name,picUrl=picUrl)
     self.result.append(song)
    picUrl = songs[0]['al']['picUrl']
    # """
    song_id,song_name = songs[0]['id'],songs[0]['name']
    song = Song(song_id=song_id,singer_name=self.result[0].singer_name,picUrl=picUrl)
    return song

 def get_song_url(self,bit_rate=320000):
  """
  獲得歌曲的下載地址
  :params song_id: 音樂ID<int>.
  :params bit_rate: {'MD 128k': 128000,'HD 320k': 320000}
  :return: 歌曲下載地址
  """

  url = 'http://music.163.com/weapi/song/enhance/player/url?csrf_token='
  csrf = ''
  params = {'ids': [song_id],'br': bit_rate,'csrf_token': csrf}
  result = self.post_request(url,params)
  # 歌曲下載地址
  song_url = result['data'][0]['url']

  # 歌曲不存在
  if song_url is None:
   click.echo('Song {} is not available due to copyright issue.'.format(song_id))
  else:
   return song_url

 def get_song_by_url(self,song_url,folder):
  """
  下載歌曲到本地
  :params song_url: 歌曲下載地址
  :params song_name: 歌曲名字
  :params song_num: 下載的歌曲數
  :params folder: 儲存路徑
  """
  # for res in self.result:
  #  print(res.song_name,res.song_id,res.singer_name)
  # print("--------")
  # print(song_url,singer_name)


class Netease():
 """
 網易雲音樂下載
 """

 def __init__(self,timeout,folder,quiet,cookie_path):
  self.crawler = Crawler(timeout,cookie_path)
  self.folder = '.' if folder is None else folder
  self.quiet = quiet
  self.url = ''
  self.pic = ''

 def download_song_by_search(self,song_name):
  """
  根據歌曲名進行搜尋
  :params song_name: 歌曲名字
  :params song_num: 下載的歌曲數
  """

  try:
   song = self.crawler.search_song(song_name,self.quiet)
  except:
   click.echo('download_song_by_serach error')
  # 如果找到了音樂,則下載
  if song != None:
   self.download_song_by_id(song.song_id,song.song_name,song.song_num,song.singer_name,self.folder)
   self.pic = song.picUrl

 def download_song_by_id(self,folder='.'):
  """
  通過歌曲的ID下載
  :params song_id: 歌曲ID
  :params song_name: 歌曲名
  :params song_num: 下載的歌曲數
  :params folder: 儲存地址
  """
  try:
   url = self.crawler.get_song_url(song_id)
   # 去掉非法字元
   song_name = song_name.replace('/','')
   song_name = song_name.replace('.','')
   self.crawler.get_song_by_url(url,folder)

  except:
   click.echo('download_song_by_id error')

4.將爬取音樂搜尋欄整合

searchWindows.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Author: Minions
# @Date: 2019-11-25 10:31:56
# @Last Modified by: Minions
# @Last Modified time: 2019-12-17 12:40:31

import tkinter
from tkinter import ttk
import os
from urllib import request
from music import Netease,Crawler
import requests

class SearchWindows(tkinter.Frame):
 def __init__(self,height=240,width=500,bg="Purple")

  self.songs = None # 搜尋到的所有歌曲(20)的資訊
  self.frame.place(x=300,y=0)
  self.info = None # 當前歌曲的資訊
  self.fileName = "C:\Musics\\"

  timeout = 60
  output = 'Musics'
  quiet = True
  cookie_path = 'Cookie'
  self.netease = Netease(timeout,output,cookie_path)

 def run(self):
  self.searchBar()
  self.download()

 # 搜尋框
 def searchBar(self):
  entry = tkinter.Entry(self.frame)
  entry.place(width=200,height=30,x=50,y=10)

  def getValue():
   self.netease.download_song_by_search(entry.get())
   self.songs = self.netease.crawler.result
   self.showSong()

  searchBtn = tkinter.Button(self.frame,text="搜尋",bg="DarkOrchid",command=getValue,height=1)

  searchBtn.place(x=270,y=10)

 # 顯示搜尋到的歌曲
 def showSong(self):
  tree = ttk.Treeview(self.frame)
  # 定義列
  tree["columns"] = ("song","singer","url")

  # 設定列,width=50)
  tree.column("singer",width=50)
  tree.column("url",width=50)

  # 設定表頭 和上面一一對應
  tree.heading("song",text="song")
  tree.heading("singer",text="singer")
  tree.heading("url",text="url")

  count = len(self.songs)
  for song in reversed(self.songs):
   url = self.netease.crawler.get_song_url(song.song_id)
   tree.insert("",text=count,values=(song.song_name,url))
   count -= 1

  # 滑鼠選中一行回撥
  def selectTree(event):
   for item in tree.selection():
    item_text = tree.item(item,"values")
    self.info = item_text

  # 滾動條
  sy = tkinter.Scrollbar(tree)
  sy.pack(side=tkinter.RIGHT,fill=tkinter.Y)
  sy.config(command=tree.yview)
  tree.config(yscrollcommand=sy.set)

  # 選中行
  tree.bind('<<TreeviewSelect>>',y=50)

 # 下載選中的歌曲
 def download(self):

  def downloadSong():
   if self.info is None:
    print("該歌曲下載失敗")
   else:
    request.urlretrieve(self.info[2],self.fileName+self.info[1]+'-'+self.info[0]+'.mp3')
    print("%s-%s下載成功" %(self.info[1],self.info[0]))
  
  # 下載按鈕
  downloadBtn = tkinter.Button(self.frame,text="下載",command=downloadSong,width=6,height=1)

  downloadBtn.place(x=345,y=200)

python實現可下載音樂的音樂播放器

5.整合所有部分

main.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Author: Minions
# @Date: 2019-11-24 20:10:15
# @Last Modified by: Minions
# @Last Modified time: 2019-12-17 9:55:31

import tkinter
from searchWindows import SearchWindows
from musicCtrl import MusicCtrl
from songSheet import SongSheet
import os

win = tkinter.Tk()
win.title("Minions音樂播放器")
win.geometry("700x400")
if os.path.exists("C:/Musics"):
 print("xxx")
else:
 os.mkdir("C:/Musics")

searchWin = SearchWindows(win)
searchWin.run()

songSheetWin = SongSheet(win)
songSheetWin.run()

musicWin = MusicCtrl(win)
musicWin.run()

win.mainloop()

python實現可下載音樂的音樂播放器

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。