python实现酷狗音乐下载,以及利用tk界面可视化

思路分析:从酷狗主页http://www.kugou.com/,搜索框输入搜索歌曲后,发现请求网页变成

http://www.kugou.com/yy/html/search.html#searchType=song&searchKeyWord=blue格式,keyword字段就是搜索歌曲名的值,然后在该页面检索出各歌曲的结果。寻找到歌曲链接后,在进入到界面播放改首首歌,设法寻找播放链接。

用到的库:

requests:用于请求网页,

lxml:解析获取到的网页,进行检索需要的字段

urllib:下载远程链接,下载mp3源文件

json:解析返回数据

tkinter:编写界面

1 )获取搜索界面的歌曲信息:(以关键字 blue为例)

      初次尝试时,我直接爬取搜索页面的网页,

url='http://www.kugou.com/yy/html/search.html#searchType=song&searchKeyWord=blue'
res=requests.get(url,headers=headers)

但输出res的内容却是空,然后在network中点开各个请求,查看他们的response。发现这样一个网站

http://songsearch.kugou.com/song_search_v2?callback=jQuery112407470964083509348_1534929985284&keyword=blue&page=1&pagesize=30&userid=-1&clientver=&platform=WebFilter&tag=em&filter=2&iscorrection=1&privilege_filter=0&_=1534929985286

python实现酷狗音乐下载,以及利用tk界面可视化

   该页面的回应内容如下:因此可以猜测这个链接就是实际搜索结果的请求链接 keyword是搜索关键字,page是结果的第几页,pagesize是一页显示多少首搜索结果。

python实现酷狗音乐下载,以及利用tk界面可视化

然后测试结果:该页面确实返回搜索界面的歌曲信息,可以使用json库进行解析返回数据。需要用到的歌曲名Filename,FileHash(后续下载时发现会用到)

python实现酷狗音乐下载,以及利用tk界面可视化

搜寻到歌曲信息的代码如下:

res = requests.get(url).text #获取的网页内容
js = json.loads(res[res.index('(') + 1:-2]) #只留下{}中的内容
data = js['data']['lists'] #歌曲信息全在lists中
for i in range(len(data)):
    name = str(data[i]['FileName']).replace('<em>', '').replace('</em>', '')
    fhash = str(data[i]['FileHash'])
    mp3_info[name]=fhash #字典存放 歌名-hash字段

2) 单首歌曲下载链接:

单曲播放界面如下:http://www.kugou.com/song/#hash=D9D954883F2CAD22B1F428D774B6CCE1&album_id=8246933

 可以看到该url中hash字段,应该是每首歌的特有标志字段,album_id是所属专辑id。当然直接get该网页,回复内容仍旧是空,需要从另外的地方寻找。

在network中发现了一个链接,返回该歌曲的详细信息和下载链接。

python实现酷狗音乐下载,以及利用tk界面可视化

play_url字段就是歌曲的下载链接。请求的网页如下,hash= 后面就是歌曲的hash标志

http://wwwapi.kugou.com/yy/index.php?r=play/getdata&callback=jQuery19103400224052340737_1537711829955&hash=

获取play_url的代码如下,类似之前的获取歌曲信息,之后使用urlretrieve下载即可

url='http://wwwapi.kugou.com/yy/index.phpr=play/getdata&callback=jQuery19103400224052340737_1537711829955&hash=' + mp3_hash  #mp3_hash就是第一步里面获得hash字段
html = requests.get(url, headers=header2).text
real_url = json.loads(html[html.index('(') + 1:-2])["data"]["play_url"] #真实下载地址
urlretrieve(real_url, path) #path是文件存放路径
 

3)完整代码如下:

界面设计使用的简单的tk,由于刚接触这方面,只用了很简陋的几个功能,结合之前的步骤,将搜索和下载函数绑定到按钮,在listbox中显示搜索结果,然后获取鼠标选中行数的内容,进行下载(提前存放所有结果的信息,包括歌名和下载链接)

# coding = utf-8
from tkinter import *
import tkinter.messagebox
import requests
from lxml import etree
from urllib.request import urlretrieve
from tkinter.filedialog import askdirectory
import json

head={
    'Host': 'songsearch.kugou.com',
    'Origin': 'http://www.kugou.com/',
    'Referer': 'http://www.kugou.com/',
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36'
}

header2={
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36'
}

mp3_info = {} #全局变量,存放歌曲名和hash
#选择文件路径的函数
def selectPath():
    path_ = askdirectory()
    path.set(path_)

def help_info():
    tkinter.messagebox._show('v0.0.1帮助', '输入下载的歌曲名.单曲搜索结果选中某行后再进行下载,重新搜索记得清空列表\n暂不支持:\n歌手搜索(歌手搜索会有结果,但不能下载,因为是酷狗音源,懒得去写)\n歌单搜索(其实是网易云搜索字段还没有看懂原理)\n新手第一个小程序,如有bug,那就不管了')

def cleartxt():
    text.delete(0,END)

def show():
    text.delete(0,END)
    song = entry.get()  #获得歌曲名
    url = "http://songsearch.kugou.com/song_search_v2?callback=jQuery112407470964083509348_1534929985284&keyword={}&" \
          "page=1&pagesize=30&userid=-1&clientver=&platform=WebFilter&tag=em&filter=2&iscorrection=1&privilege_filte" \
          "r=0&_=1534929985286".format(song)
    res = requests.get(url).text  # 收到的数据 type(res)是个str 把不必要地方去掉 因为loads方法的字符串应该形如字典{}
    # print(res.index('('))  将res 的{}中间部分提取出来
    js = json.loads(res[res.index('(') + 1:-2])
    data = js['data']['lists']  # 这是一个列表


    for i in range(len(data)):
        text.insert(END, ">>>" + str(data[i]['FileName']).replace('<em>', '').replace('</em>', ''))
        text.see(END)
        text.update()
        name = str(data[i]['FileName']).replace('<em>', '').replace('</em>', '')
        fhash = str(data[i]['FileHash'])
        mp3_info[name]=fhash

def download():
    if not entry_path.get():
        tkinter.messagebox._show('错误', '没有选中文件夹路径')
        return
    pre_path = entry_path.get()
    content=entry.get()
    if content:
        num=text.curselection()[0] #结果是一个一维元组如(5,)
        if num != None:#选择的是num首歌,对应的data[num] ,listbox下标从0开始
            mp3_name=text.get(num)[3:] #因为前三个符号是>>>用于提示,剔除后才是真正的歌名
            mp3_hash=mp3_info.get(mp3_name)  #hash码
            url = 'http://wwwapi.kugou.com/yy/index.php?r=play/getdata&callback=jQuery19103400224052340737_1537711829955&hash=' + mp3_hash
            html = requests.get(url, headers=header2).text
            real_url = json.loads(html[html.index('(') + 1:-2])["data"]["play_url"]
            print(real_url)
            try:
                path = pre_path  + '/' + mp3_name +'.mp3'
                if '\\' in path :
                    path=path.replace('\\', '/')
                urlretrieve(real_url, path)
                tkinter.messagebox._show('提示','下载成功')
                return
            except Exception as e:
                print("写入文件失败,原因:", e)
                return

root = Tk()
path = StringVar()
root.title("一个十分简陋的音乐下载器v0.0.1")
root.geometry("600x400+550+230")

Label(root, text="酷狗单曲", font=('Consolas', 15)).grid(row=0, column=0)
Button(root, text="搜索", relief = 'ridge',font=("Consolas", 15), command=show).grid(row=0, column=2)

entry = Entry(root, font=('Consolas', 15))
entry.grid(row=0, column=1)


Label(root, text="文件存放路径", font=('Consolas', 15)).grid(row=2, column=0)
#存放路径的输入栏
entry_path = Entry(root, textvariable = path,font=('Consolas', 15))
entry_path.grid(row=2, column=1)

Button(root, text="选择路径", relief = 'ridge',font=("Consolas", 15), command=selectPath).grid(row=2, column=2)#,sticky=E)

text = Listbox(root,selectmode = BROWSE,font=("Consolas", 15), width=45, height=10)
text.grid(row=3, columnspan=2)
Button(root, text="清空列表", relief = 'ridge',font=("Consolas", 15), command=cleartxt).grid(row=3, column=2,sticky=S)

#下载和退出按钮
btn_down=Button(root, text="开始下载",relief = 'ridge',font=("Consolas", 15), command=download).grid(row=4, column=0, sticky=W)
Button(root, text="退出", relief = 'ridge',font=("Consolas", 15), command=root.quit).grid(row=4, column=1, sticky=E)
Button(root, text="帮助", relief = 'ridge',font=("Consolas", 15), command=help_info).grid(row=4, column=2, sticky=E)
root.mainloop()

 

程序运行截图:

python实现酷狗音乐下载,以及利用tk界面可视化

 

大佬勿喷,菜鸟刚接触python。虽然程序简单,但通过这个例子,还是掌握了一点爬虫的基本思路,在直接爬取网页获得不了想要的结果时,很多时候要从network中观察,找到实际的请求界面,或者采用的get还是post方法,以及需要的字段等。