使用tkinter+爬虫实现网易云音乐下载器

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+爬虫实现网易云音乐下载器。

发布了73 篇原创文章 · 获赞 47 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/weixin_43870646/article/details/104609873