python爬虫篇5——万能音乐下载器

采用tkinter可视化GUI编写的界面,感兴趣的自行查找相关资料学习。

python代码:

import asyncio
import os
import re
import time

import threading
import winreg
from tkinter import *
from urllib import parse
import requests
from lxml import etree


# 127万能音乐下载器

class Music:
    def __init__(self):
        self.init_Menu()

    def init_Menu(self):
        self.top = Tk()
        self.top.geometry("400x400")
        self.top.title("Author:xuhao  QQ:916295795  Email:[email protected]")
        self.show_main_frame()

    def show_main_frame(self):
        self.frame = Frame(self.top)
        self.label = Label(self.frame,
                           text='万能音乐下载器V2.0')
        self.label.pack()
        # 设置数据库地址
        self.windowlabel = Label(self.frame,
                                 text='消息栏')
        self.windowlabel.pack()
        self.cwd = StringVar(self.frame)
        self.cwd.set("快输入歌曲信息叭!(*^▽^*)")
        # frame容器
        self.dirfm = Frame(self.frame)
        # 下滑框
        self.dirsb = Scrollbar(self.dirfm)
        self.dirsb.pack(side=RIGHT, fill=Y)
        self.v = StringVar()
        self.main = Listbox(self.dirfm, listvariable=self.v, height=15,
                            width=50, yscrollcommand=self.dirsb.set)
        self.dirsb.config(command=self.main.yview)
        self.main.pack(side=LEFT, fill=BOTH)
        self.dirfm.pack()
        # 装载了StringVar
        self.dirn = Entry(self.frame, width=50,
                          textvariable=self.cwd)
        self.dirn.pack()
        # 又是一个容器
        self.bfm = Frame(self.frame)
        self.getdata_btn = Button(self.bfm, text='查找歌曲',
                                  command=self.getmusicdata,
                                  activeforeground='white',
                                  activebackground='blue')
        self.download_btn = Button(self.bfm, text='下载歌曲',
                                   command=self.selectdownloaddata,
                                   activeforeground='white',
                                   activebackground='blue')
        self.clear = Button(self.bfm, text='清空输入',
                            command=self.clearInput,
                            activeforeground='white',
                            activebackground='green')
        self.openfiledir = Button(self.bfm, text='歌曲位置',
                                  command=self.openfd,
                                  activeforeground='white',
                                  activebackground='green')
        self.quit = Button(self.bfm, text='退出程序',
                           command=self.top.quit,
                           activeforeground='white',
                           activebackground='red')
        self.getdata_btn.pack(side=LEFT)
        self.download_btn.pack(side=LEFT)
        self.clear.pack(side=LEFT)
        self.openfiledir.pack(side=LEFT)
        self.quit.pack(side=LEFT)
        self.bfm.pack()
        self.frame.pack()
        self.data = []
        self.music_path = get_desktop() + '\music\\'
        self.insert('☺' * 40)
        self.insert('欢迎使用127music音乐下载软件!')
        self.insert('☺' * 40)
        self.insert('使用步骤:')
        self.insert('1.输入歌曲信息')
        self.insert('2.点击“查找歌曲”')
        self.insert('3.在消息栏中选中歌曲,点击“下载歌曲”开始下载')
        self.insert('4.下载成功,歌曲自动保存到桌面上的music文件夹中')
        self.insert('5.支持选择多首歌曲同时下载')
        self.insert('6.支持断点续传,网络中断后再次下载继承原下载进度')
        self.insert('☺' * 40)

    def openfd(self):
        os.system("start explorer %s" % self.music_path)

    def insert(self, str):
        self.data.append(str)
        self.v.set(self.data)
        # 在最底部更新
        self.main.see(END)

    def deleteAll(self):
        self.data = []
        self.v.set(self.data)

    def refresh(self, str, point):
        # 删除全部内容,使用delete指定第一个索引值0和最后一个参数END,即可
        # self.main.delete(0,END)
        self.data[point] = str
        self.v.set(self.data)
        # 在最底部更新
        # self.main.see(END)

    def selectdownloaddata(self):
        try:
            if len(self.namelist):
                if not re.search(r'\d+', self.main.get(ACTIVE)[0:3]).group().isdigit():
                    self.insert("选择有误呐!o(╥﹏╥)o")
                else:
                    name = self.namelist[int(re.search(r'\d+', self.main.get(ACTIVE)).group()) - 1]
                    url = self.urllist[int(re.search(r'\d+', self.main.get(ACTIVE)).group()) - 1]
                    try:
                        if self.music_path not in os.listdir():
                            os.makedirs(self.music_path)
                        if not self.data.__contains__("开始下载=>>>>%s" % name.replace(self.music_path, "")):
                            self.insert("开始下载=>>>>%s" % name.replace(self.music_path, ""))
                            self.insert('')
                            self.insert('')
                            thread_it(self.download, name, url, len(self.data) - 1)
                        else:
                            self.insert('任务已存在队列中')
                    except FileExistsError:
                        if not self.data.__contains__("开始下载=>>>>%s" % name.replace(self.music_path, "")):
                            self.insert("开始下载=>>>>%s" % name.replace(self.music_path, ""))
                            self.insert('')
                            self.insert('')
                            thread_it(self.download, name, url, len(self.data) - 1)
                        else:
                            self.insert('任务已存在队列中')
            else:
                self.insert('请确定选中歌曲喔!o(╥﹏╥)o')
        except AttributeError:
            self.insert('请确定选中歌曲喔!o(╥﹏╥)o')

    def init_music(self):
        self.base_url = "http://qcc.flash127.com/sou/"
        self.search_url = self.base_url + parse.quote(self.cwd.get()) + ".html"
        self.download_url = "http://qcc.flash127.com/public/x.html?id="
        self.result_list = []
        self.deleteAll()
        self.search_music()

    def getmusicdata(self):
        if len(self.cwd.get()):
            self.init_music()
        else:
            self.insert('输入不能为空呐!o(╥﹏╥)o')

    # 清空输入
    def clearInput(self):
        self.cwd.set("")

    def search_music(self):
        headers = {
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
            "Accept-Encoding": "gzip, deflate",
            "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
            "Cache - Control": "max - age = 0",
            "Connection": "keep-alive",
            "Cookie": "security_session_verify=834a600229f490fc3016d5915f78a48b; PHPSESSID=0842n462448oq7lf6h6m2v7074; _state=1; qun=4; Hm_lvt_756d7a4b826c7cb664be2d218d441885=1571908857; Hm_lpvt_756d7a4b826c7cb664be2d218d441885=1571910142",
            "CSRFCOOKIE": "9D55488F828C0C4B8924A1E5BF41CE02710021DD",
            "Host": "qcc.flash127.com",
            "Referer": self.search_url,
            "Upgrade-Insecure-Requests": "1",
            "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0",
        }
        try:
            respose = requests.get(self.search_url, headers=headers)
        except Exception:
            respose = requests.get(self.search_url, headers=headers)
        # print(respose.text)
        # print(self.search_url)
        result_tip = etree.HTML(respose.text).xpath('//a[@class="moren"]/text()')[0]
        self.insert(result_tip)
        music_data = etree.HTML(respose.text).xpath('//div[@class="bk_lists"]//li')
        for music_item in music_data:
            name = music_item.xpath('.//a[@class="m_t"]/@title')[0] + ".mp3"
            url = self.download_url + re.findall(r'http:.*?play/(.*?).html',
                                                 music_item.xpath('.//a[@class="m_t"]/@href')[0])[0]
            item = {"music_name": name, "music_url": url}
            self.result_list.append(item)
        else:
            self.namelist = []
            self.urllist = []
            for item in self.result_list:
                # respose = requests.get(item["music_url"])
                # if respose.text != '404!':
                self.namelist.append(self.music_path + item["music_name"])
                self.urllist.append(item["music_url"])
            for i in range(len(self.namelist)):
                self.insert(str(i + 1) + "." + self.namelist[i].replace(self.music_path, ""))
            else:
                if len(self.namelist):
                    self.insert("请选中下载的歌曲")
                else:
                    self.insert("未搜索到相关结果哟!(ಥ﹏ಥ)")

    def download(self, name, url, point):
        headers = {
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
            "Accept-Encoding": "gzip, deflate",
            "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
            "Connection": "keep-alive",
            "Cookie": "security_session_verify=212445fef54d2c1beb70138e5d8e382c; Hm_lvt_756d7a4b826c7cb664be2d218d441885=1571908857,1571968691; PHPSESSID=0c2cb1ts7csj2735cung76q8c0; _state=1; qun=4; Hm_lpvt_756d7a4b826c7cb664be2d218d441885=1571969281",
            "CSRFCOOKIE": "9D55488F828C0C4B8924A1E5BF41CE02710021DD",
            "Host": "qcc.flash127.com",
            "Upgrade-Insecure-Requests": "1",
            "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0",
        }
        response = requests.get(url, headers=headers)
        # 读取本地已下载文件大小
        if os.path.exists(name):
            finishlength = os.path.getsize(name)  # 本地已经下载的文件大小
        else:
            finishlength = 0
        newheaders = {
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
            "Accept-Encoding": "gzip, deflate",
            "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
            "Connection": "keep-alive",
            "Host": "data.flash127.com",
            "Upgrade-Insecure-Requests": "1",
            "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0"
        }
        # 第一次请求获取文件总长度
        newresponse = requests.get(response.url, stream=True, headers=newheaders)
        chunksize = 1024
        # 每次写入文件的大小
        readsize = 1024 * 32
        # 获取完整文件大小,单位MB
        totallength = int(newresponse.headers['Content-Length'])
        filesize = float('%.2f' % (totallength / (chunksize ** 2)))
        # 实现断点续传
        newheaders['Range'] = 'bytes=%d-' % finishlength
        # 添加了新的header,第二次请求下载文件从finishlength开始读取文件
        newresponse = requests.get(response.url, stream=True, headers=newheaders)
        # 统计下载时间
        time_start = time.time()  # 开始计时
        # "ab"表示追加形式写入文件
        with open(name, 'ab') as f:
            download_size = 0
            download_rate = 0
            download_starttime = time.time()
            for data in newresponse.iter_content(chunk_size=readsize):
                f.write(data)
                # 刷新文件
                f.flush()
                finishlength += len(data)
                now_jd = float('%.2f' % (finishlength / totallength)) * 100
                fin_num = int(finishlength / (1024 * 256))
                # fin_str = '▮' * int(finishlength / (1024 * 256))
                tot_str = '▯' * int(totallength / (1024 * 256))
                if time.time() - download_starttime > 2:
                    download_rate = int((((finishlength - download_size) / chunksize) / 2))
                    download_size = finishlength
                    download_starttime = time.time()
                if download_rate > 0:
                    self.refresh("大小:%sMB,下载速度:%sKB/s,预计下载时间:%s秒" % (filesize,
                                                                     download_rate, int(
                        ((totallength - finishlength) / chunksize) / download_rate)),
                                 point - 1)
                else:
                    self.refresh("大小:%sMB,下载速度:%sKB/s,预计下载时间:计算中..." % (filesize, download_rate),
                                 point - 1)
                self.refresh(
                    "下载进度:%s %d%%" % (
                        tot_str.replace(tot_str[0:fin_num], tot_str[0:fin_num].replace('▯', '▮'), 1),
                        now_jd,
                    ), point)
                time_end = time.time()  # 结束计时
            f.close()
            time_c = time_end - time_start
            self.insert("%s下载完成!(✿◡‿◡)实际下载时间%s秒" % (name.replace(self.music_path, ""), int(time_c)))


def thread_it(func, *args):
    '''将函数打包进线程'''
    # 创建
    t = threading.Thread(target=func, args=args)
    # 守护 !!!
    t.setDaemon(True)
    # 启动
    t.start()
    # 阻塞--卡死界面!
    # t.join()


# loops = asyncio.get_event_loop()
# loops.run_until_complete(main(loops, namelist, urllsit))
# 异步下载文件
# async def download(session, name, url):
#     file = await session.get(url)
#     filecode = await file.read()
#     with open(name, 'wb') as f:
#         # 写入文件
#         f.write(filecode)
#     return str(url)
#
#
# async def main(loop, NAME, URL):
#     async  with aiohttp.ClientSession() as session:
#         # 建立会话session
#         try:
#             tasks = [loop.create_task(download(session, NAME[_], URL[_])) for _ in range(len(URL))]
#             # 建立所有任务
#             finished, unfinished = await asyncio.wait(tasks)
#             # 触发await,等待任务完成
#             all_results = [r.result() for r in finished]
#             # 获取所有结果
#             self.insert("下载结果:" + str(all_results))
#         except aiohttp.ClientResponseError as s:
#             return s


# 获取桌面路径

def get_desktop():
    key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders')
    return winreg.QueryValueEx(key, "Desktop")[0]


def main():
    Music()
    mainloop()


if __name__ == '__main__':
    main()

程序可能存在部分bug,欢迎交流指正。

你可能感兴趣的:(python)