用Tkinter包装一个音乐下载爬虫

前言

(萌新小程序,高手勿喷)
近两天一不小心找到了个不错的音乐下载网站,嘿嘿嘿嘿…
正巧刚学完web,于是就想写个GUI小程序来练练手。

ps:没想到tkinter做的界面也蛮好看的,果然,好不好看和工具没关系,主要还是在于艺术设想与颜色搭配上(虽然tkinter样式确实有些少)
(本程序 api 接口来自 ‘懒人音乐’。)

一、模块

核心模块:

requests
作用:提交搜索信息,爬取音乐信息
(你以为我会写tkinter吗?呵,不,这400多行的程序核心就一个十几行的小爬虫)

辅助模块:

tkinter
tkinter.ttk
tkinter.filedialog
tkinter.messagebox
作用:GUI包装
(没错,除了十几行的小爬虫,剩下的400多行全在画框架…
而我在写框架时才发现,原来css是如此神圣、如此便捷!!!相比而言tkinter真是麻烦多了。
同时,基于写HTML时的习惯(div啊,div啊,div…),我在写tkinter时,同样将框架空间分开成了header、article、footer 三个单独函数来写,结果导致搜索与显示部分有些连接不上,显得有些死板。
下次用pyqt5来试试)

路人模块:

time
作用:记录日志时间等

二、程序

1.思路区块介绍

ui框架剖析图
如图,其中每个红色框为一个大函数区块,而每个小蓝框是大区块中的一个小功能区块。下面将分开对每部分区块功能及代码进行展示。

1)header 部分

搜索框:
header部分核心内容。
由 enter 与 button 组件构成。其中enter用于输入需搜索信息,而button用于绑定spider函数来爬取enter中搜索的内容并将返回的数据存储到内存中。

提示框:
emm…说实话,这部分是中间加的,一开始并没打算有,不过嘛,你不觉得加上后还蛮好看的吗?
咳咳,前面也说了,由于习惯简洁方便(当时确实是这么想的,绝对不是因为,为各组件取一长串不同的带特点的名字是一件非常麻烦的事,恩恩,不是的),各组件分开写在不同函数中,导致不同函数间组件不能方便地直接调用,使得操作有些不连续,因而在header最顶端创建了一个label组件,并在其中用一个对象属性作为 tk.StringVar() 变量,以方便随时修改,用作下一步操作的或出错情况下的提示。

logo:
至于搜索框左边部分,咳咳,这就厉害了,这里是全篇代码的中级核心,他显示了它是谁家的孩子(没错,它父亲就是我)。只不过我没有加图片就是了

2)article 部分

搜索站点选择框(左上角):
基于Listbox组件,用于选择你搜寻的基站。
同样,为了在不同函数间传递信息,这里用了一个对象属性来接收选定的值。(为了方便选择还创建了一个右键小菜单)

搜索站点选择框
储存地址选择框(左下角):
这一部分是我写article时最后写的一部分。我后悔了,真的好麻烦,因为我才发现Text组件竟然不支持 textvariable…换成enter?空间不够,也不好看。那么在直接键入修改地址信息后如何将信息共享到别的函数中?绑定,键入绑定,鼠标移动出方框时绑定等。如此一来,虽然成功了,却使代码繁琐了不少(算了,反正我也不在于效率,毕竟写到这连协程都没用,对,没错,我是这么安慰自己的)。
储存地址选择框
搜寻显示框(右上角):
article 的核心部分。
本想用treeview,百度了几个小时,背景颜色也没能调出来(换风格后感觉没原装好看)。索性依然使用 Listbox。
但此处,如果没有中介的话,header中爬到的数据却是无论如何也无法自动写入其中。故在下方原翻页按钮处加了些判断。使得每次搜索数据结束后,需要点击下<–>箭头才能显示输出数据。因此,箭头按钮保定函数中包含对spider函数的调用以及对结果数据的处理。
搜寻显示框
翻页按钮(右下角):
因为每次获取的数据只有10个,通过翻页爬取更多数据
翻页按钮
音乐选择提示框(右中):
在右上显示框中通过右键菜单选择音乐,并在选择后通过右键菜单来下载音乐

音乐选择提示框

3)footer 部分

下载日志提示框:
会显示日志log记录数据,记录信息在同文件夹下log.txt文件中
log数据包括,下载信息,及出错崩溃信息等。

在这里插入图片描述在这里插入图片描述

2.源代码

以下为本程序代码展示:

import requests
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
from tkinter import messagebox
import time


class Download():
	station_dict = {
   '酷狗':'kugou', '网易':'netease', 'QQ (ps:可能有防护,时灵不灵)':'qq', '酷我':'kuwo', '虾米':'xiami', '百度':'baidu', '咪咕':'migu'}
	def __init__(self, win_name):
		self._win = win_name
		self.headerTipString = tk.StringVar()					# 定义 header 处提示文本
		self.footerTipString = tk.StringVar()					# 定义 footer 处提示文本
		self.firstShow = None
		self.searchName = tk.StringVar()
		self.selectedStation = None
		self.searchPage = None
		self.selectedPages = []
		self.searchResult = [['歌名', '歌手', '地址']]
		self.iWantName = None
		self.iWantUrl = None
		self.pathName = tk.StringVar()

	def open_url(self, url):
		headers = {
   
			'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36',
		}
		try:
			re = requests.get(url=url, headers=headers)
			re.raise_for_status()
			return re.content
		except Exception as err:
			self.headerTipString.set('!!歌曲下载出错!!请查看日志')
			self.log_write(err)

	def spider(self, name, station, page):
		url = 'http://www.eggvod.cn/music/'
		data = {
   
			'input': name,
			'filter': 'name',
			'type': station,
			'page': page,				# 每页10个数据
		}
		headers = {
   
			'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36',
			'X-requested-with': 'XMLHttpRequest',
		}
		try:
			re = requests.post(url=url, headers=headers, data=data)
			re.raise_for_status()
			re_json = re.json()
			result = []
			for each in re_json['data']:
				result.append((each['title'], each['author'], each['url']))
			return result
		except Exception as err:
			self.headerTipString.set('!!数据搜索出现错误!!请查看日志')
			self.log_write(err)


	def parse_spider(self, page):
		source = self.searchResult[page]
		result = [f'{i}.{" ":^4}\t{source[i][0]:{chr(12288)}^20}\t{source[i][1]:{chr(12288)}^10}' for i in range(len(source))]
		result.insert(0, f'{"":^4}\t{self.searchResult[0][0]:{chr(12288)}^20}\t{self.searchResult[0][1]:{chr(12288)}^10}')
		return result

	def log_write(self, string):
		string = str(string)
		with open('log.txt', 'a+', encoding='utf-8') as f:
			f.write(string)
			f.write('\n\n\n')

	def _initialize(self, width=610, height=500):
		def alignstr(self, width, height):
			screenwidth = self.winfo_screenwidth()
			screenheight = self.winfo_screenheight()
			return f'{width}x{height}+{int((screenwidth - width) / 2)}+{int((screenheight - height) / 2)}'

		self._win.title('懒人音乐下载器^_^  (未选择搜索网点)')
		self._win.geometry(alignstr(self._win, width, height))  # 使程序窗口位于屏幕中间
		self._win['bg'] = '#2c3e50'

	# self._win.attributes('-alpha',0.9)

	def _header(self):
		# 背景框架
		frame = tk.Frame(self._win, width=400, height=100, bg='#2c3e50', bd=0, relief='groove')
		frame.pack(side='top', pady=2
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值