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 相关文章推荐
10款最好的Web开发的 Python 框架
Mar 18 Python
Python中return语句用法实例分析
Aug 04 Python
理解Python中的With语句
Mar 18 Python
Python装饰器原理与简单用法实例分析
Apr 29 Python
Python多线程原理与用法详解
Aug 20 Python
基于Python3.6+splinter实现自动抢火车票
Sep 25 Python
python matplotlib库直方图绘制详解
Aug 10 Python
Python log模块logging记录打印用法解析
Jan 20 Python
python with语句的原理与用法详解
Mar 30 Python
Python 可视化神器Plotly详解
Dec 26 Python
一个非常简单好用的Python图形界面库(PysimpleGUI)
Dec 28 Python
解决Pycharm 运行后没有输出的问题
Feb 05 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程序
2012/02/04 PHP
PHP json_decode函数详细解析
2014/02/17 PHP
Yii配置文件用法详解
2014/12/04 PHP
PHP HTTP 认证实例详解
2016/11/03 PHP
javascript[js]获取url参数的代码
2007/10/17 Javascript
jQuery EasyUI API 中文文档 - DataGrid数据表格
2011/11/17 Javascript
JavaScript的模块化:封装(闭包),继承(原型) 介绍
2013/07/22 Javascript
jquery制作 随机弹跳的小球特效
2015/02/01 Javascript
jquery判断复选框选中状态以及区分attr和prop
2015/12/18 Javascript
Bootstrap中文本框的宽度变窄并且加入一副验证码图片的实现方法
2016/06/23 Javascript
JS中常用的正则表达式
2016/09/29 Javascript
详解Vuejs2.0之异步跨域请求
2017/04/20 Javascript
angular directive的简单使用总结
2017/05/24 Javascript
vue+springmvc导出excel数据的实现代码
2018/06/27 Javascript
JQuery实现ajax请求的示例和注意事项
2018/12/10 jQuery
微信小程序授权登录解决方案的代码实例(含未通过授权解决方案)
2019/05/10 Javascript
vue.js表单验证插件(vee-validate)的使用教程详解
2019/05/23 Javascript
js图片查看器插件用法示例
2019/06/22 Javascript
javascript设计模式 ? 解释器模式原理与用法实例分析
2020/04/17 Javascript
使用Python的OpenCV模块识别滑动验证码的缺口(推荐)
2019/05/10 Python
pymysql之cur.fetchall() 和cur.fetchone()用法详解
2020/05/15 Python
没编程基础可以学python吗
2020/06/17 Python
PyCharm最新激活码PyCharm2020.2.3有效
2020/11/18 Python
css3的过滤效果简单实例
2016/08/03 HTML / CSS
用HTML5制作视频拼图的教程
2015/05/13 HTML / CSS
美国时装品牌:Nautica(诺帝卡)
2016/08/28 全球购物
环法自行车赛官方商店:Le Tour de France
2017/08/27 全球购物
全球性的女装店:storets
2019/06/12 全球购物
SQL SERVER面试资料
2013/03/30 面试题
医护人员英文求职信范文
2013/11/26 职场文书
团员个人的自我评价
2013/12/02 职场文书
环保倡议书格式范文
2014/05/14 职场文书
预备党员考察意见范文
2015/06/01 职场文书
2016年教育局“我们的节日——端午节”主题活动总结
2016/04/01 职场文书
解决hive中导入text文件遇到的坑
2021/04/07 Python
Redis分布式锁的7种实现
2022/04/01 Redis