import json, winreg, os, requests
from utils import Encryptor, Song
# 网易的这个加密器是公共加密器,所有的携带params、encSecKey参数的post请求都使用该加密器进行加密。
# 通过设置断点,不断的查看参数i8a,获得多组字典,然后逐个测试字典,最终获取与url相匹配的字典
class Downloader(object):
# 经过断点测试,发现在搜索时,传入到加密器的i8a参数有3组,分别是以下的dict1/dict2/dict3
dict1 = {
'csrf_token': '',
'logs': '[{"action":"searchkeywordclient","json":{"type":"song","keyword":"谢谢你的爱","offset":0}}]',
}
dict2 = {
'csrf_token': '',
'hlposttag': '</span>',
'hlpretag': '<span class="s-fc7">',
'limit': '30',
'offset': '0',
's': '谢谢你的爱',
'total': 'true',
'type': '1',
}
dict3 = {
'csrf_token': '',
's': '谢谢你的爱',
}
dict4 = {
'csrf_token': '',
'br': '128000',
'ids': '[1297493260]',
} # 获取歌曲url
def __init__(self):
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.headers1 = {
'Origin': 'https://music.163.com',
'Range': 'bytes=0-',
'Referer': 'https://music.163.com',
'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.headers2 = {
}
self.start_url = "https://music.163.com/"
self.session = requests.session()
self.response = self.session.get(url=self.start_url, headers=self.headers)
self.post_url1 = 'https://music.163.com/weapi/search/suggest/web?csrf_token='
# url1+dict2有正确的返回,但是只有4首曲目。
# url1+dict1参数错误。
# url1+dict3看起来和dict2差不多。。。
self.post_url2 = 'https://music.163.com/weapi/cloudsearch/get/web?csrf_token='
# url2+dict2可以返回一页的30首歌曲。这个才是我需要的。
# url2+dict1参数错误。
# url2+dict3参数错误。
self.post_url3 = 'https://music.163.com/weapi/search/suggest/multimatch?csrf_token='
# url3+dict2虽有有返回,但只有1首,排除
# url3+dict1参数错误。
# url3+dict3看起来和dict2差不多。。。
self.post_url4 = 'https://music.163.com/weapi/song/enhance/player/url?csrf_token=' # 这个就是获取歌曲url的
def _search_songs(self, keyword):
dict2 = {
'csrf_token': '',
'hlposttag': '</span>',
'hlpretag': '<span class="s-fc7">',
'limit': '30',
'offset': '0',
's': keyword,
'total': 'true',
'type': '1',
}
data = Encryptor.d(dict2)
response = self.session.post(self.post_url2, headers=self.headers, data=data)
return response.text
def search(self, keyword):
text = self._search_songs(keyword)
obj = json.loads(text)
song_list = []
for index, i in enumerate(obj.get('result').get("songs")):
id = i.get('id')
name = i.get('name')
ar = i.get('ar')
list2 = [i.get('name') for i in ar]
singer = "_".join(list2)
song_list.append(Song(id=id, name=name, singer=singer, index=index))
return song_list
def _song_url(self, song):
dict4 = {
'csrf_token': '',
'br': '128000',
'ids': '[%s]' % song.id,
}
data = Encryptor.d(dict4)
response = self.session.post(self.post_url4, headers=self.headers, data=data)
return response.text
def song(self, song):
text = self._song_url(song)
list1 = [i.get('url') for i in json.loads(text).get('data')]
song.url_list = list1
def download(self, song):
for index, i in enumerate(song.url_list):
if len(song.url_list) == 1:
name = "%s\\%s_%s.mp3" % (self.folder_path, song.name, song.singer)
with open(name, 'wb') as f:
response = requests.get(i, headers=self.headers1)
f.write(response.content)
else:
name = "%s\\%s_%s_%s.mp3" % (self.folder_path, song.name, song.singer, index)
with open(name, 'wb') as f:
response = requests.get(i, headers=self.headers1)
f.write(response.content)
def mkdir(self):
reg_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders', ) # 利用系统的链表
desk_path = winreg.QueryValueEx(reg_key, "Desktop")[0] # 获取桌面路径
self.folder_path = os.path.join(desk_path, "网易云音乐") #
try:
os.mkdir(self.folder_path)
except:
pass
def main():
song_downloader = Downloader()
song_downloader.mkdir()
print('欢迎使用网易云下载器')
command = None
while command != 'q':
keywords = input('搜索歌曲请输入关键字,按“q”退出:')
if keywords == "q":
break
try:
song_list = song_downloader.search(keywords)
except:
print('发生错误,请检查网络')
continue
for i in song_list:
print("%s %s %s" % (i.index, i.name, i.singer))
if int(i.index) > 10:
break
while True:
command = input('请根据歌曲编号进行下载,按“q”退出,按“s”重新搜索')
if command.strip() == 'q' or command == 's':
break
elif command.strip() in [str(i.index) for i in song_list]:
song_downloader.song(song_list[int(command)])
song_downloader.download(song_list[int(command)])
print('正在下载歌曲%s %s' % (song_list[int(command)].name, song_list[int(command)].singer))
else:
print('输入错误,请重新输入。')
if __name__ == "__main__":
main()