首页 > 其他分享 >爬虫实战---爬取音乐

爬虫实战---爬取音乐

时间:2023-02-26 00:55:06浏览次数:54  
标签:爬取 self 爬虫 目录 --- ui tk os frame

爬虫实战---爬取音乐


前言:

此方法来源于网络:https://www.bilibili.com/video/BV1g8411G7ZC?p=1, 仅供个人学习

模块:

模块
os
tkinter
webbrowser
requests
PySimpleGUI
retrying
from tkinter import ttk
import tkinter.messagebox

每个模块功能介绍:

1.os模块:

1.简介:os 模块是 Python 内置的与操作系统中的文件系统相关的模块,该模块依赖于操作系统。通常情况下,如不特别指出,该模块提供的方法、属性在Windows 和 UNIX(Linux 和Mac OS X) 系统上都是可用的。dir(os)即可获取所有方法列表

2.常用方法:

  • os.getcwd() : 获取当前工作目录的路径,在 Python 中,指定文件路径时需要对路径分隔符 \ 进行转义,即将路径中的 \ 替换为 \\ 。注:在指定文件路径时,也可以在表示路径的字符串前面加上字母 r(R),那么该字符串将原样输出,这时路径中的分隔符就不需要再转义了。

    path = os.getcwd
    
  • **os.path() ** :

    • os.path.abspath() : 获取当前工作文件的绝对路径
    • os.path.basename() : 获取文件名,不包含目录部分, 如果该路径本身就是个目录,那么返回的就是空
    • os.path.dirname() : 获取文件的目录部分, dirname + basename 就可以构成文件的完整路径名
    • os.path.exists() : 判断路径是否存在,这里的路径包括目录和文件。
    • os.path.getsize() : 获取文件大小,也可以获取目录(所有文件的)大小,取决你你传的路径参数是文件还是目录。单位是字节
    • os.path.split() : split 方法会将路径切割成两部分,以最后一个斜杠作为切割点,第一部分是文件所在的目录, 第二部分文件名 本身。
    • os.path.join() : oin是与split对应的方法,用于拼接文件路径。
    • os.path.isfile() : 判断路径是否为文件,如果该文件不存在也不会报错,而是直接返回False
  • **os.listdir() **: 返回指定路径下的目录名和文件名(不包括子孙目录) ,是一个列表,输出结果列表是以字母顺序排列,不区分文件和目录。

    • 参数:
      • path:表示一个路径,不能是文件路径。
  • os.makedirs():采用递归方法创建多级目录

    • 参数:
      • name:用于指定要创建目录的路径
      • mode:指定目录的模式,默认模式为八进制的 777。
      • exist_ok:可选参数,如果值为 False,当要创建的目录已经存在时,抛出 FileExistsError 异常;如果值为True,当要创建的目录已经存在时,不会抛出异常。默认值为 False。
      • 返回值:无。
    os.makedirs(r'xi\xixi\xixixi') //将创建三级目录,xi为一级
    
  • os.removedirs():递归删除目录

    • 参数:
      • name:用于指定要删除目录的路径。该目录中,最后一级目录必须为空,否则将抛出 OSError:[WinError 145] 目录不是空的 异常。
      • 无返回值
  • os.rmdir():删除空目录

    • 参数:
    • path:表示要删除的目录,可以使用相对路径,也可以使用绝对路径。
    • dir_fd :可选参数,用于指定要删除目录的相对于目录描述符的路径,而不是遵循符号链接(软链接)。
    • 返回值:无。
  • rename():重命名文件或目录,在方法中,如果出现 \* 则表示其后面的参数为命名关键字参数,这是一个特殊的分隔符。在调用时,命名关键字参数必须传入参数名。

    • 参数:
      • src :表示字符串类型的路径,用于指定需要进行重命名的文件或目录的路径。
      • dst:表示字符串类型的路径,用于指定新的文件或目录名称。如果指定的路径已经存在,则多数系统下将抛FileExistsError 异常。
      • src_dir_fd :可选参数,用于指定源地址的相对于目录描述符的路径,而不是遵循符号链接(软链接)。
      • dst_dir_fd :可选参数,用于指定目标地址的相对于目录描述符的路径,而不是遵循符号链接(软链接)。
      • 返回值:无

参考自https://blog.csdn.net/xw1680/article/details/125563034,更多方法详见。

2.tkinter模块:

参考1:https://blog.csdn.net/xw1680/article/details/118088846?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167730993416800182718786%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=167730993416800182718786&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_ecpm_v1~rank_v31_ecpm-1-118088846-null-null.blog_rank_default&utm_term=tkinter&spm=1018.2226.3001.4450

参考2:https://blog.csdn.net/SGDBS233/article/details/125113252

3.webbrowser模块:

参考:https://www.bookstack.cn/read/python-3.10.0-zh/632b2333e26cd2d3.md#id1

4.requests模块:

参考:https://www.runoob.com/python3/python-requests.html

https://www.cnblogs.com/simono/p/16629306.html

5.PySimpleGUI模块:

参考:https://blog.csdn.net/W295723987/article/details/126611877

https://www.cnblogs.com/L707/p/16340905.html

6.retrying模块:

参考:http://www.manongjc.com/detail/39-wkiwvutawajzimj.html

7.tkinter子模块ttk:

参考:https://blog.csdn.net/pythonitstream/article/details/124358774 (上)

https://blog.csdn.net/pythonitstream/article/details/124508042 (下)

8.tkinter子模块messagebox

参考:https://www.jb51.net/article/216076.htm

https://blog.csdn.net/qq_34745941/article/details/116995050


代码:

import os #关于操作系统的一个python标准库
import tkinter as tk #gui图形界面化模块
import webbrowser #提供了一个高级接口,允许向用户显示基于Web的文档。
import requests
import tkinter.messagebox as mes_box
import PySimpleGUI as sg
from tkinter import ttk
from retrying import retry #对同一个操作进行多次尝试

class SetUI(object):
    """
    音乐弹框界面
    """
    def __init__(self,weight=1000,height=600):
        self.ui_weight=weight
        self.ui_height=height
        self.title="音乐破解软件"
        self.ui_root=tk.Tk(className=self.title) #建立主窗口并设置窗口名
        self.ui_url = tk.StringVar() #StringVar()跟踪变量值的变化,把最新的值显示到界面上
        self.ui_var = tk.IntVar()#记录数值.get()获取值, .set()设置值
        self.ui_var.set(1)#1为选中,0未选中
        self.show_result = None
        self.song_num = None
        self.response_data = None
        self.song_url = None
        self.song_name = None
        self.song_author = None

    def set_ui(self):
        """
        设置简易ui界面
        :return:
        """
        #Frame空间
        frame_1 = tk.Frame(self.ui_root)#可作为其他组件的容器,常用来对组件进行分组Label 标 签,常用来显示单行文本
        frame_2 = tk.Frame(self.ui_root)
        frame_3 = tk.Frame(self.ui_root)
        frame_4 = tk.Frame(self.ui_root)

        #ui界面中菜单设计
        ui_menu = tk.Menu(self.ui_root)  #创建菜单栏
        #显示菜单
        self.ui_root.config(menu=ui_menu)
        #创建子菜单
        file_menu = tk.Menu(ui_menu,tearoff = 0)#Tearoff  默认情况下(tearoff=1 或 True)显示“撕掉元素(‘------------’)
        ui_menu.add_cascade(label='菜单',menu = file_menu)#添加子菜单,设置子菜单名称
        #添加子菜单选项触发单元,command设置触发事件
        file_menu.add_command(label='使用说明',command=lambda:webbrowser.open('www.baidu.com'))
        file_menu.add_command(label='关于作者',command=lambda:webbrowser.open('www.baidu.com'))
        file_menu.add_command(label='退出',command=self.ui_root.quit)#退出程序

        #控件内容设置
        choice_passageway = tk.Label(frame_1,text = '请选择音乐搜索通道',padx=10,pady=10) #Label:标签组件。主要用																				#于显示文本,添加提示信息等。
        passageway_button1 = tk.Radiobutton(frame_1,text='酷我', variable=self.ui_var,value=1,
                                            width=10,height=3)#单选
        passageway_button2 = tk.Radiobutton(frame_1, text='网易云', variable=self.ui_var, value=2,
                                            width=10, height=3)
        passageway_button3 = tk.Radiobutton(frame_1, text='QQ音乐', variable=self.ui_var, value=3,
                                            width=10, height=3)
        passageway_button4 = tk.Radiobutton(frame_1, text='酷狗', variable=self.ui_var, value=4,
                                            width=10, height=3)
        input_link = tk.Label(frame_2,text="请输入歌曲名或歌手")
        #文本框Entry用于接收输入的数据。文本框Entry的基本格式为:txt = tkinter.Entry(容器名称,width=宽度,文字字体、颜色等)
        entry_style = tk.Entry(frame_2,textvariable=self.ui_url,highlightcolor='Fuchsia',
                               highlightthickness=1,width=35)
        label2 = tk.Label(frame_2,text=" ")
        play_button = tk.Button(frame_2,text='搜索',font=('楷体',11),fg='Purple',width=2,height=1,
                                command=self.get_KuWoMusic)
        label3= tk.Label(frame_2,text=" ")
        #表格样式
        columns=("序号","歌手","歌曲","专辑")
        #设置表格,headings表示显示在顶部,colums是要显示的数据
        self.show_result = ttk.Treeview(frame_3,height=20,show="headings",columns= columns)
        #下载
        download_button =  tk.Button(frame_4,text="下载",font=('楷体',11),fg='Purple',width=6,
                                     height=1,padx=5,pady=5,command=self.download_music)

        #控件布局
        frame_1.pack()
        frame_2.pack()
        frame_3.pack()
        frame_4.pack()
        choice_passageway.grid(row=0,column=0)
        passageway_button1.grid(row=0,column=1)
        passageway_button2.grid(row=0,column=2)
        passageway_button3.grid(row=0,column=3)
        passageway_button4.grid(row=0,column=4)
        input_link.grid(row=0,column=0)
        entry_style.grid(row=0,column=0)
        label2.grid(row=0,column=2)
        play_button.grid(row=0,column=3,ipadx=10,ipady=10)
        label3.grid(row=0,column=4)
        self.show_result.grid(row=0,column=4)
        download_button.grid(row=0,column=4)

        #设置表头
        self.show_result.heading("序号",text="序号")
        self.show_result.heading("歌曲",text="歌曲")
        self.show_result.heading("歌手",text="歌手")
        self.show_result.heading("专辑",text="专辑")
        #设置列
        self.show_result.column("序号",width=100,anchor='center')
        self.show_result.column("歌曲",width=200,anchor='center')
        self.show_result.column("歌手",width=300,anchor='center')
        self.show_result.column("专辑",width=400,anchor='center')

        #鼠标点击
        self.show_result.bind('<ButtonRelease-1>',self.get_song_url)#1代表左键,n为2代表中键,n为3代表右键

    @retry(stop_max_attempt_number=5)#停止最大尝试次数
    def get_KuWoMusic(self):
        """
        获取QQ音乐
        :return:
        """
        #清空treeview表格数据
        for item in self.show_result.get_children():#get_children()函数,其返回的是treeview中的记录号.
            self.show_result.delete(item)
        headers = {  # 传入必要请求头,反爬--------------------------------------------------------
            "Accept": "application/json, text/plain, */*",
            "Accept-Encoding": "gzip, deflate",
            "Accept-Language": "zh-CN,zh;q=0.9",
            "Connection": "keep-alive",
            "Cookie": "_ga=GA1.2.2058800930.1642474837; _gid=GA1.2.1174281146.1642474837; Hm_lvt_cdb524f42f0ce19b169a8071123a4797=1642490265,1642490289,1642490402,1642498818; Hm_lpvt_cdb524f42f0ce19b169a8071123a4797=1642499234; kw_token=YK4UZW2UPL",
            "csrf": "YK4UZW2UPL",
            "Host": "www.kuwo.cn",
            "Referer": "http://www.kuwo.cn/",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36",
        }
        search_input = self.ui_url.get()#得到用户输入内容

        if len(search_input) >0:
            search_url = 'http://www.kuwo.cn/api/www/search/searchMusicBykeyWord?'
            """key携带的值,是我们要搜索的值;pn,按照常量,我们猜测,其为页数;rn,我们猜测其为一页显示的数量"""
            search_data = {
                'key':search_input,
                'pn':'1',
                'rn': '80',
                'httpsStatus':'1',
                'reqId':'858597c1-b18e-11ec-83e4-9d53d2ff08ff'
            }
            try:# params 接收一个字典或者字符串的查询参数,字典类型自动转换为url编码,不需要urlencode()
                self.response_data = requests.get(search_url,params=search_data,headers=headers,
                                                  timeout=20).json()
                songs_data = self.response_data['data']['list']
                if int(self.response_data['data']['total'])<=0:
                    mes_box.showerror(title='错误',message='搜索:{} 不存在.'.format(search_input))
                else:
                    for i in range(len(songs_data)):
                        self.show_result.insert('',i,values=(i+1,songs_data[i]['artist'],
                                                             songs_data[i]['name'],
                                                             songs_data[i]['album']))
                    """insert(parent, index, iid=None, **kw)
                        解释:
                        parent : 对于有树栏的Treeview,parent是父节点,对于只是列表栏的Treeview,parent一般为空。
                        index :插入位置。可以是END或’end’ ,也可以是数字的,如果你想新插入的item(记录)成为第某节点的第一									个,index就设为0,以此类推。
                        iid : 每一行记录(item)的标识符,这个参数,我上面没有讲解,等下,我会专门讲解。
                        **kw :设置插入的记录(item)所支持属性,"""
            except TimeoutError:
                mes_box.showerror(title='错误',message='搜索超时,请重新输入再搜索!')
        else:
            mes_box.showerror(title='错误',message='未输入需查询的歌曲或歌手,请输入后搜索!')

    def get_song_url(self ,event):
        """
        获取下载歌曲的地址
        :param event:
        :return:
        """
        #treeview中的单击左键
        for item in self.show_result.selection():#取的选项,要下载的歌曲
            item_text = self.show_result.item(item,"value")#item获得字典·
            #获取
            self.song_num=int(item_text[0])
        #获取下载歌曲的地址
        if self.song_num is not None:
            songs_data = self.response_data['data']['list']
            songs_req_id = self.response_data['reqId']
            songs_rid = songs_data[self.song_num-1]['rid']
            music_url = 'http://www.kuwo.cn/api/v1/www/music/playUrl?mid={}&type=convert_url3'\
                        '&httpsStatus=1&reqId={}'.format(songs_rid,songs_req_id)
            response_data = requests.get(music_url).json()
            self.song_url = response_data['data'].get('url')
            self.song_name = songs_data[self.song_num-1]['name']
            self.song_author = songs_data[self.song_num-1]['artist']
        else:
            mes_box.showerror(title='错误',message='未选择要下载的歌曲,请选择')

    def download_music(self):
        """
        下载音乐
        :return:
        """
        self.progress_bar(100)
        if not os.path.exists('./music'):
            os.mkdir("./music/")
        if self.song_num is not None:
            song_name = self.song_name+'--'+self.song_author+".mp3"
            try:
                save_path = os.path.join('./music/{}'.format(song_name))\
                    .replace('\\','/')
                true_path = os.path.abspath(save_path)
                resp= requests.get(self.song_url)
                with open(save_path,'wb') as file:
                    file.write(resp.content)
                    mes_box.showinfo(title='下载成功',message='歌曲:%s,保存地址为%s'%(self.song_name,
                                                                                      true_path))
            except Exception:
                mes_box.showerror(title='错误',message='未找到存放歌曲的文件夹')
        else:
            mes_box.showerror(title='错误',message='未选择要下载的歌曲,请选择后下载')

    def progress_bar(self,file_size):
        """
        任务加载进度条
        :param file_size:
        :return:
        """
        #orientation 表示 进度条是横向的或是纵向的。h横向 v纵向(默认)
        #size是窗口尺寸
        layout = [[sg.Text('任务完成进度')],
                  [sg.ProgressBar(file_size,orientation='h',size=(40,20),key='progressbar')],
                  [sg.Cancel()]]#取消按钮

        #window只需将自定义的布局加载出来即可,第一个参数是窗口标题
        window=sg.Window('机器人执行进度',layout)
        #根据key值获取到进度条
        _progress_bar = window['progressbar']
        for i in range(file_size):# read读取窗口,返回两个值,一个是事件,一个是值
            event,value = window.read(timeout=10)
            if event == 'Cancel' or event is None:
                break
            _progress_bar.UpdateBar(i+1)#更新进度条

    def ui_center(self):
        """
        UI界面窗口设置:居中
        :return:
        """
        ws=self.ui_root.winfo_screenwidth()#获取电脑屏幕的
        hs=self.ui_root.winfo_screenheight()
        x=int((ws/2)-(self.ui_weight/2))
        y=int((hs/2)-(self.ui_height/2))
        self.ui_root.geometry('{}x{}+{}+{}'.format(self.ui_weight,self.ui_height,x,y))
        #geometry设定主窗口的大小以及位置,当参数值为 None 时表示获取窗口的大小和位置信息。

    def loop(self):
        """
        函数说明:loop等待用户事件
        :return:
        """
        self.ui_root.resizable(False,False) #禁止修改窗口大小
        self.ui_center() #窗口居中
        self.set_ui()
        self.ui_root.mainloop() #进入等待与处理窗口事件

if __name__=='__main__':
    a=SetUI()#定义一个实例
    a.loop()#调用函数

标签:爬取,self,爬虫,目录,---,ui,tk,os,frame
From: https://www.cnblogs.com/xiaopixiong/p/spider-1.html

相关文章