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 random模块(获取随机数)常用方法和使用例子
May 13 Python
使用Python编写Linux系统守护进程实例
Feb 03 Python
在Python中操作字符串之startswith()方法的使用
May 20 Python
Python基于PycURL自动处理cookie的方法
Jul 25 Python
浅析Python的web.py框架中url的设定方法
Jul 11 Python
浅谈Python对内存的使用(深浅拷贝)
Jan 17 Python
使用Python微信库itchat获得好友和群组已撤回的消息
Jun 24 Python
python钉钉机器人运维脚本监控实例
Feb 20 Python
解决python中导入win32com.client出错的问题
Jul 26 Python
Pytorch提取模型特征向量保存至csv的例子
Jan 03 Python
python 实现在无序数组中找到中位数方法
Mar 03 Python
Pycharm创建python文件自动添加日期作者等信息(步骤详解)
Feb 03 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类的使用 实例代码讲解
2009/12/28 PHP
laravel实现查询最后执行的一条sql语句的方法
2019/10/09 PHP
JavaScript实现禁止后退的方法
2006/12/27 Javascript
javascript中全局对象的parseInt()方法使用介绍
2013/12/19 Javascript
javascript调试之DOM断点调试法使用技巧分享
2014/04/15 Javascript
js实现鼠标悬停图片上时滚动文字说明的方法
2015/02/17 Javascript
jquery简单实现外部链接用新窗口打开的方法
2015/05/30 Javascript
jquery实现Ctrl+Enter提交表单的方法
2015/07/21 Javascript
整理Javascript基础语法学习笔记
2015/11/29 Javascript
Angular.js回顾ng-app和ng-model使用技巧
2016/04/26 Javascript
页面get请求 中文参数方法乱码问题的快速解决方法
2016/05/31 Javascript
javascript实现抽奖程序的简单实例
2016/06/07 Javascript
Spring MVC中Ajax实现二级联动的简单实例
2016/07/06 Javascript
jQuery悬停文字提示框插件jquery.tooltipster.js用法示例【附demo源码下载】
2016/07/19 Javascript
vue.js 1.x与2.0中js实时监听input值的变化
2017/03/15 Javascript
详解Nodejs之npm&amp;package.json
2017/06/15 NodeJs
vue 2.0项目中如何引入element-ui详解
2017/09/06 Javascript
jQuery实现可兼容IE6的淡入淡出效果告警提示功能示例
2017/09/20 jQuery
详解Web使用webpack构建前端项目
2017/09/23 Javascript
浅谈vue单页面中有多个echarts图表时的公用代码写法
2020/07/19 Javascript
Vue如何实现验证码输入交互
2020/12/07 Vue.js
[01:48]帕吉至宝加入游戏,遗迹战场现“千劫神屠”
2018/04/07 DOTA
python实现文件分组复制到不同目录的例子
2014/06/04 Python
跟老齐学Python之编写类之四再论继承
2014/10/11 Python
selenium+python自动化测试之多窗口切换
2019/01/23 Python
pandas修改DataFrame列名的实现方法
2019/02/22 Python
基于Django ORM、一对一、一对多、多对多的全面讲解
2019/07/26 Python
python多任务之协程的使用详解
2019/08/26 Python
Django Form and ModelForm的区别与使用
2019/12/06 Python
python爬取天气数据的实例详解
2020/11/20 Python
网络工程师自荐书范文
2014/04/01 职场文书
2014银行领导班子群众路线对照检查材料思想汇报
2014/09/17 职场文书
机关作风建设整改方案
2014/10/27 职场文书
工程部部长岗位职责
2015/02/12 职场文书
小学教师工作总结2015
2015/04/07 职场文书
技术员岗位职责范本
2015/04/11 职场文书