浙江新高一手把手教你 Python3 如何用 selenium 和 playwright 爬取 JS 动态渲染页面

作为浙江的新高一,即将面临严肃的住校生活。虽说杭城高中宿舍水平冠绝全国,但总是缺点什么------------没有乐趣!

所以脑洞大开的我决定偷偷带 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">&times;</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">&times;</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 &copy; 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">&times;</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 的音乐与歌词!希望我的高中生活在音乐沐浴中扬帆起航!