-
-
Notifications
You must be signed in to change notification settings - Fork 2k
/
HotPlaylist.py
169 lines (147 loc) · 6.31 KB
/
HotPlaylist.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Created on 2023/02/22
@author: Irony
@site: https://pyqt.site https://github.com/PyQt5
@email: [email protected]
@file: HotPlaylist.py
@description:
"""
import os
import sys
from Lib.CoverItemWidget import CoverItemWidget
from lxml.etree import HTML # @UnresolvedImport
try:
from PyQt5.QtCore import QTimer, QUrl
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
from PyQt5.QtSvg import QSvgWidget
from PyQt5.QtWidgets import (QAbstractSlider, QApplication, QListWidget,
QListWidgetItem)
except ImportError:
from PySide2.QtCore import QTimer, QUrl
from PySide2.QtNetwork import QNetworkAccessManager, QNetworkRequest
from PySide2.QtSvg import QSvgWidget
from PySide2.QtWidgets import (QAbstractSlider, QApplication, QListWidget,
QListWidgetItem)
# offset=0,35,70,105
Url = "https://music.163.com/discover/playlist/?order=hot&cat=%E5%85%A8%E9%83%A8&limit=35&offset={0}"
Agent = b"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.50"
Referer = b"https://music.163.com"
# 作者
Actor = '''<a href="{href}" target="_blank" title="{title}" style="text-decoration: none;font-size: 12px;color: #999999;">{title}</a> '''
class Window(QListWidget):
Page = 0
def __init__(self, *args, **kwargs):
super(Window, self).__init__(*args, **kwargs)
self.resize(800, 600)
self.setFrameShape(self.NoFrame) # 无边框
self.setFlow(self.LeftToRight) # 从左到右
self.setWrapping(True) # 这三个组合可以达到和FlowLayout一样的效果
self.setResizeMode(self.Adjust)
self._loadStart = False
# 连接竖着的滚动条滚动事件
self.verticalScrollBar().actionTriggered.connect(self.onActionTriggered)
# 进度条
self.loadWidget = QSvgWidget(self,
minimumHeight=120,
minimumWidth=120,
visible=False)
self.loadWidget.load('Data/Svg_icon_loading.svg')
# 异步网络下载管理器
self._manager = QNetworkAccessManager(self)
self._manager.finished.connect(self.onFinished)
def load(self):
if self.Page == -1 or self.Page > 10:
return
self._loadStart = True
self.loadWidget.setVisible(True)
# 延迟一秒后调用目的在于显示进度条
QTimer.singleShot(1000, self._load)
def _load(self):
print("load url:", Url.format(self.Page * 35))
url = QUrl(Url.format(self.Page * 35))
req = QNetworkRequest(url)
req.setRawHeader(b"User-Agent", Agent)
self._manager.get(req)
def onFinished(self, reply):
# 请求完成后会调用该函数
req = reply.request() # 获取请求
iwidget = req.attribute(QNetworkRequest.User + 1, None)
path = req.attribute(QNetworkRequest.User + 2, None)
html = reply.readAll().data()
reply.deleteLater()
del reply
if iwidget and path and html:
# 这里是图片下载完毕
open(path, "wb").write(html)
iwidget.setCover(path)
return
# 解析网页
self._parseHtml(html)
self._loadStart = False
self.loadWidget.setVisible(False)
def _parseHtml(self, html):
# print(html)
# encoding = chardet.detect(html) or {}
# html = html.decode(encoding.get("encoding","utf-8"))
html = HTML(html)
# 查找所有的li list_item
lis = html.xpath("//ul[@id='m-pl-container']/li")
# print(lis)
if not lis:
self.Page = -1 # 后面没有页面了
return
self.Page += 1
self._makeItem(lis)
def _makeItem(self, lis):
for li in lis:
a = li.find('.//div/a')
play_url = "https://music.163.com" + a.get("href") # 歌单播放地址
img = li.find(".//div/img")
cover_url = img.get("src") # 封面图片
playlist_title = a.get("title") # 歌单名
# 歌手
author_info = li.xpath(".//p[2]/a")[0]
playlist_author = "<span style=\"font-size: 12px;\"作者:{}</span>".format(
Actor.format(href="https://music.163.com" +
author_info.get("href"),
title=author_info.get("title")))
# 播放数
play_count = (li.xpath(".//div/div/span[2]/text()") or [""])[0]
path = "cache/{0}.jpg".format(
os.path.splitext(os.path.basename(cover_url).split('?')[0])[0])
cover_path = "Data/pic_v.png"
if os.path.isfile(path):
cover_path = path
# print(cover_path, playlist_title,
# playlist_author, play_count, play_url, cover_url, path)
iwidget = CoverItemWidget(self, manager=self._manager)
iwidget.init(cover_path, playlist_title, playlist_author,
play_count, play_url, cover_url, path)
item = QListWidgetItem(self)
item.setSizeHint(iwidget.sizeHint())
self.setItemWidget(item, iwidget)
def onActionTriggered(self, action):
# 这里要判断action=QAbstractSlider.SliderMove,可以避免窗口大小改变的问题
# 同时防止多次加载同一个url
if action != QAbstractSlider.SliderMove or self._loadStart:
return
# 使用sliderPosition获取值可以同时满足鼠标滑动和拖动判断
if self.verticalScrollBar().sliderPosition() == self.verticalScrollBar(
).maximum():
# 可以下一页了
self.load()
def resizeEvent(self, event):
super(Window, self).resizeEvent(event)
self.loadWidget.setGeometry(
int((self.width() - self.loadWidget.minimumWidth()) / 2),
int((self.height() - self.loadWidget.minimumHeight()) / 2),
self.loadWidget.minimumWidth(), self.loadWidget.minimumHeight())
if __name__ == "__main__":
os.makedirs("cache", exist_ok=True)
app = QApplication(sys.argv)
w = Window()
w.show()
w.load()
sys.exit(app.exec_())