python实现可下载音乐的音乐播放器


Posted in Python onFebruary 25, 2020

本文实例为大家分享了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("", 0, 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, 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)

 # 实现播放功能
 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,
     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() # 恢复音乐播放
  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",
     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) # 参数值范围为 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, from_=0, 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, text="设置", command=showNum, bg="Aqua").place(
  x=490, y=90)

 # 点击歌单的歌更新名称
 def refreshName(self):
 refreshNameBtn = tkinter.Button(self.frame, text="update",command=self.showName,
     width=10, 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, 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():
 """
 歌曲对象,用于存储歌曲的信息
 """

 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, song_name, song_num, 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, 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: 下载的歌曲数
 :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():
 """
 网易云音乐下载
 """

 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, song_id, song_name, song_num, singer_name,
    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, 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 # 当前歌曲的信息
 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")

 # 设置列,列还不显示
 tree.column("song", 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("", 0, text=count, values=(song.song_name,
       song.singer_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>>', 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)

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实现可下载音乐的音乐播放器

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
详解Python中break语句的用法
May 14 Python
利用Python脚本生成sitemap.xml的实现方法
Jan 31 Python
详解Python读取配置文件模块ConfigParser
May 11 Python
Python使用装饰器进行django开发实例代码
Feb 06 Python
几种实用的pythonic语法实例代码
Feb 24 Python
Python2与Python3的区别实例分析
Apr 11 Python
Python中函数的基本定义与调用及内置函数详解
May 13 Python
python随机数分布random均匀分布实例
Nov 27 Python
Python3.7+tkinter实现查询界面功能
Dec 24 Python
ubuntu 安装pyqt5和卸载pyQt5的方法
Mar 24 Python
Python3 ffmpeg视频转换工具使用方法解析
Aug 10 Python
Python selenium如何打包静态网页并下载
Aug 12 Python
Python实现分数序列求和
Feb 25 #Python
python等差数列求和公式前 100 项的和实例
Feb 25 #Python
Django单元测试中Fixtures用法详解
Feb 25 #Python
python实现音乐播放器 python实现花框音乐盒子
Feb 25 #Python
python+selenium+PhantomJS抓取网页动态加载内容
Feb 25 #Python
python numpy生成等差数列、等比数列的实例
Feb 25 #Python
信号生成及DFT的python实现方式
Feb 25 #Python
You might like
php email邮箱正则
2008/10/08 PHP
php使用gzip压缩传输js和css文件的方法
2015/07/29 PHP
thinkPHP框架可添加js事件的分页类customPage.class.php完整实例
2017/03/16 PHP
js动画(animate)简单引擎代码示例
2012/12/04 Javascript
浅谈javascript中call()、apply()、bind()的用法
2015/04/20 Javascript
JS实现兼容各浏览器解析XML文档数据的方法
2015/06/01 Javascript
jQuery实现下拉框选择图片功能实例
2015/08/08 Javascript
JavaScript 数组some()和filter()的用法及区别
2016/05/20 Javascript
mui上拉加载更多下拉刷新数据的封装过程
2017/11/03 Javascript
微信小程序实时聊天WebSocket
2018/07/05 Javascript
详解Angularjs 自定义指令中的数据绑定
2018/07/19 Javascript
layer.prompt输入层的例子
2019/09/24 Javascript
JS实现灯泡开关特效
2020/03/30 Javascript
Vue如何获取数据列表展示
2019/12/11 Javascript
JavaScript中常用的3种弹出提示框(alert、confirm、prompt)
2020/11/10 Javascript
[15:57]教你分分钟做大人:斧王
2014/10/30 DOTA
一个简单的python程序实例(通讯录)
2013/11/29 Python
python字典多条件排序方法实例
2014/06/30 Python
python使用str &amp; repr转换字符串
2016/10/13 Python
Python实现矩阵加法和乘法的方法分析
2017/12/19 Python
Python在for循环中更改list值的方法【推荐】
2018/08/17 Python
详解python校验SQL脚本命名规则
2019/03/22 Python
python redis连接 有序集合去重的代码
2019/08/04 Python
python Pillow图像处理方法汇总
2019/10/16 Python
html5 Canvas画图教程(10)—把面拆成线条模拟出圆角矩形
2013/01/09 HTML / CSS
kmart凯马特官网:美国最大的打折零售商和全球最大的批发商之一
2016/11/17 全球购物
大学生2014全国两会学习心得体会
2014/03/10 职场文书
预防煤气中毒方案
2014/06/16 职场文书
银行安全保卫工作总结
2015/08/10 职场文书
小学生禁毒教育心得体会
2016/01/15 职场文书
商业计划书格式、范文
2019/03/21 职场文书
浅谈Redis存储数据类型及存取值方法
2021/05/08 Redis
MYSQL(电话号码,身份证)数据脱敏的实现
2021/05/28 MySQL
详解Redis集群搭建的三种方式
2021/05/31 Redis
Python函数式编程中itertools模块详解
2021/09/15 Python
解决 redis 无法远程连接
2022/05/15 Redis