python实现各大平台音乐搜索,下载,收听
再发一遍,旧版本(2021/6/1版本)在这
先上效果图
各大平台的歌随便搜,随便听!
打包后软件地址:https://wws.lanzoui.com/iosS7rlgzmb 密码: 2gm4 (最好用这个)
下面是源码和测试版:
源码:链接: https://pan.baidu.com/s/1N98jNox7zipVBRcqqkh1Hw 提取码: z6pv
备用链接(github): https://github.com/hedy-bit/pyqt5-pygame-urllib-
大制作,1500多行代python码实现各大平台音乐爬取和,下载和收听,使用了pyqt5,pygame和beautifulsoup
初二暑假没事干,看windows10自带的播放器有一(亿)点点不顺眼,然后再看见网易云之类的平台都要vip,所以想写一个播放器,正好又学了点pyqt5,然后就先整了个离线播音乐放器,耗时4天,后来又添加了爬虫模块,和一些功能模块,就做出来了这个音乐搜索器,支持网易,qq,酷狗,酷我的音乐搜索,下载,收听
分几个部分讲解代码
- pyqt5界面
- python爬虫
- 下载
- 功能模块
爬虫线程,下载线程和进度条线程我都是新开了类的,因为在主线程进行爬虫会有一定的概率会触发关闭
界面
快不多说,先上代码
界面的话。。。我觉得挺好看的,由于代码太长我就不放出来了
pyhton爬虫
由于怕被各大平台起诉,所以都是通过第三方平台爬虫的
爬取搜索的歌曲,歌手和url,由于要使用多线程,所以新开了一个类
class PAThread(QThread):
# 自定义信号对象。参数str就代表这个信号可以传一个字符串
trigger = pyqtSignal(str)
def __int__(self):
# 初始化函数
super(PAThread, self).__init__()
def run(self):
qmut.lock()
try:
global paing
global stop
global lrcs
global urls
global songs
global name
global songid
global proxies
global pic
global tryed
paing = True
print('搜索软件{}'.format(type))
print('开始搜索')
name = name
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.110.430.128 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest'
}
urls = []
songs = []
pic = []
lrcs = []
if int(page) == '' or int(page) < 1:
pages = 2
else:
pages = int(page)
print(pages)
for a in range(1, pages + 1):
if not stop:
urlss = ['http://music.9q4.cn/', 'https://defcon.cn/dmusic/', 'http://www.xmsj.org/',
'http://music.laomao.me/']
print(tryed)
if tryed > 3:
tryed = 0
url = urlss[tryed]
else:
url = urlss[tryed]
print(urlss[tryed])
params = {'input': name,
'filter': 'name',
'type': type,
'page': a
}
if not stop:
try:
res = post(url, params, headers=headers, proxies=proxies)
html = res.json()
for i in range(0, 10):
try:
title = jsonpath(html, '$..title')[i]
author = jsonpath(html, '$..author')[i]
url1 = jsonpath(html, '$..url')[i] # 取下载网址
pick = jsonpath(html, '$..pic')[i] # 取歌词
lrc = jsonpath(html, '$..lrc')[i]
print(title, author)
lrcs.append(lrc)
urls.append(url1)
pic.append(pick)
songs.append(str(title) + ' - ' + str(author))
# self.textEdit.setText(lrc) # 打印歌词
# print(lrc)
except:
pass
except:
stop = False
paing = False
print(urls)
print(songs)
self.trigger.emit(str('finish'))
else:
print('stop')
self.trigger.emit(str('finish'))
else:
print('stop')
self.trigger.emit(str('clear'))
pass
stop = False
paing = False
except:
print('爬取歌曲出错')
self.trigger.emit(str('unfinish'))
stop = False
paing = False
qmut.unlock()
然后就是从爬取的url下载歌曲
歌曲下载!
class WorkThread(QThread):
# 自定义信号对象。参数str就代表这个信号可以传一个字符串
trigger = pyqtSignal(str)
def __int__(self):
# 初始化函数
super(WorkThread, self).__init__()
def cbk(self, a, b, c):
'''''回调函数
@a:已经下载的数据块
@b:数据块的大小
@c:远程文件的大小
'''
per = 100.0 * a * b / c
if per > 100:
per = 100
# print ('%.2f%%' % per)
self.trigger.emit(str('%.2f%%' % per))
def run(self):
global to
global number
global path
global downloading
global pic
global lrct
global lrcd
if bo == 'boing':
try:
proxies = {
'http': 'http://124.72.109.183:8118',
' Shttp': 'http://49.85.1.79:31666'
}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest'}
try:
try:
try:
aq = pic[num]
aqq = aq.split('/')
except:
pass
if type == 'kugou' and len(aqq) - 1 == 6:
aqqe = str(aqq[0]) + str('//') + str(aqq[2]) + str('/') + str(aqq[3]) + str('/') + str(
'400') + str('/') + str(aqq[5]) + str('/') + str(aqq[6])
print(aqqe)
elif type == 'netease' and len(aqq) - 1 == 4:
aqn = aq.split('?')
b = '?param=500x500'
aqqe = (str(aqn[0]) + str(b))
print(aqqe)
else:
aqqe = pic[num]
req = get(aqqe)
checkfile = open(str(data + '/ls1.png'), 'w+b')
for i in req.iter_content(100000):
checkfile.write(i)
checkfile.close()
lsfile = str(data + '/ls1.png')
safile = str(data + '/back.png')
draw(lsfile, safile)
except:
pass
url1 = urls[num]
print(url1)
number = number + 1
path = str(data + '\{}.临时文件'.format(number))
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.110.430.128 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest'
}
with get(url1, stream=True, headers=headers) as r, open(path, 'wb') as file:
total_size = int(r.headers['content-length'])
content_size = 0
for content in r.iter_content(chunk_size=1024):
file.write(content)
content_size += len(content)
plan = (content_size / total_size) * 100
# print(int(plan))
develop = str(int(plan)) + str('%')
self.trigger.emit(str(develop))
to = 'downloadmusic\{}.mp3'.format(songs[num])
makedirs('downloadmusic', exist_ok=True)
except:
pass
try:
if bo == 'boing':
lrct = []
f = lrcs[num] # 按行读取
# print (f)
lines = f.split('\n')
# print (lines)
if not lines == ['']:
for i in lines:
if not i == '':
line1 = i.split('[')
try:
line2 = line1[1].split(']')
if line2 == '':
pass
else:
linew = line2[1]
# print(linew)
lrct.append(linew)
self.trigger.emit(str('lrcfinish'))
except:
print('{}的歌词错误'.format(str(line1)))
else:
pass
else:
self.trigger.emit(str('lrcnofinish'))
print('没有歌词')
except:
print('歌词错误')
try:
copyfile(path, to)
except:
pass
downloading = False
self.trigger.emit(str('finish'))
except:
self.trigger.emit(str('nofinish'))
列表点击播放
下面就是QListwidge的点击和播放模块
def bofang(self, num):
print ('try bofang')
try:
import urllib
global pause
global songs
global music
global downloading
downloading = True
self.console_button_3.setIcon(qtawesome.icon('fa.pause', color='#F76677', font=18))
pause = False
# QMessageBox.information(self, "ListWidget", "你选择了: "+item.text())# 显示出消息提示框
try:
pygame.mixer.stop()
except:
pass
pygame.mixer.init()
try:
self.Timer = QTimer()
self.Timer.start(500)
except:
pass
try:
self.label.setText('下载中')#调用开头的多线程下载歌曲
self.work = WorkThread()
self.work.start()
self.work.trigger.connect(self.display)
except:
print ('song download error')
downloading = False
pass
except:
time.sleep(0.1)
print ('system error')
#self.next()
pass
#用于接收返回的信号
def display(self,sd):
if sd == 'finish':
self.label.setText(songs[num])
print ('music\{}.mp3'.format(number))
pygame.mixer.music.load('music\{}.mp3'.format(number)) # 载入音乐
pygame.mixer.music.play()
# 播放音乐
else:
self.label.setText('下载错误')
功能函数
下面是双击播放和上一首还有下一首
#QlistWidget的双击事件
def change_func(self, listwidget):
global num
item = QListWidgetItem(self.listwidget.currentItem())
print(item.text())
num = int(listwidget.currentRow())
self.label.setText(songs[num])
print(listwidget.currentRow())
self.bofang(num)
#下一首按钮
def nextion(self):
try:
if play == 'shun':
print('shuning')
self.next()
elif play == 'shui':
print('shuiing')
self.shui()
elif play == 'always':
print('alwaysing')
self.next()
except:
print('no')
pass
下面自动播放,有循环,随机和单曲循环,加上下一首,上一首,
#随机播放
def shui(self):
global num
global songs
q = int(len(songs) - 1)
num = int(random.randint(1, q))
try:
print('shui')
pygame.mixer.init()
self.Timer = QTimer()
self.Timer.start(500)
# self.Timer.timeout.connect(self.timercontorl)#时间函数,与下面的进度条和时间显示有关
self.label.setText(songs[num])
self.bofang(num) # 播放音乐
except:
pass
#下一首
def next(self):
print ('nexting')
global num
global songs
if num == len(songs) - 1:
print('冇')
num = 0
else:
num = num + 1
try:
self.label.setText(songs[num])
self.bofang(num)
except:
print ('next error')
pass
#单曲循环
def always(self):
try:
self.bofang(num)
self.label.setText(songs[num])
except:
pass
#上一首
def last(self):
global num
global songs
if num == 0:
print('冇')
num = len(songs) - 1
else:
num = num - 1
try:
self.bofang(num)
self.label.setText(songs[num])
except:
pass
下面是播放模式选择和循环判断是否要自动下一首
由于每秒钟判断一次,所以要使用多线程
'''
在init里面的循环判断打开方法
t1 = threading.Thread(target=self.action)
t1.setDaemon(True)
t1.start()
'''
#选择播放模式
def playmode(self):
global play
try:
if play == 'shun':
play = 'shui'
print('随机播放')
self.label2.setText("当前为随机播放")
try:
self.console_button_6.setIcon(qtawesome.icon('fa.random', color='#3FC89C', font=18))
print('done')
except:
print('none')
pass
# self.left_shui.setText('切换为单曲循环')
elif play == 'shui':
play = 'always'
print('单曲循环')
self.label2.setText("当前为单曲循环")
try:
self.console_button_6.setIcon(qtawesome.icon('fa.retweet', color='#3FC89C', font=18))
print('done')
except:
print('none')
# self.left_shui.setText('切换为顺序播放')
elif play == 'always':
play = 'shun'
print('顺序播放')
self.label2.setText("当前为顺序播放")
try:
self.console_button_6.setIcon(qtawesome.icon('fa.align-center', color='#3FC89C', font=18))
print('done')
except:
print('none')
# self.left_shui.setText('切换为随机播放')
except:
print('error')
pass
#循环判断
def action(self):
a = 1
global num
while a < 2:
# print ('checking')
try:
time.sleep(1)
if not pygame.mixer.music.get_busy() and pause == False and not downloading:
if play == 'shun':
print('shuning')
self.next()
elif play == 'shui':
print('shuiing')
self.shui()
elif play == 'always':
print('alwaysing')
self.always()
except:
print('no')
pass
else:
pygame.mixer.music.stop()
还有就是进度条,线程
class barThread(QThread):
# 自定义信号对象。参数str就代表这个信号可以传一个字符串
trigger = pyqtSignal(str)
def __int__(self):
# 初始化函数
super(barThread, self).__init__()
def run(self):
xun4 = 1
# print ('begin')
try:
# print ('check')
sleep(1)
try:
try:
global timenum
xun4 = 1
while xun4 < 2:
sleep(1)
# print ('check')
if not downloading or not paing:
try:
# print ('check pass')
timenumm = timenum * 10000
# print (timenumm)
current = mixer.music.get_pos() # 毫秒
current %= timenumm
assq = current / timenumm * 10000
# print(current)
assq = int(assq * 10)
# print(assq)
if not assq > 10000:
self.trigger.emit(str(assq))
else:
assq = 10000
self.trigger.emit(str(assq))
except:
try:
if mixer.music.get_busy():
print('进度条错误')
except:
pass
except:
pass
except:
pass
except:
pass