作为浙江的新高一,即将面临严肃的住校生活。虽说杭城高中宿舍水平冠绝全国,但总是缺点什么------------没有乐趣!
所以脑洞大开的我决定偷偷带 mp3 到宿舍,睡前洗澡来一曲,快乐似神仙♪٩(´ω`)و♪。然而巧妇难无米之炊,我手中的 mp3 无法连接网络,需要计算机传输音乐软件,因此需要下载大量音乐。酷狗、网易云我就不说了,下载音乐伤感情。于是我就找到了一个神仙音乐网站------------ https://www.xxxx.com。这里有很多免费音乐可以免费下载,但是必须要等待 35s。如下
真伤感情!于是我无奈打开了开发者工具。发现我点开音乐播放键后,Network 瞬间传过来两个文件:
有 mp3 文件!访问一下,是可以下载的。
眉头一皱,计上心来------------可不可以通过监听这个请求来截获音乐 URL,以此实现音乐下载?事不宜迟,动起来吧!顺便锻炼自己爬取 JS 动态渲染页面的能力。
可有人访问了网站会问:为什么说是 JS 渲染的呢?audio 节点的 src不是有音乐地址吗?我们不妨做个实验:
1 from lxml import etree
2 import requests
3
4 headers = {
5 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36"
6 }
7
8 resp = requests.get("https://www.xxxx.com/music/12764", headers=headers)#自己点击后把网站改过来,这里保护该网站
9 html = etree.HTML(resp.text)
10 location = html.xpath("//audio[@id='custom-audio-player']/@src")
11
12 print(location)
惊奇地发现结果是:
[]
什么都没有?为了验证我们还可以把resp.text保存到本地,看看html:
1 <!doctype html>
2 <html lang="zh-CN">
3 <head>
4 <meta charset="UTF-8">
5 <meta http-equiv="X-UA-Compatible" content="ie=edge">
6 <meta http-equiv="Cache-Control" content="no-transform"/>
7 <meta http-equiv="Cache-Control" content="no-siteapp"/>
8 <meta name="referrer" content="same-origin">
9 <meta name="viewport" content="width=device-width, initial-scale=1">
10 <meta name="keywords" content="大海航行靠舵手-中国人民解放军军乐团.mp3, 大海航行靠舵手-中国人民解放军军乐团 在线播放, 大海航行靠舵手-中国人民解放军军乐团 MP3下载, 大海航行靠舵手-中国人民解放军军乐团 歌词下载, 免费下载">
11 <meta name="description"
12 content="《大海航行靠舵手》是 中国人民解放军军乐团 演唱的歌曲,歌曲宝提供《大海航行靠舵手-中国人民解放军军乐团.mp3》歌曲在线免费下载与播放,大海航行靠舵手 歌词下载,免费下载MP3音乐就上歌曲宝,曲库完整,更新迅速,试听流畅,支持高品质、无损音质~">
13 <meta name="mobile-web-app-capable" content="yes">
14 <meta name="apple-mobile-web-app-status-bar-style" content="black">
15 <meta name="apple-mobile-web-app-title" content="音乐MP3免费下载网站">
16 <meta name="application-name" content="音乐MP3免费下载网站">
17 <meta name="format-detection" content="telephone=no">
18 <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"/>
19
20 <title>大海航行靠舵手-中国人民解放军军乐团-MP3在线免费播放与下载 - 歌曲宝MP3音乐下载</title>
21
22 <link rel="stylesheet" href="/static/css/loading.css">
23 <link rel="stylesheet" href="//code.bdstatic.com/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css"/>
24 <link rel="stylesheet" href="//code.bdstatic.com/npm/font-awesome@4.7.0/css/font-awesome.min.css"/>
25 <link rel="stylesheet" href="//code.bdstatic.com/npm/jquery-confirm@3.3.4/dist/jquery-confirm.min.css"/>
26
27
28 <script src="//code.bdstatic.com/npm/jquery@3.7.1/dist/jquery.min.js"></script>
29
30
31 <script defer src="//code.bdstatic.com/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
32 <script defer src="//code.bdstatic.com/npm/bootstrap@4.6.1/dist/js/bootstrap.min.js"></script>
33 <script defer src="//code.bdstatic.com/npm/jquery-confirm@3.3.4/dist/jquery-confirm.min.js"></script>
34 <script type="text/javascript">
35 window.user = JSON.parse('{\u0022isLogin\u0022:false,\u0022isVip\u0022:false,\u0022config\u0022:[]}');
36 </script>
37 </head>
38 <body>
39
40
41 <div id="loader-wrapper">
42 <div class="loader"></div>
43 </div>
44
45 <script>
46 document.addEventListener('DOMContentLoaded', function() {
47 document.body.classList.add('loaded');
48
49 setTimeout(function() {
50 const loaderWrapper = document.getElementById('loader-wrapper');
51 if (loaderWrapper) {
52 loaderWrapper.remove();
53 }
54 }, 500);
55 });
56 </script>
57
58
59 <div class="container" style="max-width: 900px">
60 <nav class="navbar navbar-expand-md navbar-light px-0 mb-1 font-weight-normal">
61
62 <a class="navbar-brand d-flex align-items-center" style="max-width: calc(100% - 135px);" href="/">
63 <img src="/static/img/logo.png" width="50" height="50" class="mr-2 flex-shrink-0" alt="logo">
64
65 <div class="d-flex flex-column justify-content-center" style="min-width: 0;">
66 <span class="font-weight-bolder text-truncate d-block" style="font-size: 1.15rem; color: #50514F; line-height: 1.3;">
67 歌曲宝
68 </span>
69 <span class="domain-text-enhanced mt-1">
70 Gequbao.com
71 </span>
72 </div>
73 </a>
74
75 <div class="collapse navbar-collapse order-3 order-md-2" id="navbarSupportedContent">
76 <ul class="navbar-nav mr-auto">
77 <li class="nav-item">
78 <a class="nav-link" href="/hot-news">资讯</a>
79 </li>
80 <li class="nav-item dropdown">
81 <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown"
82 aria-haspopup="true" aria-expanded="false">
83 排行榜
84 </a>
85 <div class="dropdown-menu" aria-labelledby="navbarDropdown" style="border-top: 0.15rem solid #1e9fff;">
86 <a class="dropdown-item" href="/top/week-search"><i class="fa fa-trophy" style="color: #ff6347"></i> 周搜索榜</a>
87 <a class="dropdown-item" href="/top/week-download"><i class="fa fa-trophy" style="color: #ff6347"></i> 周下载榜</a>
88 </div>
89 </li>
90 <li class="nav-item active">
91 <a class="nav-link reddot" href="/hot-music">推荐</a>
92 </li>
93 </ul>
94 </div>
95
96 <div class="d-flex align-items-center ml-auto flex-shrink-0 order-2 order-md-3">
97
98 <ul class="navbar-nav flex-row align-items-center mr-2 mr-md-0">
99 <li class="nav-item">
100 <a class="btn btn-outline-dark btn-sm custom-login-btn" href="/member/auth/login">
101 <i class="fa fa-user-circle"></i>
102 <span class="d-none d-md-inline ml-1">登录 / 注册</span>
103 <span class="d-md-none ml-1">登录</span>
104 </a>
105 </li>
106 </ul>
107
108 <button class="navbar-toggler border-0 px-1" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
109 aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
110 <span class="navbar-toggler-icon"></span>
111 </button>
112 </div>
113 </nav>
114
115 <div class="row mb-2">
116 </div>
117
118
119 <div class="card shadow-sm mb-1 custom-player-wrapper border-0">
120 <div class="card-body py-3 px-3 px-md-4">
121
122
123 <div class="custom-player-layout mb-2 pb-3">
124
125
126 <div class="player-cover-box" id="player-toggle-btn">
127 <img src="http://img1.kuwo.cn/star/albumcover/500/56/49/1656485620.jpg" alt="cover" class="player-cover-img">
128 <div class="player-play-overlay">
129 <i class="fa fa-play" id="player-icon" style="margin-left: 4px;"></i>
130 </div>
131 </div>
132
133
134 <div class="player-text-info text-left">
135 <h1 class="song-title-styled mb-1 d-flex align-items-center justify-content-start">
136 <span class="text-truncate" title="大海航行靠舵手-中国人民解放军军乐团">大海航行靠舵手</span>
137 </h1>
138 <div class="song-author-styled mb-1 text-truncate">
139 中国人民解放军军乐团
140 </div>
141 <div class="current-lyric-styled mb-1 text-truncate" id="current-lyric-line"
142 style="min-height: 22px;">
143 暂无歌词信息
144 </div>
145 </div>
146
147
148 <div class="player-controls-area">
149
150 <div class="d-flex align-items-center mb-2 w-100" id="progress-container">
151 <span id="time-current" class="text-muted small mr-3 font-weight-bold"
152 style="font-family: monospace;">00:00</span>
153
154 <div class="player-progress-bar flex-grow-1 position-relative" id="progress-bar-bg">
155 <div class="player-progress-buffered" id="progress-bar-buffered" style="width: 0;"></div>
156 <div class="player-progress-fill" id="progress-bar-fill" style="width: 0;"></div>
157 <div class="player-progress-handle" id="progress-bar-handle"></div>
158 </div>
159
160 <span id="time-total" class="text-muted small ml-3 font-weight-bold"
161 style="font-family: monospace;">01:44</span>
162
163
164 <div
165 class="volume-control-wrapper position-relative d-flex align-items-center ml-2 ml-md-3 flex-shrink-0">
166
167
168 <span
169 class="text-secondary p-0 border-0 shadow-none d-flex align-items-center cursor-pointer">
170 <i class="fa fa-volume-up" id="volume-icon"></i>
171 </span>
172
173
174 <div class="volume-slider-container">
175 <div class="player-volume-bar" id="volume-bar-bg">
176 <div class="player-volume-fill" id="volume-bar-fill" style="height: 100%;"></div>
177 <div class="player-volume-handle" id="volume-bar-handle"></div>
178 </div>
179 </div>
180 </div>
181
182 <button id="btn-loop-mode"
183 class="btn btn-link text-secondary p-0 border-0 shadow-none ml-1 d-flex align-items-center active-loop"
184 title="关闭单曲循环">
185 <i class="fa fa-retweet"></i>
186 </button>
187
188 <button id="btn-playback-rate"
189 class="btn btn-link font-smaller text-secondary p-0 border-0 shadow-none ml-1 font-weight-bold"
190 style="text-decoration: none;" title="切换倍速">
191 1.0x
192 </button>
193 </div>
194
195
196 <div class="d-flex justify-content-start align-items-center flex-nowrap action-btn-group">
197 <button
198 class="btn btn-primary-blue rounded-pill px-4 py-1 mr-2 mr-md-3 mb-1 d-flex align-items-center shadow-sm flex-shrink-1"
199 id="btn-main-play">
200 <i class="fa fa-play-circle mr-1" style="font-size: 1.1rem;"></i>
201 <span id="main-play-text" class="font-weight-bold text-nowrap">播放</span>
202 </button>
203 <button id="music-favorite-btn"
204 class="btn btn-ui-white rounded-pill px-4 py-1 mr-2 mr-md-3 mb-1 font-weight-bold shadow-sm flex-shrink-1 text-nowrap"
205 data-status="0">
206 <i class="fa fa-heart mr-1"></i> 收藏
207 </button>
208 <button id="music-add-playlist-btn"
209 class="btn btn-ui-white rounded-pill px-4 py-1 mr-2 mr-md-3 mb-1 font-weight-bold shadow-sm flex-shrink-1 text-nowrap">
210 <i class="fa fa-plus-square mr-1"></i> 歌单
211 </button>
212 </div>
213 </div>
214 </div>
215
216
217 <audio id="custom-audio-player"
218 preload="metadata"></audio>
219
220 <div class="quote-warning mb-2" style="font-size: 0.85rem">
221 <i class="fa fa-info-circle"></i>
222 如无法下载或有错误,请刷新重试或点此 <a id="music-feedback-btn" style="text-decoration: underline;"
223 href="javascript:void(0)">反馈</a> 给我们
224 </div>
225
226 <div class="input-group mb-1 overflow-hidden">
227 <div class="input-group-prepend">
228 <i class="fa fa-music input-group-text"></i>
229 </div>
230 <span class="form-control bg-light nowrap-hidden">
231 大海航行靠舵手-中国人民解放军军乐团.mp3
232 </span>
233 <div class="input-group-append">
234 <a id="btn-download-mp3" class="btn btn-primary" target="_blank">
235 <i class="fa fa-download"></i>
236 下载歌曲</a>
237 </div>
238 </div>
239 <div class="input-group overflow-hidden">
240 <div class="input-group-prepend">
241 <i class="fa fa-file-text-o input-group-text"></i>
242 </div>
243 <span class="form-control bg-light nowrap-hidden">
244 [ti:大海航行靠舵手]
245 [ar:中国人民解放军军乐团]
246 [al:...
247 </span>
248 <div class="input-group-append">
249 <a id="btn-download-lrc" class="btn btn-primary"><i class="fa fa-download"></i> 下载歌词</a>
250 </div>
251 </div>
252 <div class="mt-1">
253 <div class="content-lrc mt-1" id="content-lrc">[ti:大海航行靠舵手]<br />
254 [ar:中国人民解放军军乐团]<br />
255 [al:大海航行靠舵手-中国人民解放军军乐团]<br />
256 [00:00.00]该歌曲暂无歌词</div>
257 <div class="expand" data-status="up">展开</div>
258 </div>
259 </div>
260 <div class="card-footer bg-white" id="download-log">
261 <div style="overflow:hidden;height:1rem;line-height: 1rem;">
262 <ul class="list-group">
263 <li class="text-truncate">
264 <a href="/music/25269030" title="泸沽湖 (cover: 麻园诗人) - 王梓" style="font-size: small"
265 target="_blank">
266 <span class="text-muted">
267 网友刚刚下载了
268 </span>
269 泸沽湖 (cover: 麻园诗人) - 王梓
270 </a>
271 </li>
272 <li class="text-truncate">
273 <a href="/music/5055964" title="Look What God Gave Her - Thomas Rhett" style="font-size: small"
274 target="_blank">
275 <span class="text-muted">
276 网友刚刚下载了
277 </span>
278 Look What God Gave Her - Thomas Rhett
279 </a>
280 </li>
281 <li class="text-truncate">
282 <a href="/music/30618" title="谁 - 曲肖冰" style="font-size: small"
283 target="_blank">
284 <span class="text-muted">
285 网友刚刚下载了
286 </span>
287 谁 - 曲肖冰
288 </a>
289 </li>
290 <li class="text-truncate">
291 <a href="/music/48961280" title="岸边客 - 夏子航(2603)" style="font-size: small"
292 target="_blank">
293 <span class="text-muted">
294 网友刚刚下载了
295 </span>
296 岸边客 - 夏子航(2603)
297 </a>
298 </li>
299 <li class="text-truncate">
300 <a href="/music/777875" title="谁来爱我 - 容祖儿" style="font-size: small"
301 target="_blank">
302 <span class="text-muted">
303 网友刚刚下载了
304 </span>
305 谁来爱我 - 容祖儿
306 </a>
307 </li>
308 <li class="text-truncate">
309 <a href="/music/38245" title="Mystery of Love - Sufjan Stevens" style="font-size: small"
310 target="_blank">
311 <span class="text-muted">
312 网友刚刚下载了
313 </span>
314 Mystery of Love - Sufjan Stevens
315 </a>
316 </li>
317 <li class="text-truncate">
318 <a href="/music/34262510" title="잘 알지도 못하면서 - 清冷音" style="font-size: small"
319 target="_blank">
320 <span class="text-muted">
321 网友刚刚下载了
322 </span>
323 잘 알지도 못하면서 - 清冷音
324 </a>
325 </li>
326 <li class="text-truncate">
327 <a href="/music/39031" title="天下有情人 - 唐艺" style="font-size: small"
328 target="_blank">
329 <span class="text-muted">
330 网友刚刚下载了
331 </span>
332 天下有情人 - 唐艺
333 </a>
334 </li>
335 <li class="text-truncate">
336 <a href="/music/65751" title="光阴的故事 - 罗大佑" style="font-size: small"
337 target="_blank">
338 <span class="text-muted">
339 网友刚刚下载了
340 </span>
341 光阴的故事 - 罗大佑
342 </a>
343 </li>
344 <li class="text-truncate">
345 <a href="/music/3085774" title="工资不够花(DJ猎人版) - 落笔" style="font-size: small"
346 target="_blank">
347 <span class="text-muted">
348 网友刚刚下载了
349 </span>
350 工资不够花(DJ猎人版) - 落笔
351 </a>
352 </li>
353 <li class="text-truncate">
354 <a href="/music/2137" title="枕着光的她 - 任素汐" style="font-size: small"
355 target="_blank">
356 <span class="text-muted">
357 网友刚刚下载了
358 </span>
359 枕着光的她 - 任素汐
360 </a>
361 </li>
362 <li class="text-truncate">
363 <a href="/music/63325502" title="迷人的危险 - 姚晓棠" style="font-size: small"
364 target="_blank">
365 <span class="text-muted">
366 网友刚刚下载了
367 </span>
368 迷人的危险 - 姚晓棠
369 </a>
370 </li>
371 <li class="text-truncate">
372 <a href="/music/595" title="春天里 - 汪峰" style="font-size: small"
373 target="_blank">
374 <span class="text-muted">
375 网友刚刚下载了
376 </span>
377 春天里 - 汪峰
378 </a>
379 </li>
380 <li class="text-truncate">
381 <a href="/music/9653" title="光年之外 - G.E.M. 邓紫棋" style="font-size: small"
382 target="_blank">
383 <span class="text-muted">
384 网友刚刚下载了
385 </span>
386 光年之外 - G.E.M. 邓紫棋
387 </a>
388 </li>
389 <li class="text-truncate">
390 <a href="/music/753716" title="风起流年 - 周深" style="font-size: small"
391 target="_blank">
392 <span class="text-muted">
393 网友刚刚下载了
394 </span>
395 风起流年 - 周深
396 </a>
397 </li>
398 <li class="text-truncate">
399 <a href="/music/24374" title="遗失的美好 - 张韶涵" style="font-size: small"
400 target="_blank">
401 <span class="text-muted">
402 网友刚刚下载了
403 </span>
404 遗失的美好 - 张韶涵
405 </a>
406 </li>
407 </ul>
408 </div>
409 </div>
410 <script type="text/javascript">
411 $(document).ready(function () {
412 autoScroll('#download-log');
413 });
414 </script>
415 </div>
416
417
418
419 <div class="modal fade" id="addToPlaylistModal" tabindex="-1" role="dialog" aria-hidden="true">
420 <div class="modal-dialog modal-sm modal-dialog-centered" role="document">
421 <div class="modal-content modal-content-custom">
422 <div class="modal-header border-bottom-0 pt-3 px-3">
423 <h6 class="modal-title font-weight-bold">加入歌单</h6>
424 <button type="button" class="close" data-dismiss="modal" aria-label="关闭">
425 <span aria-hidden="true">×</span>
426 </button>
427 </div>
428 <div class="modal-body px-3 py-2">
429 <form id="form-add-to-playlist">
430 <input type="hidden" name="music_id" id="target-add-music-id">
431 <div class="form-group mb-0">
432 <select class="form-control form-control-radius" name="playlist_id"
433 id="select-playlist-list"
434 required>
435 <option value="">正在加载歌单...</option>
436 </select>
437 </div>
438 </form>
439 </div>
440 <div class="modal-footer border-top-0 pb-3 px-3 d-flex justify-content-between">
441 <button type="button" class="btn btn-sm btn-outline-primary rounded-pill flex-grow-1 mr-2"
442 data-toggle="modal"
443 data-target="#createPlaylistModal" data-dismiss="modal">
444 新建歌单
445 </button>
446 <button type="button" class="btn btn-sm btn-primary rounded-pill flex-grow-1 ml-2"
447 id="btn-confirm-add-to-playlist">
448 确认加入
449 </button>
450 </div>
451 </div>
452 </div>
453 </div>
454 <div class="modal fade" id="createPlaylistModal" tabindex="-1" role="dialog" aria-hidden="true">
455 <div class="modal-dialog modal-sm modal-dialog-centered" role="document">
456 <div class="modal-content modal-content-custom">
457 <div class="modal-header border-bottom-0 pt-3 px-3">
458 <h6 class="modal-title font-weight-bold">新建歌单</h6>
459 <button type="button" class="close" data-dismiss="modal" aria-label="关闭">
460 <span aria-hidden="true">×</span>
461 </button>
462 </div>
463 <div class="modal-body px-3 py-2">
464 <form id="form-create-playlist">
465 <div class="form-group">
466 <label class="text-muted small font-weight-bold">歌单名称</label>
467 <input type="text" class="form-control form-control-radius" id="new-playlist-title"
468 name="title" required
469 placeholder="例如:学习专用白噪音、华语经典..." maxlength="30">
470 </div>
471 <div class="form-group mb-0">
472 <label class="text-muted small font-weight-bold">简介 (可选)</label>
473 <textarea class="form-control form-control-radius" id="new-playlist-desc"
474 name="description" rows="2"
475 placeholder="简单描述一下这个歌单..." maxlength="100"
476 style="resize: none;"></textarea>
477 </div>
478 </form>
479 </div>
480 <div class="modal-footer border-bottom-0 pb-3 px-3 d-flex justify-content-between">
481 <button type="button" class="btn btn-light rounded-pill px-4" data-dismiss="modal"
482 data-toggle="modal"
483 data-target="#addToPlaylistModal">返回
484 </button>
485 <button type="button" class="btn btn-primary rounded-pill px-4" id="btn-confirm-create-playlist">
486 确认新建
487 </button>
488 </div>
489 </div>
490 </div>
491 </div>
492
493
494
495 <div class="card shadow-sm mb-2">
496 <div class="card-body">
497 <h6 class="card-title">
498 <a href="/hot-music" class="text-red font-weight-bolder">
499 <i class="fa fa-star text-orange mr-1"></i> 热门推荐
500 </a>
501 <small>
502 <a class="float-right text-muted" href="/hot-music">更多 <i class="fa fa-angle-right"></i></a>
503 </small>
504 </h6>
505 <div class="list-group list-group-flush">
506 <a href="/music/7235" title="雨爱-杨丞琳"
507 class="list-group-item list-group-item-action px-0 text-primary text-truncate d-flex justify-content-between align-items-center">
508 <span class="ml-1 text-truncate">
509 <span style="color: #336699" class="font-weight-bolder">雨爱</span>
510 -
511 <small class="text-muted">杨丞琳</small>
512 </span>
513 <i class="fa fa-play-circle" style="font-size: larger"></i>
514 </a>
515 <a href="/music/91400" title="小半-陈粒"
516 class="list-group-item list-group-item-action px-0 text-primary text-truncate d-flex justify-content-between align-items-center">
517 <span class="ml-1 text-truncate">
518 <span style="color: #CCCC00" class="font-weight-bolder">小半</span>
519 -
520 <small class="text-muted">陈粒</small>
521 </span>
522 <i class="fa fa-play-circle" style="font-size: larger"></i>
523 </a>
524 <a href="/music/60432100" title="海屿你-白慕寒"
525 class="list-group-item list-group-item-action px-0 text-primary text-truncate d-flex justify-content-between align-items-center">
526 <span class="ml-1 text-truncate">
527 <span style="color: #009999" class="font-weight-bolder">海屿你</span>
528 -
529 <small class="text-muted">白慕寒</small>
530 </span>
531 <i class="fa fa-play-circle" style="font-size: larger"></i>
532 </a>
533 <a href="/music/8117" title="我怀念的-孙燕姿"
534 class="list-group-item list-group-item-action px-0 text-primary text-truncate d-flex justify-content-between align-items-center">
535 <span class="ml-1 text-truncate">
536 <span style="color: #FF6600" class="font-weight-bolder">我怀念的</span>
537 -
538 <small class="text-muted">孙燕姿</small>
539 </span>
540 <i class="fa fa-play-circle" style="font-size: larger"></i>
541 </a>
542 <a href="/music/4198" title="一路向北-周杰伦"
543 class="list-group-item list-group-item-action px-0 text-primary text-truncate d-flex justify-content-between align-items-center">
544 <span class="ml-1 text-truncate">
545 <span style="color: #CC0066" class="font-weight-bolder">一路向北</span>
546 -
547 <small class="text-muted">周杰伦</small>
548 </span>
549 <i class="fa fa-play-circle" style="font-size: larger"></i>
550 </a>
551 <a href="/music/4200" title="枫-周杰伦"
552 class="list-group-item list-group-item-action px-0 text-primary text-truncate d-flex justify-content-between align-items-center">
553 <span class="ml-1 text-truncate">
554 <span style="color: #FF6666" class="font-weight-bolder">枫</span>
555 -
556 <small class="text-muted">周杰伦</small>
557 </span>
558 <i class="fa fa-play-circle" style="font-size: larger"></i>
559 </a>
560 <a href="/music/62551762" title="岁月如笔写春秋-郭少杰"
561 class="list-group-item list-group-item-action px-0 text-primary text-truncate d-flex justify-content-between align-items-center">
562 <span class="ml-1 text-truncate">
563 <span style="color: #339999" class="font-weight-bolder">岁月如笔写春秋</span>
564 -
565 <small class="text-muted">郭少杰</small>
566 </span>
567 <i class="fa fa-play-circle" style="font-size: larger"></i>
568 </a>
569 <a href="/music/4197" title="花海-周杰伦"
570 class="list-group-item list-group-item-action px-0 text-primary text-truncate d-flex justify-content-between align-items-center">
571 <span class="ml-1 text-truncate">
572 <span style="color: #CC3399" class="font-weight-bolder">花海</span>
573 -
574 <small class="text-muted">周杰伦</small>
575 </span>
576 <i class="fa fa-play-circle" style="font-size: larger"></i>
577 </a>
578 <a href="/music/6441" title="盛夏的果实-莫文蔚"
579 class="list-group-item list-group-item-action px-0 text-primary text-truncate d-flex justify-content-between align-items-center">
580 <span class="ml-1 text-truncate">
581 <span style="color: #FF33CC" class="font-weight-bolder">盛夏的果实</span>
582 -
583 <small class="text-muted">莫文蔚</small>
584 </span>
585 <i class="fa fa-play-circle" style="font-size: larger"></i>
586 </a>
587 <a href="/music/11285" title="后来-刘若英"
588 class="list-group-item list-group-item-action px-0 text-primary text-truncate d-flex justify-content-between align-items-center">
589 <span class="ml-1 text-truncate">
590 <span style="color: #0099CC" class="font-weight-bolder">后来</span>
591 -
592 <small class="text-muted">刘若英</small>
593 </span>
594 <i class="fa fa-play-circle" style="font-size: larger"></i>
595 </a>
596 </div>
597 </div>
598 </div>
599
600
601 <div class="row mb-2">
602 </div>
603
604 </div>
605
606
607 <footer class="footer-modern text-center">
608 <div class="container">
609 <div class="mb-3">
610 <a href="javascript:void(0)" class="mx-2" data-toggle="modal" data-target="#feedbackModal" id="feedbackBtn"
611 rel="nofollow">
612 <i class="fa fa-commenting-o mr-1"></i>反馈建议
613 </a>
614
615 <span class="text-muted opacity-50">|</span>
616 <a href="javascript:void(0)" class="get-setting-btn mx-2" rel="nofollow" data-key="mzsm_mark">
617 <i class="fa fa-shield mr-1"></i>免责声明
618 </a>
619
620
621 <span class="text-muted opacity-50">|</span>
622 <a href="/friendly-links" class="mx-2">
623 <i class="fa fa-link mr-1"></i>友情链接
624 </a>
625
626 </div>
627
628
629 <div class="text-muted small">
630 <a href="/" class="text-decoration-none">
631 歌曲宝 - gequbao.com © 2026
632 </a>
633
634 <span class="mx-1 opacity-50">|</span>
635
636 <a href="javascript:void(0)" class="text-decoration-none js-add-bookmark" title="加入书签">
637 <i class="fa fa-star text-warning"></i> 收藏本站
638 </a>
639 </div>
640 </div>
641
642
643 <span style="display: none">
644
645 <script> var _mtj = _mtj || []; (function () { var mtj = document.createElement("script"); mtj.src = "https://node31.aizhantj.com:21233/tjjs/?k=t8h1bavogbi"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(mtj, s); })(); </script>
646 </span>
647 </footer>
648
649 <div class="modal fade" id="feedbackModal" tabindex="-1" role="dialog" aria-labelledby="feedbackModalLabel"
650 aria-hidden="true">
651 <div class="modal-dialog modal-dialog-centered" role="document">
652 <div class="modal-content border-0 shadow-lg">
653 <div class="modal-header border-0 pb-0">
654 <h5 class="modal-title font-weight-bold text-primary" id="feedbackModalLabel">
655 <i class="fa fa-commenting-o mr-2"></i>意见反馈
656 </h5>
657 <button type="button" id="feedbackModalClose" class="close text-muted" data-dismiss="modal"
658 aria-label="关闭">
659 <span aria-hidden="true">×</span>
660 </button>
661 </div>
662
663 <div class="modal-body p-4">
664 <p class="text-muted font-smaller mb-4">您的建议是我们进步的动力<b class="text-muted">(如需提交截图信息请登录后进行提交)</b>。</p>
665
666 <div class="form-group mb-3">
667 <label for="feedbackType" class="font-weight-bold small text-secondary">反馈类型</label>
668 <div class="input-group">
669 <div class="input-group-prepend">
670 <span class="input-group-text bg-light border-right-0"><i
671 class="fa fa-list-ul text-secondary"></i></span>
672 </div>
673 <select class="form-control border-left-0 focus-shadow" id="feedbackType" required>
674 <option value="" selected disabled>请选择反馈类型</option>
675 <option value="function">功能建议(网站功能改进建议)</option>
676 <option value="music">歌曲收录(请提供歌手和歌名)</option>
677 <option value="other">其他</option>
678 </select>
679 </div>
680 </div>
681
682 <div class="form-group mb-3" id="link-group" style="display: none">
683 <label for="link" class="font-weight-bold small text-secondary">错误链接</label>
684 <div class="input-group">
685 <div class="input-group-prepend">
686 <span class="input-group-text bg-light border-right-0"><i
687 class="fa fa-link text-secondary"></i></span>
688 </div>
689 <input type="text" class="form-control border-left-0 focus-shadow" placeholder="请输入出错的网址链接"
690 id="link">
691 </div>
692 </div>
693
694 <div class="form-group mb-3">
695 <label for="feedbackContent" class="font-weight-bold small text-secondary">反馈内容</label>
696 <div class="input-group">
697 <div class="input-group-prepend">
698 <span class="input-group-text bg-light border-right-0"
699 style="align-items: flex-start; padding-top: 0.7rem;"><i
700 class="fa fa-pencil text-secondary"></i></span>
701 </div>
702 <textarea class="form-control border-left-0 focus-shadow" id="feedbackContent" rows="4" required
703 placeholder="请详细描述您遇到的问题或建议..."
704 style="resize: none;"></textarea>
705 </div>
706 </div>
707
708 <div class="form-group mb-4">
709 <label for="feedbackContact" class="font-weight-bold small text-secondary">联系邮箱</label>
710 <div class="input-group">
711 <div class="input-group-prepend">
712 <span class="input-group-text bg-light border-right-0"><i
713 class="fa fa-envelope-o text-secondary"></i></span>
714 </div>
715 <input type="email" class="form-control border-left-0 focus-shadow" id="feedbackContact"
716 placeholder="example@email.com" required>
717 </div>
718 <small class="form-text text-muted">我们将通过邮件向您同步处理结果。</small>
719 </div>
720
721 <div class="d-flex justify-content-between align-items-center mt-2">
722 <button type="button" id="feedbackResetFormBtn" class="btn btn-link text-muted pl-0"
723 style="text-decoration: none;">
724 <i class="fa fa-refresh mr-1"></i>清空
725 </button>
726 <button type="button" id="feedbackSubmitFormBtn" class="btn btn-primary px-4 shadow-sm"
727 style="border-radius: 2rem;">
728 <i class="fa fa-paper-plane mr-1"></i> 提交反馈
729 </button>
730 </div>
731 </div>
732 </div>
733 </div>
734 </div>
735
736
737
738 <script type="text/javascript">
739 window.appData = JSON.parse('{\u0022mp3_id\u0022:435890,\u0022play_id\u0022:\u0022eyJpdiI6IklwNG9mUDl2MHVldUJ4bFE4SW84V3c9PSIsInZhbHVlIjoiUitzRFBzUU51RnBPcm82cG1WOFQrMFB6aFRSR1BwZDJpNnc2VElOTlQ5RDd3VkNVcDQrMnRVRlpObnUzYWpWRG5Yc3h1TWRsaGxJYUpYUDNoNmIyV0E9PSIsIm1hYyI6IjBjMTg4ZDhjZGNlNDkzYTdlNjQ1NDgwMmU3OTFjZGFkZTRmZDU0Yjc3MTViOGFjZTM5OWQxMThjYTczMTk1ZjEiLCJ0YWciOiIifQ==\u0022,\u0022mp3_title\u0022:\u0022\\u5927\\u6d77\\u822a\\u884c\\u9760\\u8235\\u624b\u0022,\u0022mp3_author\u0022:\u0022\\u4e2d\\u56fd\\u4eba\\u6c11\\u89e3\\u653e\\u519b\\u519b\\u4e50\\u56e2\u0022,\u0022mp3_type\u0022:0,\u0022mp3_cover\u0022:\u0022http:\\\/\\\/img1.kuwo.cn\\\/star\\\/albumcover\\\/500\\\/56\\\/49\\\/1656485620.jpg\u0022,\u0022mp3_duration\u0022:\u002201:44\u0022,\u0022mp3_extra_urls\u0022:[{\u0022id\u0022:1985721,\u0022share_link\u0022:\u0022aHR0cHM6Ly9wYW4ucXVhcmsuY24vcy8wNjhjNjQ1ZmRjYmI=\u0022,\u0022pwd\u0022:null,\u0022type\u0022:\u0022\\u5938\\u514b\\u7f51\\u76d8\u0022,\u0022color\u0022:\u0022#118AB2\u0022,\u0022compel_wap\u0022:false,\u0022icon\u0022:\u0022\\\/static\\\/img\\\/quark-icon.png\u0022}],\u0022ap_preload\u0022:\u0022metadata\u0022,\u0022is_robot\u0022:false,\u0022extra_url_compel\u0022:false,\u0022lrc_is_empty\u0022:true,\u0022ad_type\u0022:1,\u0022extra_recommend_wap_url\u0022:\u0022aHR0cHM6Ly9wYW4ucXVhcmsuY24vcy8wNjhjNjQ1ZmRjYmI=\u0022,\u0022vip_down_url\u0022:true}');
740 </script>
741
742 <link rel="preload" as="style" href="https://www.gequbao.com/build/assets/app-C-B9Q8LU.css" /><link rel="modulepreload" as="script" href="https://www.gequbao.com/build/assets/app-CV03aXhW.js" /><link rel="modulepreload" as="script" href="https://www.gequbao.com/build/assets/helper-CCnQIFFp.js" /><link rel="stylesheet" href="https://www.gequbao.com/build/assets/app-C-B9Q8LU.css" /><script type="module" src="https://www.gequbao.com/build/assets/app-CV03aXhW.js"></script>
743 <script defer src="//code.bdstatic.com/npm/jquery.qrcode@1.0.3/jquery.qrcode.min.js"></script>
744 <link rel="modulepreload" as="script" href="https://www.gequbao.com/build/assets/music-0Vjloyl4.js" /><link rel="modulepreload" as="script" href="https://www.gequbao.com/build/assets/helper-CCnQIFFp.js" /><link rel="modulepreload" as="script" href="https://www.gequbao.com/build/assets/download-helper-BYaTCaYd.js" /><script type="module" src="https://www.gequbao.com/build/assets/music-0Vjloyl4.js"></script>
745
746 <button id="backToTop" class="back-to-top" title="回到顶部" aria-label="回到顶部">
747 <i class="fa fa-arrow-up"></i>
748 </button>
749
750 </body>
751 </html>
View Code
对比一下:
什么!mp3地址消失了!我们只能解释:这个mp3资源地址是js当场渲染的。其实,requests这种低级爬虫只能得起骨架html,不可得起血肉------网站资源!
那么这种js渲染血肉的网站,要怎么爬取呢?
诶!我们可以用程序模拟浏览器访问该网页,让js渲染完后直接监听network里的mp3资源请求!这样简单高效,易于爬取。(当然你也可以在js渲染完后直接获取其填充血肉后的html,也可以达到目的,这里我的思路是监听资源请求截获其地址)。这就是selenium和playwright干的活了------------模拟浏览器来应对js渲染页面。
因此我们可以写一个简单的程序:
1 from playwright.sync_api import sync_playwright
2 import requests
3 import os
4 import time
5 import re
6
7 headers = {
8 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36"
9 }
10
11 browser = p.chromium.launch(headless=False)#打开浏览器
12 context = browser.new_context(
13 user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36[reference:3]")#明确UA
14 page = context.new_page()#打开新标签页
15 with page.expect_request(re.compile(r'\.mp3|from=vip'), timeout=8000) as capture:#监听正则符合r'\.mp3|from=vip'的网址,大家可以去自己总结一下规律,允许延迟8秒之内
16 page.goto("具体音乐地址,自己改一下")#去该音乐网址
17 time.sleep(1.5)#休息一下,速度不要太快被网页当做爬虫,为了逼真
18 page.click("text=播放", timeout=5000, force=False)#播放音乐,让音乐文件加载
19 request = capture.value#提取请求核心信息
20 print(request.url)
21 result1 = requests.get(request.url, headers=headers)#访问音乐地址
22 with open(f"{os.path.expanduser("~/Music/")}{search}/{i}.mp3", "wb") as p:#大家存储地址可以改
23 p.write(result1.content)#写入爬取音乐文件的具体信息
24 print("欧耶!")
有几个问题:
1、我们不是很逼真,有的时候网站怀疑我们会索要验证码,类似下方:
2、有的时候网站略怀疑我们,会故意"磨洋工"------js死活不加载渲染音乐的地址,压根爬不了
这时你肯定会报playwright的timeout错误------------这不是python内置的timeout错误,这是playwright独有的。
那我们可以异常处理,分三层:先看看能否一次成功;再看网页如果"磨洋工"就刷新一下重新加载,同时达到逼近真人的效果(真人遇到这种情况一定会刷新网页);最后如果前面两次不成功,那一定是网站索要验证码,那我们就利用ddddocr这个库解决这种简单验证码。
我简单改改,顺便封装成函数形式,加上爬取歌词的功能,而且在执行前先试着用一下requests,看看网页有没有可能犯傻让我们捡便宜:
1 import time
2 from playwright.sync_api import sync_playwright
3 from playwright._impl._errors import TimeoutError as PlaywrightTimeoutError
4 import re
5 import requests
6 import base64
7 import ddddocr
8 import os
9
10 #爬取音乐
11 def get_music(search, list_songs):
12 for i in list_songs:
13 resp = requests.get(list_songs[i], headers=headers)
14 html = etree.HTML(resp.text)
15 location = html.xpath("//audio[@id='custom-audio-player']/@src")
16 if location == []:
17 with sync_playwright() as p:
18 browser = p.chromium.launch(headless=True)
19 context = browser.new_context(
20 user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36[reference:3]")
21 page = context.new_page()
22 try:
23 with page.expect_request(re.compile(r'\.mp3|from=vip'), timeout=8000) as capture:
24 page.goto(list_songs[i])
25 time.sleep(1.5)
26 page.click("text=播放", timeout=5000, force=False)
27 request = capture.value
28 print(request.url)
29 result1 = requests.get(request.url, headers=headers)
30 with open(f"{os.path.expanduser("~/Music/")}{search}/{i}.mp3", "wb") as p:
31 p.write(result1.content)
32 print("欧耶!我是第一次那一支的!")
33 html = page.content()
34 pattern = re.compile(r'<div[^>]*id="content-lrc"[^>]*>(.*?)</div>', re.DOTALL)
35 match = pattern.search(html)
36 if match:
37 raw = match.group(1)
38 # 把 <br> 替换为换行,清理多余标签(如果有)
39 lrc_text = re.sub(r'<br\s*/?>', '\n', raw)
40 lrc_text = re.sub(r'<.*?>', '', lrc_text) # 移除其他残留标签
41 with open(f"{os.path.expanduser("~/Music/")}{search}/{i}歌词.txt", "w") as p:
42 p.write(lrc_text)
43 print(f"{i}歌词已下载")
44 browser.close()
45 except PlaywrightTimeoutError:
46 try:
47 page.reload()
48 with page.expect_request(re.compile(r'\.mp3|from=vip'), timeout=8000) as capture:
49 page.goto(list_songs[i])
50 time.sleep(1.5)
51 page.click("text=播放", timeout=5000, force=False)
52 request = capture.value
53 print(request.url)
54 result = requests.get(request.url, headers=headers)
55 with open(f"{os.path.expanduser("~/Music/")}{search}/{i}.mp3", "wb") as p:
56 p.write(result.content)
57 print("欧耶!我是刷新那一支的!")
58 html = page.content()
59 pattern = re.compile(r'<div[^>]*id="content-lrc"[^>]*>(.*?)</div>', re.DOTALL)
60 match = pattern.search(html)
61 if match:
62 raw = match.group(1)
63 # 把 <br> 替换为换行,清理多余标签(如果有)
64 lrc_text = re.sub(r'<br\s*/?>', '\n', raw)
65 lrc_text = re.sub(r'<.*?>', '', lrc_text) # 移除其他残留标签
66 with open(f"{os.path.expanduser("~/Music/")}{search}/{i}歌词.txt", "w") as p:
67 p.write(lrc_text)
68 print(f"{i}歌词已下载")
69 browser.close()
70 except PlaywrightTimeoutError:
71 page.goto(list_songs[i])
72 time.sleep(1.5)
73 page.click("text=播放", timeout=5000, force=False)
74 page.wait_for_selector("input[placeholder^='请输入验证码']", timeout=3000)
75 img_el = page.locator("img[id^='play-captcha-img-']")
76 src = img_el.get_attribute("src")
77 ocr = ddddocr.DdddOcr()
78 base64_data = src.split(",")[1]
79 img_bytes = base64.b64decode(base64_data)
80 code = ocr.classification(img_bytes)
81 time.sleep(1)
82 page.fill("xpath=//input[@placeholder='请输入验证码']", code)
83 time.sleep(1)
84 page.click("xpath=//button[@class='btn btn-blue']")
85 time.sleep(1)
86 with page.expect_request(re.compile(r'\.mp3|from=vip'), timeout=8000) as capture:
87 page.goto(list_songs[i])
88 page.click("text=播放", timeout=5000, force=False)
89 request = capture.value
90 print(request.url)
91 result = requests.get(request.url, headers=headers)
92 with open(f"{os.path.expanduser("~/Music/")}{search}/{i}.mp3", "wb") as p:
93 p.write(result.content)
94 print("欧耶!我是验证码那一支的!")
95 html = page.content()
96 pattern = re.compile(r'<div[^>]*id="content-lrc"[^>]*>(.*?)</div>', re.DOTALL)
97 match = pattern.search(html)
98 if match:
99 raw = match.group(1)
100 # 把 <br> 替换为换行,清理多余标签(如果有)
101 lrc_text = re.sub(r'<br\s*/?>', '\n', raw)
102 lrc_text = re.sub(r'<.*?>', '', lrc_text) # 移除其他残留标签
103 with open(f"{os.path.expanduser("~/Music/")}{search}/{i}歌词.txt", "w") as p:
104 p.write(lrc_text)
105 print(f"{i}歌词已下载")
106 browser.close()
107 time.sleep(1.5)
108 else:
109 result = requests.get(location[0], headers=headers)
110 with open(f"{os.path.expanduser("~/Music/")}{search}/{i}.mp3", "wb") as p:
111 p.write(result.content)
112 print("欧耶!我是html那一支的!")
113 pattern = re.compile(r'<div[^>]*id="content-lrc"[^>]*>(.*?)</div>', re.DOTALL)
114 match = pattern.search(resp.text)
115 if match:
116 raw = match.group(1)
117 # 把 <br> 替换为换行,清理多余标签(如果有)
118 lrc_text = re.sub(r'<br\s*/?>', '\n', raw)
119 lrc_text = re.sub(r'<.*?>', '', lrc_text) # 移除其他残留标签
120 with open(f"{os.path.expanduser("~/Music/")}{search}/{i}歌词.txt", "w") as p:
121 p.write(lrc_text)
122 print(f"{i}歌词已下载")
这里base64_data = src.split(",")1是为了取验证码图片主要信息,舍弃头部文件属性;img_bytes是把64位图像信息格式化一下,从而直接喂ddddocr获得验证码。
我们补写一下其他必要的函数:
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.edge.options import Options
from playwright.sync_api import sync_playwright
from playwright._impl._errors import TimeoutError as PlaywrightTimeoutError
import re
from lxml import etree
import requests
import base64
import ddddocr
import os
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36"
}
def make_dir(search):#创建歌曲文件夹
folder_path = f"{os.path.expanduser("~/Music/")}{search}"
if not os.path.exists(folder_path):
os.makedirs(folder_path)
print("目录已创建")
else:
pass
def search_content(search):#查找歌曲目录
edge_options = Options()
edge_options.add_argument("--headless=new")
edge_options.add_argument('--window-size=1920,1080')
browser1 = webdriver.Edge(options=edge_options)#打开 Safari 浏览器
browser1.get("https://www.xxxx.com")#打开歌曲包,自己改一下网址
#输入
input_option =browser1.find_element(By.XPATH, "//input[@class='form-control text-center border-primary rounded shadow-sm s-input-ele js-search-input']")
input_option.send_keys(search)
#访问
input_option.send_keys(Keys.RETURN)
time.sleep(2)
#歌曲列表
list_music = dict()
get_locations = browser1.find_elements(By.XPATH, "//div[@class='card-text']/div[@class='row no-gutters py-2d5 border-top align-items-center']/div[@class='col-9 col-md-8']/a")
for i in get_locations:
list_music[i.get_attribute("title")] = i.get_attribute("href")
print(list_music)
browser1.close()
return list_music
这用了selenium,抱歉,因为最先写search_content,还没想到用playwright。
gui.py的文件我也给你们吧!
# gui.py
import tkinter as tk
from tkinter import scrolledtext, messagebox, Listbox, MULTIPLE, EXTENDED
import threading
import sys
from io import StringIO
import get # 导入你的 get.py 模块
class MusicDownloaderGUI:
def __init__(self, root):
self.root = root
root.title("音乐下载器 - 歌曲宝")
root.geometry("650x600")
# ---- 搜索区域 ----
tk.Label(root, text="请输入搜索关键词:", font=("微软雅黑", 12)).pack(pady=5)
self.entry = tk.Entry(root, width=40, font=("微软雅黑", 12))
self.entry.pack(pady=5)
self.entry.bind('<Return>', lambda e: self.search_songs())
self.search_btn = tk.Button(root, text="搜索歌曲", command=self.search_songs, bg="#2196F3", fg="white", font=("微软雅黑", 12))
self.search_btn.pack(pady=5)
# ---- 歌曲列表(支持多选) ----
tk.Label(root, text="歌曲列表(按住 Ctrl 或 Shift 多选):", font=("微软雅黑", 10)).pack(pady=5)
frame = tk.Frame(root)
frame.pack(padx=10, pady=5, fill=tk.BOTH, expand=True)
self.listbox = Listbox(frame, selectmode=EXTENDED, height=10, font=("微软雅黑", 10))
self.listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar = tk.Scrollbar(frame, orient=tk.VERTICAL, command=self.listbox.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.listbox.config(yscrollcommand=scrollbar.set)
# ---- 下载按钮 ----
self.download_btn = tk.Button(root, text="下载选中的歌曲", command=self.download_selected, bg="#4CAF50", fg="white", font=("微软雅黑", 12), state=tk.DISABLED)
self.download_btn.pack(pady=5)
# ---- 日志文本框 ----
tk.Label(root, text="运行日志:", font=("微软雅黑", 10)).pack(pady=2)
self.log_text = scrolledtext.ScrolledText(root, wrap=tk.WORD, width=70, height=15, font=("Consolas", 9))
self.log_text.pack(padx=10, pady=5, fill=tk.BOTH, expand=True)
# ---- 状态栏 ----
self.status_var = tk.StringVar()
self.status_var.set("就绪")
self.status_bar = tk.Label(root, textvariable=self.status_var, bd=1, relief=tk.SUNKEN, anchor=tk.W)
self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
# 存储搜索结果
self.songs_dict = {}
self.keyword = ""
# 重定向 stdout 到日志
self.stdout_redirect = StringIO()
sys.stdout = self.stdout_redirect
self.update_log()
def update_log(self):
"""定期刷新日志显示"""
content = self.stdout_redirect.getvalue()
if content:
self.log_text.insert(tk.END, content)
self.log_text.see(tk.END)
self.stdout_redirect.truncate(0)
self.stdout_redirect.seek(0)
self.root.after(200, self.update_log)
def log(self, msg):
"""直接写入日志(用于非 print 信息)"""
self.log_text.insert(tk.END, msg + "\n")
self.log_text.see(tk.END)
def search_songs(self):
"""搜索歌曲并填充列表"""
keyword = self.entry.get().strip()
if not keyword:
messagebox.showwarning("提示", "请输入搜索关键词")
return
self.search_btn.config(state=tk.DISABLED)
self.download_btn.config(state=tk.DISABLED)
self.listbox.delete(0, tk.END)
self.log_text.delete(1.0, tk.END)
self.log(f"正在搜索:{keyword}")
# 后台执行搜索
def task():
try:
self.songs_dict = get.search_content(keyword)
self.keyword = keyword
if not self.songs_dict:
self.log("未找到任何歌曲")
else:
for title in self.songs_dict.keys():
self.listbox.insert(tk.END, title)
self.log(f"找到 {len(self.songs_dict)} 首歌曲")
self.root.after(0, lambda: self.download_btn.config(state=tk.NORMAL))
except Exception as e:
self.log(f"搜索出错:{e}")
finally:
self.root.after(0, lambda: self.search_btn.config(state=tk.NORMAL))
threading.Thread(target=task, daemon=True).start()
def download_selected(self):
"""下载选中的歌曲"""
selected_indices = self.listbox.curselection()
if not selected_indices:
messagebox.showinfo("提示", "请至少选择一首歌曲")
return
# 构建选中歌曲字典
selected_titles = [self.listbox.get(idx) for idx in selected_indices]
selected_dict = {title: self.songs_dict[title] for title in selected_titles}
self.download_btn.config(state=tk.DISABLED)
self.search_btn.config(state=tk.DISABLED)
self.log(f"开始下载 {len(selected_dict)} 首歌曲...")
# 创建文件夹(确保存在)
try:
get.make_dir(self.keyword) # 调用你的创建目录函数
except Exception as e:
self.log(f"创建目录失败:{e}")
# 后台执行下载
def task():
try:
# 调用 get_music,传入选中的字典
get.get_music(self.keyword, selected_dict)
self.log("所有选中歌曲处理完毕!")
except Exception as e:
self.log(f"下载过程出错:{e}")
finally:
self.root.after(0, lambda: self.download_btn.config(state=tk.NORMAL))
self.root.after(0, lambda: self.search_btn.config(state=tk.NORMAL))
threading.Thread(target=task, daemon=True).start()
if __name__ == "__main__":
root = tk.Tk()
app = MusicDownloaderGUI(root)
root.mainloop()
好了~程序结束~,希望你可以利用这12.88kb左右的文件,下载 nGB 的音乐与歌词!希望我的高中生活在音乐沐浴中扬帆起航!