Python爬虫酷狗音乐
小祁同学 2022-12-08
技术文章
python
介绍
使用Python玩转酷狗音乐,各种玩法,https://music.goodluckweb.top/
# 前言
本篇博客介绍使用python爬虫酷狗音乐
兴趣源于大一学python时,老师带我们爬酷狗,当时就觉得很高级。
大学时光转瞬即逝,以后该上班了,自然也就没有时间去研究酷狗了。
这里介绍两篇,一篇是纯python制作的c/s模式,另一篇是python使用flask,前端vue,其中前端vue是学习vue时,在b站跟着黑马的老师学的,还有一个前端是油猴插件对酷狗的一个注入,实现下载功能。
界面如下:
# 免责申明
- 以下代码和程序仅限于技术研究和项目开发练习使用,禁止商业用途,如有发现直接关闭服务
- 音乐版权归音乐平台所有,若有侵犯版权,请联系我删除
# 控制台版本
- 使用requests直接访问酷狗的api
- 返回json后解析对应的信息
- os下载MP3地址
- pygame播放音乐
import requests,json,os,pygame,sys
# 获取详情信息
def getDetail(hash,id):
url = 'https://wwwapi.kugou.com/yy/index.php?r=play/getdata&callback=jQuery191010266427224936736_1546780601995&hash=' + hash + '&album_id=' + \
str(id) + '&_=1660098516343&dfid=3MnIg12Pjtqc2t4XoN29Sycy&appid=1014&mid=3c64b7e900714c1ac334f255fe6a72e2&platid=4'
content = requests.get(url, cookies=cookies).text
data = content.replace('jQuery191010266427224936736_1546780601995(', '').replace(');', '')
json_data = json.loads(data)
return json_data["data"]["play_url"]
# 播放
def playMusic(url,name):
pygame.mixer.init()
if not os.path.exists(directoiry):
os.makedirs(directoiry)
filenamepath = directoiry + "\\" + name + ".mp3"
print(filenamepath)
if not os.path.exists(filenamepath):
file = open(filenamepath, "wb+")
file.write(requests.get(url, headers).content)
file.close()
print(name+" 下载完成!")
pygame.mixer.music.load(filenamepath)
pygame.display.set_caption(name)
pygame.mixer.music.play()
pygame.display.set_mode([400, 400])
# 设置打开界面的关闭方法,没有的话打开的界面没法关闭。
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# 调用kugou-api搜索
def search(keyword):
url = "http://mobilecdn.kugou.com/api/v3/search/song?format=json&keyword=" + keyword + "&page=1&pagesize=20&showtype=1"
content = requests.get(url).text
json_data = json.loads(content)
for i in range(0, len(json_data["data"]["info"])):
music_detail = json_data["data"]["info"][i]
print("编号:"+str(i+1)+"\t歌曲:"+music_detail["filename"])
data.append(music_detail)
if __name__ == '__main__':
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0"}
cookies = {
'Hm_lvt_aedee6983d4cfc62f509129360d6bb3d': '1670739555',
'kg_dfid_collect': 'd41d8cd98f00b204e9800998ecf8427e',
'kg_mid': '3c64b7e900714c1ac334f255fe6a72e2',
'kg_dfid': '3MnIg12Pjtqc2t4XoN29Sycy',
'kg_mid_temp': '3c64b7e900714c1ac334f255fe6a72e2'
}
data = []
directoiry = "D:\\music"
search_str = input("请输入歌名:")
search(search_str)
print("-"*30)
play_num = int(input("请输入你要播放的音乐编号:"))
play_num = play_num - 1
mp3url = getDetail(data[play_num]["hash"], data[play_num]["album_id"])
playMusic(mp3url,data[play_num]["filename"])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# Tkinter版本
此版本是对一位学长所写的进行二次开发,此版本有4个图片,运行时需要下载该4个图片,目录及文件名如下:
from tkinter import *
import requests,re,json,os,pygame,threading,time,datetime,sys
from tkinter.filedialog import askdirectory#选择目录
#全局
thread_Sign = []#错误
zhan = 0
data = []
LocalMusic = []
# 音质
extension = ".mp3"
# 歌词
lyric_play = ""
d = {"song":"{}","url":"{}","size":"{}","Lyrics":""}#放列表里面
headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0"}
allRanking = {"Top500":"https://m.kugou.com/rank/info/?rankid=8888&page={}&json=true","飙升榜":"https://m.kugou.com/rank/info/?rankid=6666&page={}&json=true",
"华语新歌":"https://m.kugou.com/rank/info/?rankid=31308&page={}&json=true","欧美新歌榜":"https://m.kugou.com/rank/info/?rankid=31310&page={}&json=true",
"粤语新歌榜":"https://m.kugou.com/rank/info/?rankid=31313&page={}&json=true","韩国新歌榜":"https://m.kugou.com/rank/info/?rankid=31311&page={}&json=true"}
def search():#爬取的仅为第一页的歌曲,每页30首歌
global zhan
if zhan == 0:
zhan = 1
elif zhan == 1:
return
l_state["text"] = "正在搜索{}..".format(strVar.get())
data.clear()
list1.delete(0, END)
music_goodluck_url = "https://music.api.goodluckweb.top/search?keywords=" + strVar.get()
json_data = requests.get(music_goodluck_url).text
# html = gethtml("http://songsearch.kugou.com/song_search_v2?callback=jQuery11240438564549339878_1546412498217&keyword={}&page=1&pagesize=30&userid=-1&clientver=&platform=WebFilter&tag=em&filter=2&iscorrection=1&privilege_filter=0&_=1546412498221".format(strVar.get()))
jsoning = json.loads(json_data)
for i in range(0, len(jsoning["data"]["info"])):
music_detail = jsoning["data"]["info"][i]
data.append(music_detail)
list1.insert(END, " " + music_detail["filename"])
l_state["text"] = "搜索成功,共搜索到{}首歌".format(len(jsoning["data"]["info"]))
zhan = 0
def getRanking(ranking):#获取############################333
global zhan
if zhan == 0:
zhan = 1
elif zhan == 1:
return
l_state["text"] = "开始获取“{}”列表...".format(ranking)
data.clear()
list1.delete(0, END)
# for i in range(1, 23):
for i in range(1, 2):
rank_url = allRanking[ranking].format(i)
content = requests.get(rank_url).text
jsoning = json.loads(content)
for j in range(0, len(jsoning["songs"]["list"])):
music_detail = jsoning["songs"]["list"][j]
data.append(music_detail)
list1.insert(END, " " + music_detail["filename"])
zhan = 0
l_state["text"] = "“{}”搜索成功".format(ranking)
def getLocalMusic():
LocalMusic.clear();LocalMusic.append(os.listdir(savePath.get()))
# 进度条
def downloadpro(url, file_path):
# stream = True,表示采用流下载
r = requests.get(url, stream=True, verify=False)
# 既然要实现下载进度,那就要知道你文件大小啊,下面这句就是得到总大小
total_size = int(r.headers['Content-Length'])
temp_size = 0
root.geometry('500x370')
l_progress = Label(root, bd=0, bg=color2, fg="#FFFFFF")
l_progress.place(x=0, y=350, height=20, width=500)
with open(file_path, "wb+") as f:
# iter_content()函数就是得到文件的内容,
# 有些人下载文件很大怎么办,内存都装不下怎么办?
# 那就要指定chunk_size=1024,大小自己设置,
# 意思是下载一点写一点到磁盘。
for chunk in r.iter_content(chunk_size=1024):
if chunk:
temp_size += len(chunk)
f.write(chunk)
f.flush()
#############花哨的下载进度部分###############
done = int(50 * temp_size / total_size)
l_progress["text"] = "[%s%s] %d%%" % ('█' * done, ' ' * (50 - done), 100 * temp_size / total_size)
l_progress.place_forget()
#下载
def downLoad(music = 0):#如果直接传入某个值。只下载单首歌曲
downList = []
global extension
if music==-1:
downList_select = list1.curselection()
index = downList_select[0]
music_hash = data[index]["hash"]
if quality.get() == 1 and data[index]["sqfilesize"] > 0:
music_hash = data[index]["sqhash"]
extension = ".flac"
elif quality.get() == 2 and data[index]["320filesize"] > 0:
music_hash = data[index]["320hash"]
music_goodluck_url = "https://music.api.goodluckweb.top/song?hash={}&id={}".format(music_hash,data[index]["album_id"])
json_data = requests.get(music_goodluck_url).text
jsoning = json.loads(json_data)
global lyric_play
lyric_play = jsoning["data"]["lyrics"]
if os.path.exists(savePath.get() + "\\" + data[index]["filename"] + extension):
return
downList.append(jsoning["data"])
else:
print("下载")
downList_select = list1.curselection()
extension = ".mp3"
for i in range(0, len(downList_select)):
index = downList_select[i]
music_hash = data[index]["hash"]
if quality.get() == 1 and data[index]["sqfilesize"] > 0:
music_hash = data[index]["sqhash"]
extension = ".flac"
elif quality.get() == 2 and data[index]["320filesize"] > 0:
music_hash = data[index]["320hash"]
music_goodluck_url = "https://music.api.goodluckweb.top/song?hash={}&id={}".format(music_hash,
data[index]["album_id"])
json_data = requests.get(music_goodluck_url).text
# html = gethtml("http://songsearch.kugou.com/song_search_v2?callback=jQuery11240438564549339878_1546412498217&keyword={}&page=1&pagesize=30&userid=-1&clientver=&platform=WebFilter&tag=em&filter=2&iscorrection=1&privilege_filter=0&_=1546412498221".format(strVar.get()))
jsoning = json.loads(json_data)
downList.append(jsoning["data"])
time.sleep(1)
urlError = []
path = savePath.get()
l_state["text"] = "开始寻找,共{}首".format(len(downList))
if path != "":
if not os.path.exists(path):
os.makedirs(path)
getLocalMusic()
for i in range(0,len(downList)):
url = downList[i]['play_url'];musicName = '{}\\{}'.format(path,downList[i]['audio_name']+extension)
if url == "":urlError.append((downList[i]['audio_name'],"歌曲下载权限不够"));continue
elif musicName in LocalMusic:urlError.append((downList[i]['audio_name'],"本地存在"));print("本地存在");continue
# file = open(musicName,"wb+")
# file.write(requests.get(url,headers).content)
# file.close()
downloadpro(url,musicName)
l_state["text"] = "进度 ({}/{})".format(i+1,len(downList))
l_state["text"] = "下载完成"
print("失败记录:共失败{}首:".format(len(urlError))+ str(urlError))
def Lyrics(i):#歌词
time_List = {}
ls_i1 = lyric_play.split("\n")
for ls_i2 in ls_i1:
g = re.search("\[(.*?)\](.*)\r",ls_i2)
if g == None:break
time_List[g[1]] = g[2]
time_start = datetime.datetime.now()
root.geometry('500x370')
l_geci = Label(root,bd = 0,bg = color2,fg = "#FFFFFF")
l_geci.place(x=0, y=350, height=20, width=500)
print("循环心跳_启动")
while True:
time_i = str(datetime.datetime.now() - time_start)[2:10]
time.sleep(0.01)
if time_i in time_List:
# print("找到歌词",time_List[time_i])
l_geci["text"] = time_List[time_i]
if pygame.mixer.music.get_busy() == 0:
print("播放结束")
break
def list1_shj():#播放
pygame.mixer.init()#初始化播放器
# list1_val 选中的选项
list1_val = list1.curselection()
if len(list1_val)-1 == -1:#如果选中为-1
return
# i = list1_val[len(list1_val)-1]
# i判断几首音乐
i = len(list1_val)-1
# if not os.path.exists(savePath.get() + "\\" + data[list1_val[0]]["filename"] + extension):
downLoad(-1)
# 下面代码还有问题
if not os.path.exists(savePath.get()+"\\"+data[list1_val[0]]["filename"]+extension):
print("歌曲下载失败,无法播放")
return
list1.select_clear(0,list1.size())
pygame.mixer.music.load(savePath.get()+"\\"+data[list1_val[0]]["filename"]+extension)
pygame.mixer.music.play()
print("正在播放"+data[list1_val[0]]["filename"]+extension)
play_tip = "正在播放"+data[list1_val[0]]["filename"]+extension
if len(play_tip) > 16:
play_tip = play_tip[0:16]+"..."
l_state["text"] = play_tip
# 歌词
run(Lyrics,i)
#多线程
def run(function,*canshu):#传入参数
print("多线程启动"+str(function))
t = threading.Thread(target = function,args=canshu)
t.setDaemon(True)
t.start()
#事件函数
def selectAllButton(event):
if IntVar1.get() == 1:
list1.select_clear(0, list1.size())
IntVar1.set(0)
else:
list1.select_set(0, list1.size())
IntVar1.set(1)
all.select()
def selectButton(event,val):
if val == 1:
radio1.select()
elif val == 2:
radio2.select()
elif val == 3:
radio3.select()
# print(quality.get())
def bu2Callback():#插入列表框
list1.delete(0,END)
for i in data:
list1.insert(END," "+i["song"])
def allClick(event):#全选按钮
if IntVar1.get() == 1:
list1.select_clear(0,list1.size())
else:
list1.select_set(0, list1.size())
def top_Click(event):#点击顶部,标记坐标
click_xy[0] = event.x
click_xy[1] = event.y
print(click_xy)
def top_Move(event):#拖动
root.geometry("+{}+{}".format(root.winfo_rootx() + event.x - click_xy[0], root.winfo_rooty() + event.y - click_xy[1]))
def close_Click(event):
sys.exit()
def callback(event,name,shuxing,zhi):#l_Close["image"] = img3这样做才可以
name[shuxing] = zhi
def binds(widget,*Events_function):#传入多个由控件和所对应事件组成的元组
for i in Events_function:
widget.bind(i[0],i[1])
def list1_Click(event):#设置列表框选中项#################################????????
for i in list1.curselection():
list1.itemconfig(i, bg="#FFFFFF")
def choosePath(event):#选择地址
save = askdirectory()
if save != "":
savePath.set(save)
# 屏蔽warning信息,因为下面verify=False会报警告信息
requests.packages.urllib3.disable_warnings()
#界面
color1 = "#e2e6e7"#默认背景
color2 = "#2c323b"#默认黑色
click_xy = [0,0]
root = Tk()
root.title("酷狗音乐")
root.geometry('500x350+500+200')
root.overrideredirect(True)
img1 = PhotoImage(file = os.getcwd()+"\\images\\背景.png")
back = Label(root,bd = 10,image = img1)
back.place(x=0,y=0,height = 350,width = 500)
list1 = Listbox(root,bd = 0,bg = "#3e4550",fg ="#FFFFFF",selectmode = MULTIPLE)#列表框,设置多行
list1.place(x = 99, y = 39,width = 403,height = 272);binds(list1,("<Double-1>",lambda event:run(list1_shj)))
l_top = Label(root,bg = color2)#顶部
l_top.place(x=0,y=0,height = 40,width = 500)
l_top.bind("<B1-Motion>", top_Move) # 拿下拖动
l_top.bind("<Button-1>", top_Click)
l_d = Label(root,bg = color2)#底部
l_d.place(x=0,y=310,height = 40,width = 500)
icon = PhotoImage(file = os.getcwd()+"\\images\\图标.png")#图标
l_icon = Label(root,image = icon)
l_icon.place(x=0,y=0,width = 40,height = 40)
l_title = Label(root,text = "酷狗音乐",bg = color2,fg = "#FFFFFF",font = ("微软雅黑",10))
l_title.place(x=28,y=6)
l_state = Label(root,text = "",bg = color2,fg = "#e2e2e2",font = ("微软雅黑",8))#提示
l_state.place(x=1,y=330)
img3 = PhotoImage(file = os.getcwd()+"\\images\\焦点.png")
img2 = PhotoImage(file = os.getcwd()+"\\images\\关闭.png")
l_Close = Label(root,image = img2)
l_Close.place(x=460,y=0,height = 40,width = 40)
binds(l_Close,("<Button-1>",close_Click),("<Enter>",lambda event:callback(event,l_Close,"image",img3)),("<Leave>",lambda event:callback(event,l_Close,"image",img2)))
l1 = Label(root,text = "搜索:",bg = color2,fg = "#dddddd",font =("微软雅黑",10))
l1.place(x = 100,y = 8)
strVar = StringVar()#设置文本模型,搜索文本框
e_Search = Entry(root,bd=0,bg = "#343b45",fg = "#dddddd",font =("微软雅黑",10),textvariable = strVar)
e_Search.place(x = 140,y = 11,width = 150)
binds(e_Search,("<Return>",lambda event:run(search)),("<FocusIn>",lambda event:callback(event,e_Search,"bg","#505a68")),("<FocusOut>",lambda event:callback(event,e_Search,"bg","#343b45")))
butSearch = Button(root,bd = 0,bg = "#3d444e",fg = "#d3d2d1",text = '搜索',)#搜索按钮
butSearch.place(x = 295,y = 10,height = 23,width = 40,)
binds(butSearch,("<Button-1>",lambda event:run(search)),("<Enter>",lambda event:callback(event,butSearch,"bg","#505a68")),("<Leave>",lambda event:callback(event,butSearch,"bg","#343b45")))
quality=IntVar()
quality.set(1)
radio1 = Radiobutton(root,bg = color2,font =("微软雅黑",10),value=1,variable=quality,activebackground=color2)
radio1.place(x=340,y=8)
radio1Label = Label(root,text = "超",bg = color2,fg = "#dddddd",font =("微软雅黑",10))
radio1Label.place(x=355,y=10)
radio1Label.bind("<Button-1>",lambda event: selectButton(event, 1))
radio2 = Radiobutton(root,bg = color2,font =("微软雅黑",10),value=2,variable=quality,activebackground=color2)
radio2.place(x=380,y=8)
radio2Label = Label(root,text = "高",bg = color2,fg = "#dddddd",font =("微软雅黑",10))
radio2Label.place(x=395,y=10)
radio2Label.bind("<Button-1>",lambda event: selectButton(event, 2))
radio3 = Radiobutton(root,bg = color2,font =("微软雅黑",10),value=3,variable=quality,activebackground=color2)
radio3.place(x=420,y=8)
radio3Label = Label(root,text = "普",bg = color2,fg = "#dddddd",font =("微软雅黑",10))
radio3Label.place(x=435,y=10)
radio3Label.bind("<Button-1>",lambda event: selectButton(event, 3))
b_500 = Button(root,bd = 0,bg = "#343b45",fg = "#e2e2e2",text = "排行榜500")#500排行
b_500.place(x =0 , y = 40,width = 100,height = 45)
binds(b_500,("<Button-1>",lambda event:run(getRanking,"Top500")),("<Enter>",lambda event:callback(event,b_500,"bg","#505a68")),("<Leave>",lambda event:callback(event,b_500,"bg","#343b45")))
b_2 = Button(root,bd = 0,bg = "#343b45",fg = "#e2e2e2",text = "酷狗飙升榜")#飙升榜
b_2.place(x =0 , y = 85,width = 100,height = 45)
binds(b_2,("<Button-1>",lambda event:run(getRanking,"飙升榜")),("<Enter>",lambda event:callback(event,b_2,"bg","#505a68")),("<Leave>",lambda event:callback(event,b_2,"bg","#343b45")))
b_3 = Button(root,bd = 0,bg = "#343b45",fg = "#e2e2e2",text = "华语新歌榜")#华语新歌
b_3.place(x =0 , y = 130,width = 100,height = 45)
binds(b_3,("<Button-1>",lambda event:run(getRanking,"华语新歌")),("<Enter>",lambda event:callback(event,b_3,"bg","#505a68")),("<Leave>",lambda event:callback(event,b_3,"bg","#343b45")))
b_4 = Button(root,bd = 0,bg = "#343b45",fg = "#e2e2e2",text = "欧美新歌榜")#欧美新歌榜
b_4.place(x =0 , y = 175,width = 100,height = 45)
binds(b_4,("<Button-1>",lambda event:run(getRanking,"欧美新歌榜")),("<Enter>",lambda event:callback(event,b_4,"bg","#505a68")),("<Leave>",lambda event:callback(event,b_4,"bg","#343b45")))
b_5 = Button(root,bd = 0,bg = "#343b45",fg = "#e2e2e2",text = "粤语新歌榜")#粤语新歌榜
b_5.place(x =0 , y = 220,width = 100,height = 45)
binds(b_5,("<Button-1>",lambda event:run(getRanking,"粤语新歌榜")),("<Enter>",lambda event:callback(event,b_5,"bg","#505a68")),("<Leave>",lambda event:callback(event,b_5,"bg","#343b45")))
b_6 = Button(root,bd = 0,bg = "#343b45",fg = "#e2e2e2",text = "韩国新歌榜")#韩国新歌榜
b_6.place(x =0 , y = 265,width = 100,height = 45)
binds(b_6,("<Button-1>",lambda event:run(getRanking,"韩国新歌榜")),("<Enter>",lambda event:callback(event,b_6,"bg","#505a68")),("<Leave>",lambda event:callback(event,b_6,"bg","#343b45")))
IntVar1 = IntVar()#全选按钮
all = Checkbutton(root,bg = color2,variable = IntVar1,activebackground=color2)
all.place(x = 440,y = 315)
all.bind("<Button-1>",allClick)
allLabel = Label(root,text = "全选",bg = color2,fg = "#dddddd",font =("微软雅黑",10))
allLabel.place(x=460,y=315)
allLabel.bind("<Button-1>",selectAllButton)
b_Download = Button(root,bd = 0,bg = "#3d444e",fg = "#d3d2d1",text = '下载')#下载按钮
b_Download.place(x = 350,y = 315,width = 80,)
binds(b_Download,("<Button-1>",lambda event:run(downLoad)),("<Enter>",lambda event:callback(event,b_Download,"bg","#505a68")),("<Leave>",lambda event:callback(event,b_Download,"bg","#343b45")))
l_save = Label(root,text = "保存路径:",bg = color2,fg = "#dddddd",font =("微软雅黑",10))#标签(保存地址)
l_save.place(x = 159,y = 318)
savePath = StringVar()#保存地址
savePath.set("D:\music")
e1_Save = Entry(root,bd = 0,bg = "#343b45",fg = "#dddddd",font =("微软雅黑",10),textvariable = savePath)
e1_Save.place(x = 225,y = 319,width = 120)
binds(e1_Save,("<Button-1>",choosePath),("<FocusIn>",lambda event:callback(event,e1_Save,"bg","#505a68")),("<FocusOut>",lambda event:callback(event,e1_Save,"bg","#343b45")))
root.mainloop()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
# Flask代理酷狗api
这种方式仅做一下代理,需要修改cookie为你的cookie,如果你有vip,那么就可以听无损和vip音乐
# -*- coding: utf-8 -*-
import urllib, requests, flask, hashlib, time, json
from flask import request
from flask_cors import CORS
'''
@description: 酷狗api代理
@author: 小祁同学
@date: 2022-12-11
@com: www.goodluckweb.top
'''
# 开发api和解决跨域
server = flask.Flask(__name__)
CORS(server, supports_credentials=True)
# 登录信息/vip 切换KuGoo(大概24小时酷狗网页登录一次)
#!!!注意这里需要换成你的,用你的账号登录,如果你的账号有vip,那么可以解析无损的
cookies = {
'Hm_lvt_aedee6983d4cfc62f509129360d6bb3d': '1670739555',
'kg_dfid_collect': 'd41d8cd98f00b204e9800998ecf8427e',
'kg_mid': '3c64b7e900714c1ac334f255fe6a72e2',
'kg_dfid': '3MnIg12Pjtqc2t4XoN29Sycy',
'kg_mid_temp': '3c64b7e900714c1ac334f255fe6a72e2'
}
# 下载,向前端返回二进制流(单曲下载,一次返回一个流文件) 酷狗
@server.route('/download', methods=['POST'])
def downloadMp3():
getDataJson = request.get_data()
getData = json.loads(getDataJson)
hash = getData["hash"]
album_id = getData["album_id"]
# 获取歌曲详情信息
dataValue = getMp3Datas(hash, album_id)
dataValue = json.loads(dataValue)
# 歌曲mp3实际地址
url = dataValue['data']['play_url']
# url为空,下载失败
if url == "":
return "{code:'0'}"
else:
f = urllib.request.urlopen(url)
# 读取二进制流文件
fileContent = f.read()
return fileContent
# 搜索歌曲,热搜,自动出框搜索
@server.route('/searchTip')
def searchTip():
name = request.values.get('keywords')
url = "https://searchtip.kugou.com/getSearchTip?MusicTipCount=10&keyword=" + name + "&MVTipCount=0&albumcount=0&callback=jQuery191008413846929136715_1660113619867&_=1660113619870"
content = requests.get(url, cookies=cookies).text
data = content.replace('jQuery191008413846929136715_1660113619867(', '').replace(')', '')
return data
# 搜索歌曲
@server.route('/search')
def search():
name = request.values.get('keywords')
url = "http://mobilecdn.kugou.com/api/v3/search/song?format=json&keyword=" + name + "&page=1&pagesize=20&showtype=1"
content = requests.get(url).text
return content
# 获取歌曲详细信息 (后端调用)
def getMp3Datas(hash, id):
url = 'https://wwwapi.kugou.com/yy/index.php?r=play/getdata&callback=jQuery191010266427224936736_1546780601995&hash=' + hash + '&album_id=' + \
str(id) + '&_=1660098516343&dfid=3MnIg12Pjtqc2t4XoN29Sycy&appid=1014&mid=3c64b7e900714c1ac334f255fe6a72e2&platid=4'
content = requests.get(url, cookies=cookies).text
data = content.replace('jQuery191010266427224936736_1546780601995(', '').replace(');', '')
return data
# 获取歌曲详细信息 (前端调用)
@server.route('/song')
def song():
hash = request.values.get('hash')
album_id = request.values.get('id')
data = getMp3Datas(hash, album_id)
return data
# 酷狗音乐热评,评论
@server.route('/comment/hot')
def get_comment():
hash = request.values.get('hash')
url = "https://mcomment.kugou.com/index.php?r=commentsv2/getCommentWithLike&code=fc4be23b4e972707f36b8a828a93ba8a&extdata="+hash+"&p=1&pagesize=10&kugouid=&clienttoken"
content = requests.get(url).text
return content
# 酷狗top500
@server.route('/top')
def top():
page = request.values.get('page')
if page is None or not page:
page = '1'
url = 'https://m.kugou.com/rank/info/?rankid=8888&page=' + page + '&json=true'
content = requests.get(url).text
return content
# 酷狗mv播放
@server.route('/mv')
def mv():
mvHash = request.values.get('hash')
k = int(time.time() * 1000)
key = "NVPh5oo715z5DIWAeQlhMDsWXXQV4hwtclienttime={0}clientver=20000cmd=100dfid=-ext=mp4hash={1}ismp3=0key=kugoumvcloudmid={0}pid=6srcappid=2919ssl=1uuid={0}NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt".format(
k, mvHash)
signature = kugou_signature(key)
url = "https://gateway.kugou.com/v2/interface/index?cmd=100&ext=mp4&hash={0}&ismp3=0&key=kugoumvcloud&pid=6&ssl=1&srcappid=2919&clientver=20000&clienttime={1}&mid={1}&uuid={1}&dfid=-&signature={2}".format(
mvHash, k, signature)
header = {
"x-router": "trackermv.kugou.com"
}
html = requests.get(url, headers=header)
encoding = 'utf-8'
html.encoding = encoding
return html.text
# 传入MV的加密串 返回signature值
def kugou_signature(o):
m = hashlib.md5()
m.update(o.encode("utf8"))
signature = m.hexdigest()
# 返回signature值
return signature.upper()
# 根据hash获取sqhash音质(油猴)
@server.route('/url/byhash')
def getSqMp3Url():
hash = request.values.get('hash')
albumid = request.values.get('albumid')
data = getMp3Datas(hash, albumid)
return data
# python3.*
if __name__ == '__main__':
server.run(host='0.0.0.0', port=5001)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# Vue悦听音乐
music.goodluckweb.top (opens new window) 直接访问后查看页面源代码,使用的vue.js
b站前端课程黑马程序员vue入门 (opens new window),该课程使用的网易云接口。
# 油猴酷狗插件
https://greasyfork.org/zh-CN/scripts/449833-酷狗在线听-下载 (opens new window)