python實(shí)現(xiàn)可下載音樂的音樂播放器
本文實(shí)例為大家分享了tkinter+pygame+spider實(shí)現(xiàn)音樂播放器,供大家參考,具體內(nèi)容如下
1.確定頁面
SongSheet ------ 顯示歌單
MusicCtrl ------顯示音樂一些控件(播放,跳轉(zhuǎn),音量調(diào)節(jié))
SearchWindows ------搜索欄(搜索歌曲默認(rèn)顯示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 = "" # 點(diǎn)擊歌曲獲得更新的路徑
self.count = 0 # 計(jì)數(shù),共多少歌曲
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")
# 設(shè)置列,列還不顯示
tree.column("song", width=95)
# 設(shè)置表頭 和上面一一對(duì)應(yīng)
tree.heading("song", text="song")
# 添加數(shù)據(jù) 往第0行添加
for music in musics:
# 去除空格
music = "".join(music.split(" "))
tree.insert("", 0, text=self.count, values=(music))
self.count += 1
# 鼠標(biāo)選中一行回調(diào)
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)
# 添加滾動(dòng)條
sy = tkinter.Scrollbar(tree)
sy.pack(side=tkinter.RIGHT, fill=tkinter.Y)
sy.config(command=tree.yview)
tree.config(yscrollcommand=sy.set)

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, master):
self.frame = tkinter.Frame(master,height=150, width=700, bd=1,
bg="MediumSeaGreen")
self.frame.place(height=150, width=700, x=0, 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,
width=10,height=2)
playBtn.place(x=300,y=10)
# 實(shí)現(xiàn)播放功能
def playFunc(self):
pygame.mixer.init()
track = pygame.mixer.music.load(self.music) # 載入一個(gè)音樂文件用于播放
pygame.mixer.music.play() # 開始播放音樂流
# 暫停播放按鈕
def pauseMusic(self):
pauseBtn = tkinter.Button(self.frame, text="暫停/繼續(xù)",
command=self.pauseFunc,
width=10, 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() # 恢復(fù)音樂播放
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)
# 更換音樂后應(yīng)該繼續(xù)播放,并且更換音樂時(shí)長
self.playFunc()
self.songPos()
# 音量調(diào)節(jié)
def volume(self):
volumeNum = tkinter.Label(self.frame, text="volume", fg="Aquamarine",
font=("華文行楷", 10), bg="MediumSeaGreen",
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) # 參數(shù)值范圍為 0.0~1.0
tkinter.Button(self.frame, text="設(shè)置", command=showNum, bg="Aqua").place(
x=550, y=100)
# 音樂絕對(duì)定位
def songPos(self):
# print(self.music.info.length)
pos = tkinter.Scale(self.frame, from_=0, to=round(
MP3(self.music).info.length),
orient=tkinter.HORIZONTAL, tickinterval=50, length=300)
pos.place(x=180, y=60)
def showNum():
# 為了對(duì)一個(gè) MP3 文件的進(jìn)行絕對(duì)定位,建議首先調(diào)用 rewind()函數(shù),不然會(huì)一直往后走
pygame.mixer.music.rewind()
if pygame.mixer.music.get_busy():
self.curDuration = pos.get()
pygame.mixer.music.set_pos(self.curDuration)
else:
print("請(qǐng)先播放音樂!")
tkinter.Button(self.frame, text="設(shè)置", command=showNum, bg="Aqua").place(
x=490, y=90)
# 點(diǎn)擊歌單的歌更新名稱
def refreshName(self):
refreshNameBtn = tkinter.Button(self.frame, text="update",command=self.showName,
width=10, height=2)
refreshNameBtn.place(x=45, y=50)

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腳本實(shí)現(xiàn)
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, text, pubKey, modulus):
text = text[::-1]
rs = pow(int(binascii.hexlify(text), 16), int(pubKey, 16), int(modulus, 16))
return format(rs, 'x').zfill(256)
def create_secret_key(self, size):
return binascii.hexlify(os.urandom(size))[:16]
class Song():
"""
歌曲對(duì)象,用于存儲(chǔ)歌曲的信息
"""
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():
"""
網(wǎng)易云爬取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請(qǐng)求
: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: 搜索內(nèi)容
:params search_type: 搜索類型
:params limit: 返回結(jié)果數(shù)量
: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, song_name, song_num, quiet=True, limit=20):
"""
根據(jù)音樂名搜索
:params song_name: 音樂名
:params song_num: 下載的歌曲數(shù)
:params quiet: 自動(dòng)選擇匹配最優(yōu)結(jié)果
:params limit: 返回結(jié)果數(shù)量
:return: Song獨(dú)享
"""
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, song_name=song_name,
song_num=song_num, singer_name=self.result[0].singer_name,
picUrl=picUrl)
return song
def get_song_url(self, song_id, 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, song_name, song_num, singer_name,
folder):
"""
下載歌曲到本地
:params song_url: 歌曲下載地址
:params song_name: 歌曲名字
:params song_num: 下載的歌曲數(shù)
:params folder: 保存路徑
"""
# for res in self.result:
# print(res.song_name, res.song_id, res.singer_name)
# print("--------")
# print(song_url, song_name, singer_name)
class Netease():
"""
網(wǎng)易云音樂下載
"""
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):
"""
根據(jù)歌曲名進(jìn)行搜索
:params song_name: 歌曲名字
:params song_num: 下載的歌曲數(shù)
"""
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, song_id, song_name, song_num, singer_name,
folder='.'):
"""
通過歌曲的ID下載
:params song_id: 歌曲ID
:params song_name: 歌曲名
:params song_num: 下載的歌曲數(shù)
: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, song_name, song_num,
singer_name, 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, master):
self.frame = tkinter.Frame(master, height=240, width=500, bd=1,
bg="Purple")
self.songs = None # 搜索到的所有歌曲(20)的信息
self.frame.place(x=300,y=0)
self.info = None # 當(dāng)前歌曲的信息
self.fileName = "C:\Musics\\"
timeout = 60
output = 'Musics'
quiet = True
cookie_path = 'Cookie'
self.netease = Netease(timeout, output, quiet, 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, width=10, height=1)
searchBtn.place(x=270, y=10)
# 顯示搜索到的歌曲
def showSong(self):
tree = ttk.Treeview(self.frame)
# 定義列
tree["columns"] = ("song", "singer", "url")
# 設(shè)置列,列還不顯示
tree.column("song", width=50)
tree.column("singer", width=50)
tree.column("url", width=50)
# 設(shè)置表頭 和上面一一對(duì)應(yīng)
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("", 0, text=count, values=(song.song_name,
song.singer_name, url))
count -= 1
# 鼠標(biāo)選中一行回調(diào)
def selectTree(event):
for item in tree.selection():
item_text = tree.item(item, "values")
self.info = item_text
# 滾動(dòng)條
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>>', selectTree)
tree.place(width=300, height=200, x=50, 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="下載", bg="DarkOrchid",
command=downloadSong, width=6, height=1)
downloadBtn.place(x=345, y=200)

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()

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
DjangoWeb使用Datatable進(jìn)行后端分頁的實(shí)現(xiàn)
這篇文章主要介紹了DjangoWeb使用Datatable進(jìn)行后端分頁的實(shí)現(xiàn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-05-05
如何用python腳本實(shí)現(xiàn)一次獲取token,多次使用token
這篇文章主要介紹了如何用python腳本實(shí)現(xiàn)一次獲取token,多次使用token問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08
Python實(shí)現(xiàn)修改IE注冊(cè)表功能示例
這篇文章主要介紹了Python實(shí)現(xiàn)修改IE注冊(cè)表功能,結(jié)合完整實(shí)例形式分析了Python操作IE注冊(cè)表項(xiàng)的相關(guān)實(shí)現(xiàn)技巧與注意事項(xiàng),需要的朋友可以參考下2018-05-05
keras實(shí)現(xiàn)調(diào)用自己訓(xùn)練的模型,并去掉全連接層
這篇文章主要介紹了keras實(shí)現(xiàn)調(diào)用自己訓(xùn)練的模型,并去掉全連接層,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-06-06
一篇文章帶你搞定Ubuntu中打開Pycharm總是卡頓崩潰
這篇文章主要介紹了一篇文章帶你搞定Ubuntu中打開Pycharm總是卡頓崩潰,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
Python解析JSON數(shù)據(jù)的方法簡單例子
這篇文章主要給大家介紹了關(guān)于Python解析JSON數(shù)據(jù)的方法,解析JSON文件是Python中非常常見的操作,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-09-09
關(guān)于sklearn包導(dǎo)入錯(cuò)誤:ImportError:?cannot?import?name Type解
這篇文章主要介紹了關(guān)于sklearn包導(dǎo)入錯(cuò)誤:ImportError:?cannot?import?name‘Type‘解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02

