Tkinter 是 Python 的标准 GUI 库。Python 使用 Tkinter 可以快速的创建 GUI 应用程序。
由于 Tkinter 是内置到 python 的安装包中、只要安装好 Python 之后就能 import Tkinter 库、而且 IDLE 也是用 Tkinter 编写而成、对于简单的图形界面 Tkinter 还是能应付自如。
使用Tkinter+爬虫完成网易云音乐下载器,简单分为两个步骤:界面—功能
首先我们在完成Tkinter界面
一、界面布局
#创建界面
root = Tk()
#标题
root.title("网易云音乐下载器")
#窗口大小
root.geometry('550x400')
#标签控件
label = Label(root,text='请输入歌曲名称:',font=("华文行楷",20))
#标签定位
label.grid()
#输入框
entry = Entry(root,font=("隶书",20))
entry.grid(row=0,column=1)
#列表框
text = Listbox(root,font=("楷书",16),width=50,heigh=15)
text.grid(row=1,columnspan=2)
#开始按钮
button = Button(root,text="开始下载",font=("隶书",15))
button.grid(row=2,column=0,sticky=W)
#退出按钮
button1 = Button(root,text="退出程序",font=("隶书",15))
button1.grid(row=2,column=1,sticky=E)
#显示界面
root.mainloop()
通过以上代码,可以得到一个简洁的操作界面,但是还为添加功能,如下
二、功能
现在我们需要做的就是姜爬虫的程序添加到界面的按键中,实现功能。
我们需要下载音乐的平台是网易云音乐网
通过检查网页源代码,查看搜索和下载的网址,我们可以找到网页的信息是在这个url里面
https://music.163.com/weapi/cloudsearch/get/web?csrf_token=e87cd24a6de0c3c3b28727eaa8f2727b
这个url需要通过post请求才能得到数据,其中post请求的加密参数是
params,encSecKey,我们需要解密这两个参数才能拿到数据。
通过不断的调试,找到相对应的函数代码块,在通过python的方式还原加密方式,
import requests
import random
import base64
from Crypto.Cipher import AES
import json
import binascii
class Music_api():
# 设置从JS文件提取的RSA的模数、协商的AES对称密钥、RSA的公钥等重要信息
def __init__(self):
self.modulus = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
self.nonce = '0CoJUm6Qyw8W8jud'
self.pubKey = '010001'
self.url = "https://music.163.com/weapi/cloudsearch/get/web?csrf_token="
self.HEADER = {}
self.setHeader()
self.secKey = self.getRandom()
# 生成16字节即256位的随机数
def getRandom(self):
string = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
res = ""
for i in range(16):
res += string[int(random.random() * 62)]
return res
# AES加密,用seckey对text加密
def aesEncrypt(self, text, secKey):
pad = 16 - len(text) % 16
text = text + pad * chr(pad)
encryptor = AES.new(secKey.encode('utf-8'), 2, '0102030405060708'.encode('utf-8'))
ciphertext = encryptor.encrypt(text.encode('utf-8'))
ciphertext = base64.b64encode(ciphertext).decode("utf-8")
return ciphertext
# 快速模幂运算,求 x^y mod mo
def quickpow(self, x, y, mo):
res = 1
while y:
if y & 1:
res = res * x % mo
y = y // 2
x = x * x % mo
return res
# rsa加密
def rsaEncrypt(self, text, pubKey, modulus):
text = text[::-1]
a = int(binascii.hexlify(str.encode(text)), 16)
b = int(pubKey, 16)
c = int(modulus, 16)
rs = self.quickpow(a, b, c)
return format(rs, 'x').zfill(256)
# 设置请求头
def setHeader(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': 'https://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'
}
# 设置相应的请求参数,从而搜索列表
# 总体的密码加密步骤为:
# 首先用nonce对text加密生成密文1
# 然后用随机数seckey加密密文1生成密文2
# 随后,用公钥加密seckey生成密文3
# 其中,密文2作为请求参数中的params,密文3作为encSeckey字段
# 这样,接收方可以通过私钥解密密文3获得seckey(随机数)
# 然后用seckey解密密文2获得密文1
# 最终用统一协商的密钥nonce解密密文1最终获得text
def search(self, s, offset, type="1"):
text = {"hlpretag": "<span class=\"s-fc7\">",
"hlposttag": "</span>",
"#/discover": "",
"s": s,
"type": type,
"offset": offset,
"total": "true",
"limit": "30",
"csrf_token": ""}
text = json.dumps(text)
params = self.aesEncrypt(self.aesEncrypt(text, self.nonce), self.secKey)
encSecKey = self.rsaEncrypt(self.secKey, self.pubKey, self.modulus)
data = {
'params': params,
'encSecKey': encSecKey
}
result = requests.post(url=self.url,
data=data,
headers=self.HEADER).json()
return result
# 获取指定音乐列表(相当于主函数)
def get_music_list(self, keywords):
music_list = []
for offset in range(1):
result = Music_api().search(keywords, str(offset))
result = result['result']['songs']
for music in result:
# if music['copyright'] == 1 and music['fee'] == 8:
if (music['privilege']['fee'] == 0 or music['privilege']['payed']) and music['privilege']['pl'] > 0 and \
music['privilege']['dl'] == 0:
continue
if music['privilege']['dl'] == 0 and music['privilege']['pl'] == 0:
continue
# if music['fee'] == 8:
music_list.append(music)
return music_list
# print(Music_api().get_music_list("山海"))#测试
数据是没有问题的,值得注意的是,
python 在 Windows下使用AES时要安装的是pycryptodome 模块 pip install pycryptodome
python 在 Linux下使用AES时要安装的是pycrypto模块 pip install pycrypto
在导入from Crypto.Cipher import AES时,会出现报错,将crypto的c小写改成大写C。
三、结合功能和界面
通过在按钮中添加参数command,引用函数(注意不要加(),只需要函数名)
#下载歌曲
def download_song(item):
name = item['name']
id = item['id']
url = "http://music.163.com/song/media/outer/url?id={}.mp3".format(id)
os.makedirs("music",exist_ok=True)
path = "music\{}.mp3".format(name)
#文本框
text.insert(END,"歌曲:{},正在下载......".format(name))
#文本框滚动
text.see(END)
#更新
text.update()
# 下载
urlretrieve(url, path)
#文本框
text.insert(END,"下载完毕,{},请试听......".format(name))
#文本框滚动
text.see(END)
#更新
text.update()
#搜索歌曲名称
def get_music_name():
name = entry.get() #获取输入的歌曲名称
music_list_id = Music_api().get_music_list(name)
name = music_list_id[0]['name']
id = music_list_id[0]['id']
item = {}
item['name'] = name
item['id'] = id
download_song(item)
通过from urllib.request import urlretrieve,一行代码实现下载mp3,剩去了with open的冗余代码,
通过os模块创建文件夹,保存文件
exist_ok=True表示创建文件夹时,没有就创建,有则不报错也部覆盖创建。
至此,功能和界面就完成了,看看效果
再看看文件夹中是否存在
ok,以上就完成了使用Tkinter+爬虫实现网易云音乐下载器。