Nodejs 如何解决落网音乐不能下载的问题?
Nodejs 如何解决落网音乐不能下载的问题?
本人比较喜欢听音乐,尤其是上下班路上,由于公司离住的地方不远,走着上班也就40分钟,喜欢边走边听音乐,听惯了那种流行音乐,总有点泛滥的感觉,于是到处扒拉那种不一样的听歌软件(什么酷狗,酷我,虾米,咪咕之类的就不算了),找到一些比较符合自己口味的,像LavaRadio,落网 http://www.luoo.net/。第一次用落网的感觉是,哇,这个小清新居然整理了这么多优质音乐,他每一期提供的音乐风格各有不同,总有那么点让人期待的感觉,后来,后来就一直用落网了…
不过让人遗憾的是,落网的音乐不能下载,上班路上安可没有那么多的流量,虽说用落网的客户端也可以听缓存的音乐,不过断网的情况下效果还是不理想。于是就想办法折腾落网的音乐,发现一个Chrome插件:声海盗,这货可以下载落网的音乐,用过后,感觉效果还是不太好,比如下载的音乐全是01.MP3这样的,而且必须得播放时才能下载,不是很方便。
实在抵挡不住那么好的音乐的诱惑,决定自己开发个小工具来下载落网的音乐,用着方便就行了,这么想也就这么做了,刚好最近学习Nodejs,就拿Nodejs来开发了。
开工前的准备
分析了落网的页面源代码,发现最后的脚本中有个播放列表的数组(窃喜),那么开发思路就有了,这样就可以很方便拿到他的播放列表了,然后根据播放列表的信息去下载MP3就可以了。他的列表代码这这样滴:
<script>
try{
var volPlaylist = [{"id":"11999","title":"\u4e16\u754c\u662f\u5757\u5fe7\u4f24\u7684\u77f3\u5934","artist":"\u6cbc\u6cfd","album":"\u8fdc","mp3":"http:\/\/luoo.800edu.net\/low\/luoo\/radio614\/01.mp3","poster":"http:\/\/img.luoo.net\/pics\/albums\/6679\/cover.jpg_580x580.jpg","poster_small":"http:\/\/img.luoo.net\/pics\/albums\/6679\/cover.jpg_60x60.jpg"},........];
var navid = '1';
}catch(e){}
…
</script>
需要解决的问题:
- 根据期刊地址,发送http请求,从返回的html代码中拿到这段playList代码;
- 下载mp3到本地,每个期刊创建一个目录,便于组织这些音乐;
- 让用户按照自己的喜好,下载自己喜欢的音乐,也就是得有个界面让用户去选;
- 下载完以后干啥呢,听呗!,还得解决下载后播放音乐的问题。
开工
针对以上问题去研究实现方案,四处奔波后(主要在npmjs上,上面有大量的第三方包),基本解决以上问题,解决方法如下:
- 发送Http请求的问题,这个可以直接用Nodejs自带的http模块,不过简单的处理还行,复杂的处理比如下载MP3就没有那么方便了,不过幸好有个request模块解决了这个问题,发送请求特方便,下载文件同样很Easy。
- 如何从返回的html代码中获取那个playlist列表呢,jsdom+jquery去提取?算了,直接分析字符串获取吧,也不麻烦。
- 创建目录啥的就不说了,直接用自带的fs模块去搞就行了。
- 弄个界面?起初想到了node-webkit,虽然用这货虽然可以把UI做的很棒,不过有些重量级了,我还是崇尚简洁轻量的,后来找到term-list模块,可以直接在控制台中弄个简单的列表选择界面,纯文本的哦,真方便。
- 下载音乐是需要时间的,怎么才能更友好的下载呢(提示下载进度),发现request模块并没有下载进度相关的信息,后来找到request-progress可以解决这个问题,然后再从界面上显示进度,可以使用progress这个东东。
- 咋播放下载下来的音乐呢,简单点就直接使用本地的播放器播放得了,那就用open模块,可以自动调用本地app打开MP3.
万事俱备,只欠代码,来吧,开搞吧!
感觉讲代码什么的最头疼了,说几个关键的吧,其余的根据各模块的官方文档都能搞明白。
从html代码中提取想要的信息,也就是字符串截取:
function findContent(html, key, endTag, offset) {
var start = html.indexOf(key);
var end = html.indexOf(endTag, start);
return html.substring(start + key.length, end + offset);
}
然后就可以很方便的获取播放列表了:playList = JSON.parse(findContent(html, 'var volPlaylist = ', '}];', 2));
简化版的MP3下载代码:
progress(request(mp3Info.mp3)) // 请求MP3的下载地址
.on('progress', function (state) {
// 这里处理下载进度
})
.pipe(fs.createWriteStream(mp3File)) // MP3保存到本地文件
.on('close', function (err) {
// 这里处理下载结束
});
结合request和request-progress模块去下载东西还是蛮方便的,若没有这些模块,我不知道要写多少代码,向此模块的作者致敬!
其他的全是些UI方面的东西了,也没啥值得说的,下面大家可以去GitHub找到完整的代码。
贴图欣赏
程序跑起来的效果图如下,输入要获取的期刊url,就可以分析出他的下载列表,然后选择自己喜欢的音乐回车开始下载,下载完成后,再次回车就可以播放了:
luoo-down,Windowns版的截图:
MP3下载目录,每期的歌曲单独存放到自己的目录中:
问题
大家知道我们获取播放列表是从落网的网页源代码中获取的,要是哪一天落网把页面改了,找不到播放列表了,那就妥妥的悲剧了。希望落网的程序猿们不要动那段播放列表的代码哦。
其实这个问题可以分析他们客户端调用的接口,接口变动总该是少的吧,他们有个Android的客户端,可以反编译分析下接口地址,不过会麻烦些,没有折腾,现在这个版本能用就先用着吧。
开源
开发完成后,边听歌,边调试,最后搞稳定了,差不多可以分享给大家用了,就扔到github上去了,开源分享精神必须是有滴!
GitHub项目地址:https://github.com/stanzhai/luoo-down
大家有兴趣的可以加入进来一起完善哦。
总结
总结下用到的一些模块:
var fs = require('fs')
, path = require('path')
, readline = require('readline')
, request = require('request') // 非常方便的发送http请求
, progress = require('request-progress') // 处理请求进度
, ProgressBar = require('progress') // 控制台进度显示
, open = require('open') // 使用本地应用打开文件
, colors = require('colors') // 控制台文本颜色
, List = require('term-list'); // 控制台列表菜单项
相关技术点:Nodejs网页信息采集,文件下载及下载进度,控制台(终端)UI。
Nodejs 如何解决落网音乐不能下载的问题?
开工前的准备
首先,我们需要分析落网网站的页面源代码。我发现落网的页面中有一个包含播放列表的JavaScript变量 volPlaylist
,它包含了每一首歌曲的详细信息。有了这个信息,我们可以方便地提取并下载音乐。
实现方案
为了解决这个问题,我们可以采用以下步骤:
- 发送HTTP请求:获取HTML页面内容。
- 解析HTML:从中提取播放列表信息。
- 创建目录:为每期音乐创建一个独立的目录。
- 用户界面:提供一个简单的界面供用户选择要下载的音乐。
- 下载音乐:下载音乐文件,并显示下载进度。
- 播放音乐:使用本地播放器播放下载后的音乐。
关键代码示例
-
发送HTTP请求:
const request = require('request'); const url = 'http://www.luoo.net/vol/1'; // 落网期刊地址 request(url, (error, response, body) => { if (!error && response.statusCode == 200) { const playList = extractPlayList(body); // 提取播放列表 console.log(playList); } });
-
提取播放列表:
function extractPlayList(html) { const playlistStr = findContent(html, 'var volPlaylist = ', '}];', 2); return JSON.parse(playlistStr); } function findContent(html, key, endTag, offset) { var start = html.indexOf(key); var end = html.indexOf(endTag, start); return html.substring(start + key.length, end + offset); }
-
下载音乐:
const fs = require('fs'); const requestProgress = require('request-progress'); function downloadMp3(mp3Info, mp3File) { requestProgress(request(mp3Info.mp3)) .on('progress', (state) => { console.log(`Downloaded ${Math.round(state.percent * 100)}%`); }) .pipe(fs.createWriteStream(mp3File)) .on('close', (err) => { if (err) { console.error(err); } else { console.log('Download complete.'); } }); }
-
创建目录:
const path = require('path'); const createDirectory = (directory) => { if (!fs.existsSync(directory)){ fs.mkdirSync(directory); } };
-
用户界面:
const termList = require('term-list'); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); const list = new termList({ style: 'checkbox' }); rl.on('line', (line) => { const selected = list.selected(); if (selected) { const mp3Info = playList[selected.index]; downloadMp3(mp3Info, path.join(directory, `${mp3Info.title}.mp3`)); } });
-
播放音乐:
const open = require('open'); function playMp3(mp3File) { open(mp3File); }
总结
通过上述代码示例,我们可以看到如何使用Node.js来解决落网音乐不能下载的问题。通过发送HTTP请求获取HTML页面内容,解析播放列表,下载音乐文件,并提供一个简单的用户界面供用户选择和下载音乐。这个过程不仅展示了Node.js的强大功能,也体现了开源社区的合作精神。
GitHub项目地址:https://github.com/stanzhai/luoo-down
希望这个解决方案能够帮助你愉快地听音乐!
以前很喜欢他们的巴士电台,可惜现在没有了
嗯,不过现在他们的音乐也不错的
很好听
用wireshark之类的监听一下android app 对应的请求应该可以比较容易获取到对应的API接口,不需要那么麻烦得反编译吧。
不过我觉得落网这样的独立小众音乐真心挺赞的,应该尽自己所能去支持,如果觉得有问题可以想办法给他们工程师建议,比如在手机客户端中增加缓存等功能,而不是写个程序把音乐抓下来然后把代码开源。
小众的独立音乐发展不宜…
嗯,当时也考虑到了这一点,写的脚本只是方便单曲音乐下载用的,没有进行批量下载,也没有加入批量抓取期刊的功能,纯粹是方便在网页上听歌时,感觉不错的音乐,用工具下一下。
wireshark之类的监听一下android app 对应的请求,思路不错,学习了,原来我都是反编译代码,带代码中扒拉api地址,确实麻烦了。
哎呀,这个太好了,之前用python搞过一次,failed
哇塞,太棒了,可不可以来我们工作圈的Node.js实战圈子分享一下?网址是www.gongzuoquan.com,您注册好后,告诉我注册邮箱或手机号,我拉您进圈子。嘻嘻。
不错,顶一个
学习了。
学习了,支持支持!
要不要来我们工作圈的Node.js实战圈子交流一下?非常欢迎您来做客交流。 网址是www.gongzuoquan.com,您注册好后,告诉我注册邮箱或手机号,我拉您进圈子。嘻嘻。
要不要来我们工作圈的Node.js实战圈子交流一下?非常欢迎您来做客交流。 网址是www.gongzuoquan.com,您注册好后,告诉我注册邮箱或手机号,我拉您进圈子。嘻嘻。
要不要来我们工作圈的Node.js实战圈子交流一下?非常欢迎您来做客交流分享。网址是www.gongzuoquan.com,您注册好后,告诉我注册邮箱或手机号,我拉您进圈子。嘿嘿。
要不要来我们工作圈的Node.js实战圈子交流一下?非常欢迎您来做客交流分享。网址是www.gongzuoquan.com,您注册好后,告诉我注册邮箱或手机号,我拉您进圈子。嘿嘿。
学习了
今天在公司下载,发现连接timeout,最后想了下公司需要走代理,如果大家和我一样对request添加下自己的代理 request.defaults({‘proxy’:‘http://proxy.com/’})
嘻嘻!!
为了下载落网音乐,你可以利用Node.js的强大功能来解析网页内容并下载音频文件。以下是一个基于你的需求构建的小工具的简化示例,展示了如何实现这个目标:
首先,确保安装了所需的Node.js模块:
npm install request request-progress fs path
接下来是核心逻辑的代码示例:
const request = require('request');
const fs = require('fs');
const path = require('path');
// 获取HTML内容
function getHtml(url) {
return new Promise((resolve, reject) => {
request(url, (err, res, body) => {
if (err) return reject(err);
resolve(body);
});
});
}
// 解析播放列表
function parsePlaylist(html) {
const startMarker = "var volPlaylist = ";
const endMarker = "};";
const playlistStr = html.substring(html.indexOf(startMarker) + startMarker.length, html.indexOf(endMarker));
return JSON.parse(playlistStr + "}");
}
// 下载单个MP3文件
async function downloadMp3(mp3Info, saveDir) {
const savePath = path.join(saveDir, `${mp3Info.title}.mp3`);
const writeStream = fs.createWriteStream(savePath);
await new Promise((resolve, reject) => {
request(mp3Info.mp3)
.on('error', err => reject(err))
.pipe(writeStream)
.on('close', () => resolve());
});
console.log(`Downloaded ${mp3Info.title} to ${savePath}`);
}
// 主函数
async function main(url, saveDir) {
try {
const html = await getHtml(url);
const playlist = parsePlaylist(html);
playlist.forEach(mp3Info => downloadMp3(mp3Info, saveDir));
} catch (error) {
console.error("Error:", error);
}
}
main('http://www.luoo.net/vol/614', './downloads');
这段代码实现了从指定URL获取HTML,解析出播放列表,然后下载所有MP3文件到指定目录的功能。每个MP3文件将被命名为对应的标题,并保存在一个独立的子目录中。
请注意,这种方法依赖于网站的具体结构,如果落网改变了其网页结构,可能需要相应调整解析逻辑。此外,频繁爬取网站内容可能会导致IP被封禁,建议遵守网站的robots.txt规则并合理安排爬取频率。