Python利用网易云音乐接口搭建的音乐推荐,根据单曲歌名推荐相关用户喜爱的歌曲

转载自:https://blog.csdn.net/seaisgod/article/details/76340442

一、网易云音乐的相关接口

这边我想要的数据接口有:

* 网易的搜索功能,根据歌名获取歌曲的id

* 歌曲相关的评论用户接口

* 用户的相关数据包括歌单或听歌记录,这边听歌记录的接口好像不能用,所以我就用的歌单接口

关于每个接口大家可以自己F12网易官网看看是长什么样子,网易的新接口加密方式我也是在网上找的资料。

这边就是接口部分的代码:

import requests
import json
import os
import base64
import binascii
from Crypto.Cipher import AES
class NetEaseAPI:
    def __init__(self):
        self.header = {
            '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 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537.36'  # NOQA
        }
        self.cookies = {'appver': '1.5.2'}
        self.playlist_class_dict = {}
        self.session = requests.Session()
    def httpRequest(self, method, action, query=None, urlencoded=None, callback=None, timeout=None):
        connection = json.loads(self.rawHttpRequest(method, action, query, urlencoded, callback, timeout))
        return connection
    def rawHttpRequest(self, method, action, query=None, urlencoded=None, callback=None, timeout=None):
        if method == 'GET':
            url = action if query is None else action + '?' + query
            connection = self.session.get(url)
        elif method == 'POST':
            connection = self.session.post(action, query, self.header)
        elif method == 'Login_POST':
            connection = self.session.post(action,  query, self.header)
            self.session.cookies.save()
        connection.encoding = 'UTF-8'
        return connection.text
    def search(self, s, stype=1, offset=0, total='true', limit=1):
        action = 'http://music.163.com/api/search/get'
        data = {
            's': s,
            'type': stype,
            'offset': offset,
            'total': total,
            'limit': limit
        }
        return self.httpRequest('POST', action, data)
    def aesEncrypt(self, text, secKey):
        pad = 16 - len(text) % 16
        text = text + chr(pad) * pad
        encryptor = AES.new(secKey, 2, '0102030405060708')
        ciphertext = encryptor.encrypt(text)
        ciphertext = base64.b64encode(ciphertext).decode('utf-8')
        return ciphertext
    def rsaEncrypt(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 createSecretKey(self,size):
        return (''.join(map(lambda xx: (hex(ord(xx))[2:]), os.urandom(size))))[0:16]
    def encrypted_request(self, text):
        modulus = ('00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7'
           'b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280'
           '104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932'
           '575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b'
           '3ece0462db0a22b8e7')
        nonce = '0CoJUm6Qyw8W8jud'
        pubKey = '010001'
        text = json.dumps(text)
        secKey = binascii.hexlify(os.urandom(16))[:16]
        encText = self.aesEncrypt(self.aesEncrypt(text, nonce), secKey)
        encSecKey = self.rsaEncrypt(secKey, pubKey, modulus)
        data = {'params': encText, 'encSecKey': encSecKey}
        return data
    def getComment(self, songId, offset=0, total='fasle', limit=100):
        action = 'http://music.163.com/api/v1/resource/comments/R_SO_4_{}/?rid=R_SO_4_{}&\
        offset={}&total={}&limit={}'.format(songId, songId, offset, total, limit)
        comments = self.httpRequest('GET', action)
        return comments['hotComments']
    def getPlaylist(self, uid):
        text = {
            'uid': uid,
            'limit':100
        }
        text = json.dumps(text)
        nonce = '0CoJUm6Qyw8W8jud'
        pubKey = '010001'
        modulus = ('00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7'
           'b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280'
           '104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932'
           '575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b'
           '3ece0462db0a22b8e7')
        secKey = self.createSecretKey(16)
        encText = self.aesEncrypt(self.aesEncrypt(text, nonce), secKey)
        encSecKey = self.rsaEncrypt(secKey, pubKey, modulus)
        data = {
            'params': encText,
            'encSecKey': encSecKey
        }
        action = 'http://music.163.com/weapi/user/playlist?csrf_token='
        playlist = self.httpRequest('POST', action, data)
        res = list()
        for play in playlist['playlist']:
            res.append({'id':play['id'],'subscribedCount':play['subscribedCount'],'playCount':play['playCount']})
        return res
    def getPlaylistDetail(self, id):
        text = {
            'id': id,
            'limit':100,
            'total':True
        }
        text = json.dumps(text)
        nonce = '0CoJUm6Qyw8W8jud'
        pubKey = '010001'
        modulus = ('00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7'
           'b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280'
           '104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932'
           '575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b'
           '3ece0462db0a22b8e7')
        secKey = self.createSecretKey(16)
        encText = self.aesEncrypt(self.aesEncrypt(text, nonce), secKey)
        encSecKey = self.rsaEncrypt(secKey, pubKey, modulus)
        data = {
            'params': encText,
            'encSecKey': encSecKey
        }
        action = 'http://music.163.com/weapi/v3/playlist/detail?csrf_token='
        playlistDetail = self.httpRequest('POST', action, data)
        music = list()
        musicCount = dict()
        for count in playlistDetail['playlist']['trackIds']:
            musicCount[count['id']] = count['v']
        for detail in playlistDetail['playlist']['tracks']:
            singer = ''
            for author in detail['ar']:
                singer += author['name']+','
            music.append({'id':detail['id'],'name':detail['name'],'singer':singer, 'playCount':musicCount[detail['id']]})
        return music

二、推荐的逻辑

这边我获取这首歌的所有热门评论用户以及他们的歌单,在获取所有歌单的数据,将歌单中的所有歌曲根据歌单的听取次数、订阅人数、歌曲的听取次数等等进行加权评分,奖所有的歌曲按照分数高低选取其中前30首进行推荐

下面就是评分的代码:

from __future__ import division
import time
from NetEaseAPI import *
class musicRecom():
    def getSongId(self,musicTitle):  
        res = NetEaseAPI().search(musicTitle)
        return res['result']['songs'][0]['id']
    def musicRank(self,musicDict):
        maxSubCount = 0
        maxLsitCount = 0
        maxMusicCount = 0
        for music in musicDict:
            if musicDict[music]['listSubscribedCount'] > maxSubCount :
                maxSubCount = musicDict[music]['listSubscribedCount'] 
            if musicDict[music]['listCount'] > maxLsitCount :
                maxLsitCount = musicDict[music]['listCount']
            if musicDict[music]['musicPlayCount'] > maxMusicCount :
                maxMusicCount = musicDict[music]['musicPlayCount']
        for music in musicDict:
            musicDict[music]['score'] = musicDict[music]['listSubscribedCount'] / maxSubCount + musicDict[music]['listCount'] / maxLsitCount + musicDict[music]['musicPlayCount'] / maxMusicCount
        return sorted(musicDict.items(), key = lambda d:d[1]['score'], reverse = True)

三、界面

为了可以方便输入某首歌名从而进行推荐,我写了一个简单的操作界面,tkinter库,比较丑,这个库不怎么会用,没有怎么写过界面

界面以及事件代码:

from __future__ import division
from Tkinter import *
import Tkinter
import ttk
from musicRecom import *
import threading
class GUI(Frame):
    def __init__(self, master = None):
        Frame.__init__(self, master)
        self.pack()
        self.create()
    def progress(self,count1,count2):
        self.len1.set(count1)
        self.len2.set(count2)
    def search(self):
        musicTitle = self.musicTitle.get()
        if musicTitle == '':
            print "Please input the music title!"
        else:
            self.len1 = StringVar()
            self.len2 = StringVar()
            self.scale1 = Scale(self,from_ = 0,
                    to = 100, 
                    resolution = 0.1, 
                    orient = HORIZONTAL,
                    variable = self.len1,
                    length = 500
                ).grid(row = 2,column=0, columnspan=4, padx = 5)
            self.scale2 = Scale(self,from_ = 0,
                    to = 100, 
                    resolution = 0.1, 
                    orient = HORIZONTAL,
                    variable = self.len2,
                    length = 500
                ).grid(row = 3,column=0, columnspan=4, padx = 5)
            threading.Thread(target = self.musicRecom).start()
    def musicRecom(self):
        self.searchButton.destroy()
        musicTitle = self.musicTitle.get()
        res = dict()
        songId = musicRecom().getSongId(musicTitle)
        time.sleep(1)
        comment = NetEaseAPI().getComment(songId)
        time.sleep(1)
        count1 = 0
        for user in comment:
            count1 = count1 + 1
            uid = user['user']['userId']
            playlist = NetEaseAPI().getPlaylist(uid)
            time.sleep(1)
            count2 = 0
            for table in playlist:
                count2 = count2 + 1
                musicDetail = NetEaseAPI().getPlaylistDetail(table['id'])
                time.sleep(1)
                self.progress(count1/len(comment)*100,count2/len(playlist)*100)
                for music in musicDetail:
                    res[music['id']]={'id':music['id'],'name':music['name'],'singer':music['singer'],'musicPlayCount':int(music['playCount']),'listCount':int(table['playCount']),'listSubscribedCount':int(table['subscribedCount'])}
        self.res = musicRecom().musicRank(res)
        self.maxPage = 4
        self.page = 1
        self.nextButton = Button(self, text='Pre', command=self.Pre)
        self.nextButton.grid(row = 4,column = 1, padx=5, pady=5)
        self.nextButton = Button(self, text='Next', command=self.Next)
        self.nextButton.grid(row = 4,column = 2, padx=5, pady=5)
        self.frame = Frame(self)
        self.frame.grid(row = 5,columnspan=4)
        self.getCont()
    def getCont(self):
        index = 1
        num = 0
        self.frame.destroy()
        self.frame = Frame(self)
        self.frame.grid(row = 5,columnspan=4)
        for item in self.res:
            num = num + 1
            if num > self.page * 15:
                break
            if num <= self.page * 15 and num > (self.page - 1) * 15:                
                Label(self.frame, text=index + (self.page - 1) * 15).grid(row = index + 4,column=0)
                Label(self.frame, text=item[1]['name'].encode('utf8')).grid(row = index + 4,column=1)
                Label(self.frame, text=item[1]['id']).grid(row = index + 4,column=2)
                Label(self.frame, text=item[1]['singer'].encode('utf8')).grid(row = index + 4,column=3)
                index = index + 1
    def Next(self):
        if self.page < self.maxPage:
            self.page = self.page + 1
        else:
            self.page = 1
        self.getCont()
    def Pre(self):
        if self.page > 1:
            self.page = self.page - 1
        else:
            self.page = self.maxPage
        self.getCont()
    def create(self):
        self.labelName = Label(self, text = "Please input the music name:")
        self.labelName.grid(row = 0, column = 0)
        self.musicTitle = StringVar()
        self.inputName = Entry(self, textvariable = self.musicTitle, width=50)
        self.inputName.grid(row = 0, column = 1, columnspan=3, padx=5, pady=5)
        self.searchButton = Button(self, text='Search', command=self.search)
        self.searchButton.grid(row = 1,column = 1, padx=5, pady=5)

四、主调度

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
 
# from Crawler import *
import sys
from GUI import *
default_encoding = 'utf-8'
if sys.getdefaultencoding() != default_encoding:
    reload(sys)
    sys.setdefaultencoding(default_encoding)
 
def main():
    root = Tkinter.Tk()
    app = GUI(root)
    root.geometry('640x560')
    root.resizable(False, False)
    app.master.title('网易云音乐的歌曲推荐')
    app.mainloop()
 
 
 
if __name__ == "__main__":
    main()

五、测试结果

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值