效果图:

最近学习QTreeWidget,总想着做些什么,正好学习过一点简单的爬虫,就做了一个简易的“酷我音乐下载器”,界面可能不太好看,以后继续优化。

ui_kuwo.py

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'ui_kuwo.ui'
#
# Created by: PyQt5 UI code generator 5.13.0
#
# WARNING! All changes made in this file will be lost! from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 624)
font = QtGui.QFont()
font.setPointSize(10)
MainWindow.setFont(font)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setGeometry(QtCore.QRect(60, 20, 681, 41))
font = QtGui.QFont()
font.setPointSize(12)
self.lineEdit.setFont(font)
self.lineEdit.setStyleSheet("border:2px groove gray;border-radius:10px;padding:2px 4px")
self.lineEdit.setInputMask("")
self.lineEdit.setText("")
self.lineEdit.setObjectName("lineEdit")
self.treeWidget = QtWidgets.QTreeWidget(self.centralwidget)
self.treeWidget.setGeometry(QtCore.QRect(60, 110, 681, 411))
self.treeWidget.setObjectName("treeWidget")
self.treeWidget.headerItem().setTextAlignment(0, QtCore.Qt.AlignCenter)
font = QtGui.QFont()
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.treeWidget.headerItem().setFont(0, font)
self.treeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignCenter)
font = QtGui.QFont()
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.treeWidget.headerItem().setFont(1, font)
self.treeWidget.headerItem().setTextAlignment(2, QtCore.Qt.AlignCenter)
font = QtGui.QFont()
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.treeWidget.headerItem().setFont(2, font)
self.treeWidget.headerItem().setTextAlignment(3, QtCore.Qt.AlignCenter)
font = QtGui.QFont()
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.treeWidget.headerItem().setFont(3, font)
self.treeWidget.headerItem().setTextAlignment(4, QtCore.Qt.AlignCenter)
font = QtGui.QFont()
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.treeWidget.headerItem().setFont(4, font)
self.treeWidget.header().setDefaultSectionSize(130)
self.btnSel_ALL = QtWidgets.QPushButton(self.centralwidget)
self.btnSel_ALL.setGeometry(QtCore.QRect(210, 80, 75, 23))
self.btnSel_ALL.setObjectName("btnSel_ALL")
self.btnSel_None = QtWidgets.QPushButton(self.centralwidget)
self.btnSel_None.setGeometry(QtCore.QRect(340, 80, 75, 23))
self.btnSel_None.setObjectName("btnSel_None")
self.btnSel_Invs = QtWidgets.QPushButton(self.centralwidget)
self.btnSel_Invs.setGeometry(QtCore.QRect(470, 80, 75, 23))
self.btnSel_Invs.setObjectName("btnSel_Invs")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(60, 530, 681, 16))
self.label.setText("")
self.label.setObjectName("label")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.toolBar = QtWidgets.QToolBar(MainWindow)
self.toolBar.setToolButtonStyle(QtCore.Qt.ToolButtonTextUnderIcon)
self.toolBar.setObjectName("toolBar")
MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)
self.actSong_Download = QtWidgets.QAction(MainWindow)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap("Image/icon/download.jpg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.actSong_Download.setIcon(icon)
self.actSong_Download.setObjectName("actSong_Download")
self.actSong_Search = QtWidgets.QAction(MainWindow)
icon1 = QtGui.QIcon()
icon1.addPixmap(QtGui.QPixmap("Image/icon/search.jpg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.actSong_Search.setIcon(icon1)
self.actSong_Search.setObjectName("actSong_Search")
self.toolBar.addAction(self.actSong_Download) self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "酷我音乐下载器"))
self.lineEdit.setPlaceholderText(_translate("MainWindow", "请输入歌手名/歌曲名"))
self.treeWidget.headerItem().setText(0, _translate("MainWindow", "序号"))
self.treeWidget.headerItem().setText(1, _translate("MainWindow", "歌曲"))
self.treeWidget.headerItem().setText(2, _translate("MainWindow", "歌手"))
self.treeWidget.headerItem().setText(3, _translate("MainWindow", "专辑"))
self.treeWidget.headerItem().setText(4, _translate("MainWindow", "时长"))
self.btnSel_ALL.setText(_translate("MainWindow", "全选"))
self.btnSel_None.setText(_translate("MainWindow", "全不选"))
self.btnSel_Invs.setText(_translate("MainWindow", "反选"))
self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar"))
self.actSong_Download.setText(_translate("MainWindow", "下载歌曲"))
self.actSong_Download.setToolTip(_translate("MainWindow", "下载歌曲"))
self.actSong_Search.setText(_translate("MainWindow", "搜索"))
self.actSong_Search.setToolTip(_translate("MainWindow", "搜索"))

myMainWindow_kuwo.py

#!/usr/bin/env python
# _*_ coding: UTF-8 _*_
"""=================================================
@Project -> File : Operate-system -> myMainWindow_kuwo_download.py
@IDE : PyCharm
@Author : zihan
@Date : 2020/4/21 10:26
@Desc :
=================================================""" import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QTreeWidgetItem, QLineEdit
from enum import Enum
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from ui_kuwo import Ui_MainWindow
import requests
import os def create_folder(path):
if os.path.exists(path):
pass
else:
os.mkdir(path) # 节点类型的枚举类型
class TreeItemType(Enum):
itTopItem = 1001 # 顶层节点
itGroupItem = 1002 # 分组节点
itImageItem = 1003 # 图片文件节点 class TreeColNum(Enum): # 目录树的列号的枚举类型
col_order = 0 # 序号列
col_song_name = 1 # 歌曲名列
col_singer_name = 2 # 歌手列
col_singer_album = 3 # 专辑列
col_song_time = 4 # 时长列
col_song_rid = 5 # rid class SongDownload(QThread):
label_str = pyqtSignal(str) def __init__(self, path, song_url, song_name):
super(SongDownload, self).__init__()
self.__path = path
self.__url = song_url
self.__songname = song_name def run(self):
self.label_str.emit("正在请求响应...请耐心等待")
# self.label_str.emit("正在下载{}".format(self.__songname))
# print("正在下载{}".format(self.__songname))
data = requests.get(self.__url).content with open(self.__path + "/" + self.__songname + ".mp3", "wb") as f:
f.write(data)
self.label_str.emit(self.__songname + " 成功下载到" + self.__path)
print(self.__songname + " 成功下载到" + self.__path) class QmyMainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.thread = None self.itemFlags = Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsAutoTristate self.ui.lineEdit.addAction(self.ui.actSong_Search, QLineEdit.TrailingPosition) self.ui.treeWidget.hideColumn(5) # 当点击搜索时
self.ui.actSong_Search.triggered.connect(self.do_act_song_search_triggered)
# 全选
self.ui.btnSel_ALL.clicked.connect(self.do_btn_sel_all_clicked)
# 全不选
self.ui.btnSel_None.clicked.connect(self.do_btn_sel_none_clicked)
# 反选
self.ui.btnSel_Invs.clicked.connect(self.do_btn_sel_invs_clicked)
# 当点击下载歌曲时
self.ui.actSong_Download.triggered.connect(self.do_act_song_download_triggered) # 全选
def do_btn_sel_all_clicked(self):
for i in range(self.ui.treeWidget.topLevelItemCount()):
aItem = self.ui.treeWidget.topLevelItem(i)
aItem.setCheckState(TreeColNum.col_order.value, Qt.Checked) # 全不选
def do_btn_sel_none_clicked(self):
for i in range(self.ui.treeWidget.topLevelItemCount()):
aItem = self.ui.treeWidget.topLevelItem(i)
aItem.setCheckState(TreeColNum.col_order.value, Qt.Unchecked) # 反选
def do_btn_sel_invs_clicked(self):
for i in range(self.ui.treeWidget.topLevelItemCount()):
aItem = self.ui.treeWidget.topLevelItem(i)
if aItem.checkState(TreeColNum.col_order.value) != Qt.Checked:
aItem.setCheckState(TreeColNum.col_order.value, Qt.Checked)
else:
aItem.setCheckState(TreeColNum.col_order.value, Qt.Unchecked) # 当点击搜索按钮时
def do_act_song_search_triggered(self):
# 获取歌手名或者歌曲名
search_text = self.ui.lineEdit.text()
self.ui.treeWidget.clear()
self.get_kuwo_songs(search_text) # 搜索之后列出歌单
def show_songs_tree(self, order, song_name, singer_name, singer_album, song_time, song_rid):
# 创建节点
item = QTreeWidgetItem(TreeItemType.itTopItem.value)
item.setText(TreeColNum.col_order.value, str(order+1))
item.setText(TreeColNum.col_song_name.value, song_name)
item.setText(TreeColNum.col_singer_name.value, singer_name)
item.setText(TreeColNum.col_singer_album.value, singer_album)
item.setText(TreeColNum.col_song_time.value, song_time)
item.setText(TreeColNum.col_song_rid.value, str(song_rid)) item.setFlags(self.itemFlags)
item.setCheckState(TreeColNum.col_order.value, Qt.Checked)
item.setData(TreeColNum.col_order.value, Qt.UserRole, "") # 设置文字居中
item.setTextAlignment(TreeColNum.col_order.value, Qt.AlignCenter)
item.setTextAlignment(TreeColNum.col_song_name.value, Qt.AlignCenter)
item.setTextAlignment(TreeColNum.col_singer_name.value, Qt.AlignCenter)
item.setTextAlignment(TreeColNum.col_singer_album.value, Qt.AlignCenter)
item.setTextAlignment(TreeColNum.col_song_time.value, Qt.AlignCenter)
item.setTextAlignment(TreeColNum.col_song_rid.value, Qt.AlignCenter)
self.ui.treeWidget.addTopLevelItem(item) # 获取歌曲,默认5页
def get_kuwo_songs(self, key, page=5):
# 右键检查-->network-->在Name找到searchMusicBykeyWord?点开,在右侧可以看到请求头和url信息
headers = {
'User-Agent': "agent信息",
'Referer': "refer信息",
'csrf': "csrf信息",
'Cookie': "cookie信息"
}
order = 0
for i in range(1, page + 1):
url = "http://www.kuwo.cn/api/www/search/searchMusicBykeyWord?key={}&pn={}".format(key, i)
html = requests.get(url, headers=headers).json()
data = html["data"]["list"]
for dic_songs_info in data:
song_rid = dic_songs_info["rid"]
song_name = dic_songs_info["name"]
singer_name = dic_songs_info["artist"]
song_time = dic_songs_info["songTimeMinutes"]
singer_album = dic_songs_info["album"]
path_singer = "./酷我音乐/" + self.ui.lineEdit.text()
create_folder(path_singer)
self.show_songs_tree(order, song_name, singer_name, singer_album, song_time, song_rid)
order = order + 1 def start_thread(self, path_singer, song_url, song_name):
# 创建线程
self.thread = SongDownload(path_singer, song_url, song_name)
# 连接信号
self.thread.label_str[str].connect(self.do_label_change)
# 开启线程
self.thread.start() # 当点击下载
def do_act_song_download_triggered(self):
for i in range(self.ui.treeWidget.topLevelItemCount()):
aItem = self.ui.treeWidget.topLevelItem(i)
if aItem.checkState(TreeColNum.col_order.value) != Qt.Checked:
pass
else:
song_name = aItem.text(1)
song_rid = int(aItem.text(5))
song_url = self.get_one_song_url(song_rid)
path_singer = "./酷我音乐/" + self.ui.lineEdit.text()
self.start_thread(path_singer, song_url, song_name) # 获取一首歌的url
def get_one_song_url(self, rid):
# 点开一首免费歌曲,找到url格式url?format,点开找到头信息
url = "http://www.kuwo.cn/url?format=mp3&rid={}&response=url&type=convert_url3&br=128kmp3&from=web&t=1587429921873&reqId=617f0321-8369-11ea-80b3-bbd056ce88a1".format(
rid)
headers = {
'Cookie': "",
'Referer': "",
'User-Agent': "",
'csrf': ""
}
data = requests.get(url, headers=headers).json()
# {'code': 200, 'msg': 'success', 'url': 'https://win-web-ra01-sycdn.kuwo.cn/e9cbd04a0602f9f82c40e0f1800fa696/5e9e44a1/resource/n3/128/43/28/1310690697.mp3'}
song_url = data["url"]
return song_url def do_label_change(self, str_label):
self.ui.label.setText(str_label) if __name__ == "__main__":
app = QApplication(sys.argv) # 创建app,用QApplication类
form = QmyMainWindow()
form.show()
sys.exit(app.exec_())

下面说一些值得注意的点。

一、搜索歌曲时请求头信息

初学者容易在这个地方就卡住,我用的google浏览器,现在就这个跟大家分享一下。

首先,打开酷我音乐网页,随便搜索一个歌手名,比如周杰伦。

可以看到他的歌单,此时鼠标右击打开Inspect-->NetWork

在左边找到searchMusicByKeyWord这种样式单击鼠标左键,可以在右边看到url以及请求头信息。

二、下载歌曲时,url格式的获取

这是第二个值得注意的地方,这时,需要找到一个可以免费播放的歌曲,然后在network左侧找到url?format这种样式的单击鼠标左键,在右侧找到url链接(我个人是这样找的,但是我觉得原理可能并不是这样,如果没有免费歌曲的话,那岂不是下载不了,如果有谁知道正确简单的查找方法,欢迎在评论区分享给大家,谢谢。)

第十八篇 -- QTreeWidget应用篇 -- kuwo的更多相关文章

  1. 转: 【Java并发编程】之十八:第五篇中volatile意外问题的正确分析解答(含代码)

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/17382679 在<Java并发编程学习笔记之五:volatile变量修饰符-意料之外 ...

  2. Nginx详解二十八:Nginx架构篇Nginx+Lua的安全waf防火墙

    Nginx+Lua的安全waf防火墙 看一下别人写好的:https://github.com/loveshell/ngx_lua_waf 先安装git:yum -y install git 在/opt ...

  3. Python之路【第十八篇】:Web框架们

    Python之路[第十八篇]:Web框架们   Python的WEB框架 Bottle Bottle是一个快速.简洁.轻量级的基于WSIG的微型Web框架,此框架只由一个 .py 文件,除了Pytho ...

  4. Android为TV端助力 转载:Android绘图Canvas十八般武器之Shader详解及实战篇(上)

    前言 Android中绘图离不开的就是Canvas了,Canvas是一个庞大的知识体系,有Java层的,也有jni层深入到Framework.Canvas有许多的知识内容,构建了一个武器库一般,所谓十 ...

  5. Android为TV端助力 转载:Android绘图Canvas十八般武器之Shader详解及实战篇(下)

    LinearGradient 线性渐变渲染器 LinearGradient中文翻译过来就是线性渐变的意思.线性渐变通俗来讲就是给起点设置一个颜色值如#faf84d,终点设置一个颜色值如#CC423C, ...

  6. 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十八:SDRAM模块① — 单字读写

    实验十八:SDRAM模块① — 单字读写 笔者与SDRAM有段不短的孽缘,它作为冤魂日夜不断纠缠笔者.笔者尝试过许多方法将其退散,不过屡试屡败的笔者,最终心情像橘子一样橙.<整合篇>之际, ...

  7. Egret入门学习日记 --- 第十八篇(书中 8.5~8.7 节 内容)

    第十八篇(书中 8.5~8.7 节 内容) 其实语法篇,我感觉没必要写录入到日记里. 我也犹豫了好久,到底要不要录入. 这样,我先读一遍语法篇的所有内容,我觉得值得留下的,我就录入日记里. 不然像昨天 ...

  8. 《手把手教你》系列技巧篇(二十八)-java+ selenium自动化测试-处理模态对话框弹窗(详解教程)

    1.简介 在前边的文章中窗口句柄切换宏哥介绍了switchTo方法,这篇继续介绍switchTo中关于处理alert弹窗的问题.很多时候,我们进入一个网站,就会弹窗一个alert框,有些我们直接关闭, ...

  9. 《手把手教你》系列技巧篇(三十八)-java+ selenium自动化测试-日历时间控件-下篇(详解教程)

    1.简介 理想很丰满现实很骨感,在应用selenium实现web自动化时,经常会遇到处理日期控件点击问题,手工很简单,可以一个个点击日期控件选择需要的日期,但自动化执行过程中,完全复制手工这样的操作就 ...

随机推荐

  1. NX二次开发-获取面的外围边和孔槽边

    函数: UF_MODL_ask_face_loops()  获取面的所有封闭边组合(多组edge) UF_MODL_ask_loop_list_count() 获取loop的数量(面上孔.槽的数量+1 ...

  2. SpringBoot数据访问(二) SpringBoot整合JPA

    JPA简介 Spring Data JPA是Spring Data大家族的一部分,它可以轻松实现基于JPA的存储库.该模块用于增强支持基于JPA的数据访问层,它使我们可以更加容易地构建使用数据访问技术 ...

  3. 【模拟8.11】星空(差分转化,状压DP,最短路)

    一道很好的题,综合很多知识点. 首先复习差分:      将原来的每个点a[i]转化为b[i]=a[i]^a[i+1],(如果是求和形式就是b[i]=a[i+1]-a[i]) 我们发现这样的方便在于我 ...

  4. 【题解】数颜色 STL vector数组

    小 C 的兔子不是雪白的,而是五彩缤纷的. 题目 题目描述 小 C 的兔子不是雪白的,而是五彩缤纷的.每只兔子都有一种颜色,不同的兔子可能有 相同的颜色.小 C 把她标号从 1 到 n 的 n只兔子排 ...

  5. Mysql优化(出自官方文档) - 第一篇(SQL优化系列)

    Mysql优化(出自官方文档) - 第一篇 目录 Mysql优化(出自官方文档) - 第一篇 1 WHERE Clause Optimization 2 Range Optimization Skip ...

  6. 『心善渊』Selenium3.0基础 — 12、Selenium操作鼠标和键盘事件

    目录 (一)对鼠标的操作 1.鼠标事件介绍 2.ActionChains 类鼠标操作的常用方法 3.perform()方法 4.鼠标事件操作步骤 5.示例 (1)右键单击.左键双击 (2)鼠标拖拽动作 ...

  7. Maven——基础篇

    Maven--基础篇 Maven出现前的问题 一个项目就是一个工程,而工程内一般是通过package包来分模块,比较用户模块,订单模块等,如果项目过于庞大,通过包模块来划分就不太合适,而应该拆分为模块 ...

  8. 单片机引脚扩展芯片74HC595手工分解实验

    我们先来看下效果 74HC595是常用的串转并芯片,支持芯片级联实现少量IO口控制多个IO口输出功能 14脚:DS,串行数据输入引脚 13脚:OE, 输出使能控制脚,它是低电才使能输出,所以接GND ...

  9. 面试官:spring中定义bean的方法有哪些?我一口气说出了12种,把面试官整懵了。

    前言 在庞大的java体系中,spring有着举足轻重的地位,它给每位开发者带来了极大的便利和惊喜.我们都知道spring是创建和管理bean的工厂,它提供了多种定义bean的方式,能够满足我们日常工 ...

  10. shiro框架基础

    一.shiro框架简介 Apache Shiro是Java的一个安全框架.其内部架构如下: 下面来介绍下里面的几个重要类: Subject:主体,应用代码直接交互的对象就是Subject.代表了当前用 ...