0%

不务正业系列:白嫖网易云(偶尔更新)

项目分析

首先,感谢这位大佬的博客:(狗头保命)

『Python』网易云音乐API爬虫 Am0xil的博客-CSDN博客 网易云音乐搜索接口

网易云官方 API 接口地址:https://music.163.com

大佬使用的 API:https://music.163.com/weapi/cloudsearch/get/web?csrf_token=

  • 这个 API 是大佬用 F12 找的
  • 我也找了找,没有找到
  • 当时找到了“web?csrf_token”,但是没有发现歌曲的下载链接

这里我先简述一下服务器请求机制:(之前在CSapp中学到过,也当是复习了吧)

服务器请求

一般的请求消息如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
GET /home.html HTTP/1.0 <!-- 请求消息行 -->
Accept: */* <!-- 请求消息头 -->
Host: localhost:GET /home.html HTTP/1.0
Accept: */*
Host: localhost:GET /home.html HTTP/1.0
Accept: */*
Host: localhost:
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:10.0.3) Gecko/20120305 Firefox/10.0.3
Connection: close
Proxy-Connection: close

<html> <!-- 消息正文 -->
<head><title>test</title></head>
<body>
<img align="middle" src="godzilla.gif">
Dave O'Hallaron
</body>
</html>
  • 请求消息行:请求消息的第一行为请求消息行

    • 例如:GET /test/test.html HTTP/1.1
    • GET 为请求方式,请求方式分为:Get(默认)、POST、DELETE、HEAD等
      • GET:明文传输 不安全,数据量有限,不超过1kb
      • POST:暗文传输,安全,数据量没有限制
    • /test/test.html 为URI,统一资源标识符
    • HTTP/1.1 为协议版本
  • 请求消息头:从第二行开始到空白行统称为请求消息头(包含各种信息)

  • 消息正文:当请求方式是[POST]方式时,才能看见消息正文,消息正文就是要传输的一些数据,如果没有数据需要传输时,消息正文为空

服务器响应

一般的响应如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.0 200 OK <!-- 响应消息行 -->
Server: Tiny Web Server <!-- 响应消息头 -->
Content-length: 120
Content-type: text/html

<html> <!-- 响应正文 -->
<head><title>test</title></head>
<body>
<img align="middle" src="godzilla.gif">
Dave O'Hallaron
</body>
</html>
  • 响应消息行:第一行响应消息为响应消息行
    • 例如:HTTP/1.0 200 OK
    • HTTP/1.0 为协议版本
    • 200 为响应状态码,常用的响应状态码有40余种,这里我们仅列出几种,详细请看:
      • 200:一切正常
      • 302/307:临时重定向
      • 304:未修改,客户端可以从缓存中读取数据,无需从服务器读取
      • 404:服务器上不存在客户端所请求的资源
      • 500:服务器内部错误
    • OK 为状态码描述
  • 响应消息头:和请求消息头类似
  • 响应正文:即网页的源代码(F12可查看)

先看看大佬对请求的处理:

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
def get_music_list(params, encSecKey):
url = "https://music.163.com/weapi/cloudsearch/get/web?csrf_token="

payload = 'params=' + parse.quote(params) + '&encSecKey=' + parse.quote(encSecKey)
headers = {
'authority': 'music.163.com',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36',
'content-type': 'application/x-www-form-urlencoded',
'accept': '*/*',
'origin': 'https://music.163.com',
'sec-fetch-site': 'same-origin',
'sec-fetch-mode': 'cors',
'sec-fetch-dest': 'empty',
'referer': 'https://music.163.com/search/',
'accept-language': 'zh-CN,zh;q=0.9',
}
response = requests.request("POST", url, headers=headers, data=payload)
return response.text


# 通过歌曲的id获取播放链接
def get_reply(params, encSecKey):
url = "https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token="
payload = 'params=' + parse.quote(params) + '&encSecKey=' + parse.quote(encSecKey)
headers = {
'authority': 'music.163.com',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36',
'content-type': 'application/x-www-form-urlencoded',
'accept': '*/*',
'origin': 'https://music.163.com',
'sec-fetch-site': 'same-origin',
'sec-fetch-mode': 'cors',
'sec-fetch-dest': 'empty',
'referer': 'https://music.163.com/',
'accept-language': 'zh-CN,zh;q=0.9'
}
response = requests.request("POST", url, headers=headers, data=payload)
return response.text
  • requests.request(method,url,**kwargs) 的参数:
    • method:请求类型(这里是”POST”)
    • url:请求对象的链接(这里是目标 API)
    • headers:请求头,直接 F12 查看,上面有什么条目直接抄
      • PS:我 F12 看到的与作者代码中有不同的地方,但不影响结果
    • data:字典、字节序列或文件对象,作为 Requests 的内容
  • 最后返回服务器响应

大佬这两个函数就是实现了 “获取歌曲列表,获取歌曲播放链接” 的功能(就是 url 不同)

  • 在查找界面 F12 截下的图(这个API和大佬用的不一样,爬取的歌单少一点)
  • 在播放界面 F12 截下的图(不知道大佬是怎么断定它就是播放链接的)
  • 请求头部分截取(主要是 cookie 太长了~)

要说大佬最亮眼的地方,就是对网易云加密算法的破解了(“params”和“encSecKey”)

点开 Headers 后我们可以发现,除了传统的请求头参数外,本次请求还携带了一个 Form 表单,其中有两个参数,分别是 paramsencSecKey

具体来说,是用了 AES 和一些自创的加密算法,本人以前是搞逆向的,所以这些东西还是能应付,就是 JavaScript 有点恼火

搜索 encSecKey 我找到的就是这么一堆东西:

在网上找个格式化网站进行格式化:

搜索“encSecKey”:(最好不要搜索“params”)

1
2
3
4
5
var bKB0x = window.asrsea(JSON.stringify(i1x), buV7O(["流泪", "强"]), buV7O(Rg9X.md), buV7O(["爱心", "女孩", "惊恐", "大笑"]));
e1x.data = j1x.cr2x({
params: bKB0x.encText,
encSecKey: bKB0x.encSecKey
})
  • 把 window.asrsea 实例化为 bKB0x,然后调用其中的方法“encText”,“encSecKey”

搜索“asrsea”:

1
2
3
4
5
6
7
8
9
10
function d(d, e, f, g) {
var h = {},
i = a(16);
return h.encText = b(d, g),
h.encText = b(h.encText, i),
h.encSecKey = c(i, e, f),
h
}

window.asrsea = d, window.ecnonasr = e
  • 定义“encText”函数:
    • 利用“a(16)”获取“i”
    • 进行第一次“b”算法
    • 把“b”算法的结果和“i”作为参数,再进行一次“b”算法
  • 定义“encSecKey”函数:
    • 利用“a(16)”获取“i”
    • 进行一次“c”算法

“a”算法的实现:

1
2
3
4
5
6
function a(a) {
var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
c = "";
for (d = 0; a > d; d += 1) e = Math.random() * b.length, e = Math.floor(e), c += b.charAt(e);
return c
}
  • 从大小写英文字母以及10个数字中随机抽取16个字符拼接成一个新的字符串返回结果

“b”算法的实现:

1
2
3
4
5
6
7
8
9
10
function b(a, b) {
var c = CryptoJS.enc.Utf8.parse(b),
d = CryptoJS.enc.Utf8.parse("0102030405060708"),
e = CryptoJS.enc.Utf8.parse(a),
f = CryptoJS.AES.encrypt(e, c, {
iv: d,
mode: CryptoJS.mode.CBC
});
return f.toString()
}
  • 通过 CBC 模式进行 AES 加密,将传入的 a 参数和 b 参数分别作为需要加密的内容和密钥,iv偏移量为一个固定的字符串“0102030405060708”

“c”算法的实现:(前两个还好,这一串代码直接给我干沉默了…)

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
function c(a, b, c) {
var d, e;
return setMaxDigits(131),
d = new RSAKeyPair(b, "", c),
e = encryptedString(d, a)
}

function RSAKeyPair(a, b, c) {
this.e = biFromHex(a),
this.d = biFromHex(b),
this.m = biFromHex(c),
this.chunkSize = 2 * biHighIndex(this.m),
this.radix = 16,
this.barrett = new BarrettMu(this.m)
}

function encryptedString(a, b) {
for (var f, g, h, i, j, k, l, c = new Array, d = b.length, e = 0; d > e;) c[e] = b.charCodeAt(e), e++;
for (; 0 != c.length % a.chunkSize;) c[e++] = 0;
for (f = c.length, g = "", e = 0; f > e; e += a.chunkSize) {
for (j = new BigInt, h = 0, i = e; i < e + a.chunkSize; ++h) j.digits[h] = c[i++], j.digits[h] += c[i++] << 8;
k = a.barrett.powMod(j, a.e), l = 16 == a.radix ? biToHex(k) : biToString(k, a.radix), g += l + " "
}
return g.substring(0, g.length - 1)
}

最后提一下这些函数的参数:(由于我不会断点调试,这里就不展示过程了)

1
2
3
4
d:{"hlpretag":"<span class=\"s-fc7\">","hlposttag":"</span>","s":"Lily","type":"1","offset":"0","total":"true","limit":"30","csrf_token":""}
e:"010001"
f:"00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
g:"0CoJUm6Qyw8W8jud"
  • 参数 e,f,g 始终保持不表,由此可见它们是3个常量
  • 只是参数 d 中的歌曲名称在变化

理解完大佬的代码后,我在他的基础上进行了修改:

白嫖网易云_V1.0

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
import base64
import binascii
import json
import random
import string
from urllib import parse

import requests
import webbrowser
from Crypto.Cipher import AES

class Music():
def get_random(self):
random_str = ''.join(random.sample(string.ascii_letters + string.digits, 16))
return random_str

def len_change(self,text):
pad = 16 - len(text) % 16
text = text + pad * chr(pad)
text = text.encode("utf-8")
return text

def aes(self,text, key):
iv = b'0102030405060708'
text = self.len_change(text)
cipher = AES.new(key.encode(), AES.MODE_CBC, iv)
encrypted = cipher.encrypt(text)
encrypt = base64.b64encode(encrypted).decode()
return encrypt

def b(self,text, str):
first_data = self.aes(text, '0CoJUm6Qyw8W8jud')
second_data = self.aes(first_data, str)
return second_data

def c(self,text):
e = '010001'
f = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
text = text[::-1]
result = pow(int(binascii.hexlify(text.encode()), 16), int(e, 16), int(f, 16))
return format(result, 'x').zfill(131)

def get_final_param(self,text, str):
params = self.b(text, str)
encSecKey = self.c(str)
return {'params': params, 'encSecKey': encSecKey}

def get_music_list(self,params, encSecKey):
url = "https://music.163.com/weapi/cloudsearch/get/web?csrf_token="

payload = 'params=' + parse.quote(params) + '&encSecKey=' + parse.quote(encSecKey)
headers = {
'authority': 'music.163.com',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36',
'content-type': 'application/x-www-form-urlencoded',
'accept': '*/*',
'origin': 'https://music.163.com',
'sec-fetch-site': 'same-origin',
'sec-fetch-mode': 'cors',
'sec-fetch-dest': 'empty',
'referer': 'https://music.163.com/search/',
'accept-language': 'zh-CN,zh;q=0.9',
}
response = requests.request("POST", url, headers=headers, data=payload)
return response.text

def get_reply(self,params, encSecKey):
url = "https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token="
payload = 'params=' + parse.quote(params) + '&encSecKey=' + parse.quote(encSecKey)
headers = {
'authority': 'music.163.com',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36',
'content-type': 'application/x-www-form-urlencoded',
'accept': '*/*',
'origin': 'https://music.163.com',
'sec-fetch-site': 'same-origin',
'sec-fetch-mode': 'cors',
'sec-fetch-dest': 'empty',
'referer': 'https://music.163.com/',
'accept-language': 'zh-CN,zh;q=0.9'
}
response = requests.request("POST", url, headers=headers, data=payload)
return response.text

def choice(self,song_name_list,song_url_list,song_num):
num = eval(input("请输入播放编号:"))
if(num < song_num):
print("开始播放:"+song_name_list[num])
wb = webbrowser.get('windows-default')
wb.open(song_url_list[num])
else:
print("编号错误")

def start(self):
song_url_list = []
song_name_list = []
song_num = 0
song_search = input('请输入搜索目标,按回车键进行搜索:')
d = {"hlpretag": "<span class=\"s-fc7\">", "hlposttag": "</span>", "s": song_search, "type": "1", "offset": "0",
"total": "true", "limit": "30", "csrf_token": ""}
d = json.dumps(d)
random_param = self.get_random()
param = self.get_final_param(d, random_param)
song_list = self.get_music_list(param['params'], param['encSecKey'])
print('搜索结果如下:')
if len(song_list) > 0:
song_list = json.loads(song_list)['result']['songs']
for i, item in enumerate(song_list):
item = json.dumps(item)
song_name = json.loads(str(item))['name']
print(str(i) + ":" + song_name)
d = {"ids": "[" + str(json.loads(str(item))['id']) + "]", "level": "standard", "encodeType": "",
"csrf_token": ""}
d = json.dumps(d)
param = self.get_final_param(d, random_param)
song_info = self.get_reply(param['params'], param['encSecKey'])
if len(song_info) > 0:
song_info = json.loads(song_info)
song_url = json.dumps(song_info['data'][0]['url'], ensure_ascii=False)
song_name_list.append(song_name)
song_url_list.append(song_url)
else:
print("该首歌曲解析失败,可能是因为歌曲格式问题")
song_num = i+1
print("一共搜索到{}个目标".format(song_num))
self.choice(song_name_list,song_url_list,song_num)
else:
print("很抱歉,未能搜索到相关歌曲信息")

if __name__ == '__main__':
mus = Music()
mus.start()

效果展示:

PS:有些歌曲要 VIP,这个我可破解不了,当然也播放不了

更新日志:

  • version:v1.0

  • date:2022.5.19

  • type:

    • Features:NULL
    • Changed:NULL
    • Removed:NULL
  • desc:

    • 第一代版本,下一个版本打算加上 UI 界面,另外把 VIP 歌曲单独标记出来

白嫖网易云_V1.1

VIP 歌曲标记没有完成,但是 UI 界面做好了

  • PS:还是不会并发,所以爬虫工作的时候UI界面会卡顿
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
import base64
import binascii
import json
import random
import string
from urllib import parse

import requests
import webbrowser
from Crypto.Cipher import AES

import sys
import os
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

class Music():
def get_random(self):
random_str = ''.join(random.sample(string.ascii_letters + string.digits, 16))
return random_str

def len_change(self,text):
pad = 16 - len(text) % 16
text = text + pad * chr(pad)
text = text.encode("utf-8")
return text

def aes(self,text, key):
iv = b'0102030405060708'
text = self.len_change(text)
cipher = AES.new(key.encode(), AES.MODE_CBC, iv)
encrypted = cipher.encrypt(text)
encrypt = base64.b64encode(encrypted).decode()
return encrypt

def b(self,text, str):
first_data = self.aes(text, '0CoJUm6Qyw8W8jud')
second_data = self.aes(first_data, str)
return second_data

def c(self,text):
e = '010001'
f = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
text = text[::-1]
result = pow(int(binascii.hexlify(text.encode()), 16), int(e, 16), int(f, 16))
return format(result, 'x').zfill(131)

def get_final_param(self,text, str):
params = self.b(text, str)
encSecKey = self.c(str)
return {'params': params, 'encSecKey': encSecKey}

def get_music_list(self,params, encSecKey):
url = "https://music.163.com/weapi/cloudsearch/get/web?csrf_token="

payload = 'params=' + parse.quote(params) + '&encSecKey=' + parse.quote(encSecKey)
headers = {
'authority': 'music.163.com',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36',
'content-type': 'application/x-www-form-urlencoded',
'accept': '*/*',
'origin': 'https://music.163.com',
'sec-fetch-site': 'same-origin',
'sec-fetch-mode': 'cors',
'sec-fetch-dest': 'empty',
'referer': 'https://music.163.com/search/',
'accept-language': 'zh-CN,zh;q=0.9',
}
response = requests.request("POST", url, headers=headers, data=payload)
return response.text

def get_reply(self,params, encSecKey):
url = "https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token="
payload = 'params=' + parse.quote(params) + '&encSecKey=' + parse.quote(encSecKey)
headers = {
'authority': 'music.163.com',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36',
'content-type': 'application/x-www-form-urlencoded',
'accept': '*/*',
'origin': 'https://music.163.com',
'sec-fetch-site': 'same-origin',
'sec-fetch-mode': 'cors',
'sec-fetch-dest': 'empty',
'referer': 'https://music.163.com/',
'accept-language': 'zh-CN,zh;q=0.9'
}
response = requests.request("POST", url, headers=headers, data=payload)
return response.text

def start(self,song_search):
song_url_list = []
song_name_list = []
song_num = 0
d = {"hlpretag": "<span class=\"s-fc7\">", "hlposttag": "</span>", "s": song_search, "type": "1", "offset": "0",
"total": "true", "limit": "30", "csrf_token": ""}
d = json.dumps(d)
random_param = self.get_random()
param = self.get_final_param(d, random_param)
song_list = self.get_music_list(param['params'], param['encSecKey'])
print('搜索结果如下:')
if len(song_list) > 0:
song_list = json.loads(song_list)['result']['songs']
for i, item in enumerate(song_list):
item = json.dumps(item)
song_name = json.loads(str(item))['name']
print(str(i) + ":" + song_name)
d = {"ids": "[" + str(json.loads(str(item))['id']) + "]", "level": "standard", "encodeType": "",
"csrf_token": ""}
d = json.dumps(d)
param = self.get_final_param(d, random_param)
song_info = self.get_reply(param['params'], param['encSecKey'])
if len(song_info) > 0:
song_info = json.loads(song_info)
song_url = json.dumps(song_info['data'][0]['url'], ensure_ascii=False)
song_name_list.append(song_name)
song_url_list.append(song_url)
else:
print("该首歌曲解析失败,可能是因为歌曲格式问题")
song_num = i+1
print("一共搜索到{}个目标".format(song_num))
return song_name_list,song_url_list
else:
return None

class MainWin(QtWidgets.QWidget):
def __init__(self,parent=None):
super(MainWin, self).__init__(parent)
self.song_result = ""
self.song_num = 0
self.layout = QGridLayout()
self.setWindowFlags(Qt.SubWindow)
self.setupUI()

def searchMusic(self):
print('* searchMusic ')
self.song_name_list = []
self.song_url_list = []
self.song_num = 0
self.song_result = ""
self.song_search = self.searchBox.text()
if len(self.song_search) == 0:
self.resultText.setText("请先输入歌曲名称")
return
mus = Music()
self.song_name_list,self.song_url_list=mus.start(self.song_search)
for song_name in self.song_name_list:
self.song_num += 1
self.song_result+=(str(self.song_num)+"."+str(song_name)+"\n")
self.song_result=self.song_result[:-1]
self.resultText.setText(self.song_result)

def choiceMusic(self):
print('* choiceMusic ')
self.song_choice = self.choiceBox.text()
if len(self.song_choice) == 0:
self.resultText.setText("请先输入编号")
return
self.song_choice = eval(self.song_choice)
if len(self.song_result) == 0:
self.resultText.setText("请先搜索歌曲")
return
if self.song_choice > self.song_num:
self.resultText.setText("输入的编号有误")
return
self.resultText.setText("开始播放:{}".format(self.song_name_list[self.song_choice-1]))
wb = webbrowser.get('windows-default')
wb.open(self.song_url_list[self.song_choice-1])

def quitWindows(self):
print('* quitWindows ')
self.close()

def setupUI(self):
self.resize(550,450)
self.groupBox = QtWidgets.QGroupBox(self)
self.groupBox.setGeometry(QtCore.QRect(10,10,530,430))
self.groupBox.setObjectName("groupBox")
self.groupBox.setStyleSheet("color:white")
self.searchBox = QtWidgets.QLineEdit(self.groupBox)
self.searchBox.setGeometry(QtCore.QRect(105,25,160,20))
self.searchBox.setObjectName("searchBox")
self.searchBox.setStyleSheet("color:black")
self.choiceBox = QtWidgets.QLineEdit(self.groupBox)
self.choiceBox.setGeometry(QtCore.QRect(105,315,160,20))
self.choiceBox.setObjectName("choiceBox")
self.choiceBox.setStyleSheet("color:black")
self.resultText = QtWidgets.QTextEdit(self.groupBox)
self.resultText.setGeometry(QtCore.QRect(20,60,260,240))
self.resultText.setObjectName("resultText")
self.resultText.setFont(QFont("微软雅黑",10,QFont.Bold))
self.resultText.setStyleSheet("color:black")
self.label = QtWidgets.QLabel(self.groupBox)
self.label.setGeometry(QtCore.QRect(20,20,80,30))
self.label.setObjectName("laber")
self.label2 = QtWidgets.QLabel(self.groupBox)
self.label2.setGeometry(QtCore.QRect(15,20,80,610))
self.label2.setObjectName("laber2")
self.searchButton = QtWidgets.QPushButton(self)
self.searchButton.setGeometry(QtCore.QRect(50,380,90,30))
self.searchButton.setObjectName("searchButton")
self.choiceButton = QtWidgets.QPushButton(self)
self.choiceButton.setGeometry(QtCore.QRect(160,380,90,30))
self.choiceButton.setObjectName("choiceButton")
self.quitButton = QtWidgets.QPushButton(self)
self.quitButton.setGeometry(QtCore.QRect(270,380,90,30))
self.quitButton.setObjectName("quitButton")

self.retranslateUI()
self.searchButton.clicked.connect(self.searchMusic)
self.choiceButton.clicked.connect(self.choiceMusic)
self.quitButton.clicked.connect(self.quitWindows)

QtCore.QMetaObject.connectSlotsByName(self)

def retranslateUI(self):
self.setWindowTitle("网易云API")
self.groupBox.setTitle("输入搜索的目标")
self.label.setText("歌曲&歌手")
self.label2.setText("请输入编号")
self.searchButton.setText("搜索")
self.choiceButton.setText("播放")
self.quitButton.setText("退出")

if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
win = MainWin()
palette = QPalette()
palette.setBrush(QPalette.Background, QBrush(QPixmap("D:\PythonProject\images\YHellow.png")))
win.setPalette(palette)
win.show()
sys.exit(app.exec_())

更新日志:

  • version:v1.1

  • date:2022.5.20

  • type:

    • Features:
      • 全新的 UI 界面
    • Changed:NULL
    • Removed:NULL
  • desc:

    • 本项目已相对完善,暂时不会更新了