Java: 根据网易云音乐URL下载歌曲、歌词、专辑封面和MV

先看最终效果图:

前提请下载:

(1)jsoup-1.11.3.jar

(2)JMF 2.1.1e

工作原理如下:输入一个网易云音乐URL,如:http://music.163.com/#/song?id=5048563

然后使用开源的JSoup分析框架源代码得到如下:

<script type="application/ld+json">
{
"@context": "https://ziyuan.baidu.com/contexts/cambrian.jsonld",
"@id": "http://music.163.com/song?id=5048563",
"appid": "1582028769404989",
"title": "I'll Be There For You",
"images": ["http://p1.music.126.net/DOjgC0ig0QrOD67aJM5kGg==/2532175279901155.jpg"],
"description": "歌手:The Rembrandts。所属专辑:Friends。",
"pubDate": "2005-10-01T00:00:00"
}
</script>

以及:

<a title="播放mv" href="/mv?id=5361246"><i class="icn u-icn u-icn-2"></i></a>

这样,就获得了该音乐的基础信息和MV地址(如果有MV的话)

比较重要的网址如下(外链MP3):

http://music.163.com/song/media/outer/url?id=5048563

自动解析后,返回下载地址:http://m10.music.126.net/20180717183319/797111ebf9f62bbb19e78e94765fb58b/ymusic/0707/bc0b/6847/809ceab63a4d5fbd953ca7ea9a240519.mp3

用Java下载文件即可。同理下载专辑封面和MV也是如此分析。

获取歌词网址如下:

http://music.163.com/api/song/media?id=5048563

这样就获得了歌词。如果有LRC歌词,则默认显示LRC,否则是普通歌词,否则没有歌词(暂无或纯音乐)。

实例歌词如下:

{"songStatus":3,"lyricVersion":9,"lyric":"[00:11.780]So no one told you life was gonna be this way:\n[00:15.920]your jobs a joke, you're broke, your love life's D.O.A.\n[00:20.880]It's like you're always stuck in second gear,\n[00:24.980]And it hasn't been your day, your week, your month, or even your year, but\n[00:30.660]I'll be there for you, when the rain starts to pour\n[00:35.590]I'll be there for you, like I've been there before\n[00:40.690]I'll be there for you, 'cause you're there for me too.\n[00:48.260]You're still in bed at ten and work began at eight\n[00:53.220]You've burned your breakfast, so far things are goin' great.\n[00:59.120]Your mother warned you there'd be days like these\n[01:03.450]Oh but she didn't tell you when the world has brought you down to your knees that\n[01:09.160]I'll be there for you, when the rain starts to pour\n[01:14.200]I'll be there for you, like I've been there before\n[01:18.300]I'll be there for you, 'cause you're there for me too.\n[01:28.350]No one could ever know me, no one could ever see me\n[01:35.190]Seems you're the only one who knows what it's like to be me.\n[01:40.250]Someone to face the day with, make it through all the rest with\n[01:46.960]Someone I'll always laugh with, even at my worst I'm best with you, yeah.\n[02:12.100]It's like you're always stuck in second year\n[02:16.300]And it hasn't been your day, your week, your month, or even your year.\n[02:23.340]I'll be there for you, when the rain starts to pour\n[02:28.340]I'll be there for you, like I've been there before\n[02:32.260]I'll be there for you, 'cause you're there for me too.\n[02:41.010]I'll be there for you, I'll be there for you,\n[02:50.870]I'll be there for you 'cause you're there for me too...\n[02:57.580]How you doin'?\n[02:58.580]\n","code":200}

需要注意的是,歌词里可能会显示“\n”或者"\r",此时就需要String.format()转换格式或将"\\r\\n"转换成"\r\n",以便保存到文件里。(庆祝微软记事本重大升级)

适当修改格式即可正常显示。效果如下:

选择适当选项即可下载。下载后:

剩下附加的诸多功能就不说了。关键代码如下:

HTTPAnalyzer.java

// Coding starts here
// Version 3.0 专用

import java.io.IOException;

import org.jsoup.Jsoup;  // jsoup-1.11.3

/**
 * HTTPAnalyzer类。分析网页框架,获得有用信息
 * @author 赵利昂
 * @since 0.5
 */
public class HTTPAnalyzer
{
    private String myURL = "";  // 用户输入的URL地址
    private String URL_id = "";  // 解析得到的ID
    private String frameURL = "";  // 网页框架地址
    private String bodyCode = "";  // 框架源代码
    private String essentialPart = "";  // 用于分析数据的部分
    private String info[];  // 歌曲信息
    private String lrc;  // LRC歌词(带时间标签,可能无歌词,需要去MainFrame判断)
    private String lrcUrl;  // LRC歌词地址
    private boolean hasMV = false;
    private String mvUrl = "NULL";  // MV地址(若无则返回“NULL”)
    
    /**
     * 获取URL框架中有用的信息
     * @param myURL 要分析的URL
     * @throws IOException
     */
    public HTTPAnalyzer(String myURL) throws IOException
    {
        MainFrame.logEvent("尝试解析网页框架地址");
        if(myURL.toLowerCase().startsWith("music.163"))
            this.myURL = "http://" + myURL;
        else
            this.myURL = myURL;
        
        this.URL_id = myURL.substring(myURL.lastIndexOf("song?id=") + 8);
        this.frameURL = "http://music.163.com/song?id=" + this.URL_id;
        this.bodyCode = Jsoup.connect(this.frameURL).timeout(3000).execute().body();
        this.essentialPart = bodyCode.substring(
                bodyCode.indexOf("<script type=\"application/ld+json\">"),
                bodyCode.indexOf("<script type=\"text/javascript\">"));
        this.lrcUrl = "http://music.163.com/api/song/media?id=" + URL_id;
        
        int[] pos = new int[7];
        pos[0] = this.essentialPart.indexOf("\"@context\": \"");
        pos[1] = this.essentialPart.indexOf("\"appid\": \"");
        pos[2] = this.essentialPart.indexOf("\"title\": \"");
        pos[3] = this.essentialPart.indexOf("\"images\": [\"");
        pos[4] = this.essentialPart.indexOf("\"description\": \"歌手:");
        pos[5] = this.essentialPart.indexOf("。所属专辑:");
        pos[6] = this.essentialPart.indexOf("\"pubDate\": \"");
        
        String[] substrings = new String[7];
        substrings[0] = this.essentialPart.substring(pos[0]);
        substrings[1] = this.essentialPart.substring(pos[1]);
        substrings[2] = this.essentialPart.substring(pos[2]);
        substrings[3] = this.essentialPart.substring(pos[3]);
        substrings[4] = this.essentialPart.substring(pos[4]);
        substrings[5] = this.essentialPart.substring(pos[5]);
        substrings[6] = this.essentialPart.substring(pos[6]);
        
        /*
         * info[0]: context <!-- @URL -->
         * info[1]: appid
         * info[2]: title
         * info[3]: images <!-- @URL -->
         * info[4]: artist
         * info[5]: album
         * info[6]: pubDate
         */
        MainFrame.logEvent("尝试返回解析内容到本地");
        this.info = new String[7];
        this.info[0] = substrings[0].substring(13, substrings[0].indexOf("\","));
        this.info[1] = substrings[1].substring(10, substrings[1].indexOf("\","));
        this.info[2] = substrings[2].substring(10, substrings[2].indexOf("\","));
        this.info[3] = substrings[3].substring(12, substrings[3].indexOf("\"],"));
        this.info[4] = substrings[4].substring(19, substrings[4].indexOf("。"));
        this.info[5] = substrings[5].substring(6, substrings[5].indexOf("。\","));
        this.info[6] = substrings[6].substring(12, substrings[6].length() - 14);
        
        int mvStart = bodyCode.indexOf("<a title=\"播放mv\" href=\"/mv?id=");
        if(mvStart != -1)
        {
            hasMV = true;
            String mvEssential = bodyCode.substring(mvStart,
                    bodyCode.indexOf("\"><i class=\"icn u-icn u-icn-2\"></i>", mvStart));
            this.mvUrl = "http://music.163.com/mv?id=" + mvEssential.substring(29);
            
            String downloadMvEssential = Jsoup.connect(this.mvUrl).timeout(100000).execute().body();
            String actualMvUrl = downloadMvEssential.substring(
                    downloadMvEssential.indexOf("http%3A%2F%2Fv4.music.126.net"),
                    downloadMvEssential.indexOf(".mp4\""));
            
            MainFrame.actualDownloadMVURL = actualMvUrl.replace("%3A", ":").replace("%2F", "/").replace("%3D", "=") + ".mp4";
        }
        else
        {
            hasMV = false;
            this.mvUrl = "NULL";
            MainFrame.actualDownloadMVURL = "NULL";
        }
        
        MainFrame.logEvent("尝试获取歌词主体");
        this.lrc = Jsoup.connect(this.lrcUrl).timeout(3000).execute().body();
        
    }
    
    public String getEssentialPart() { return this.essentialPart; }

    public String getMyURL() { return myURL; }

    public String getURL_id() { return URL_id; }

    public String getFrameURL() { return frameURL; }

    public String getBodyCode() { return bodyCode; }
    
    public String[] getInfoArray() { return info; }

    public String getLrc() { return lrc; }
    
    public String getLrcUrl() { return lrcUrl; }

    public boolean hasMusicVideo() { return hasMV; }

    public String getMvUrl() { return mvUrl; }

    @Override
    public String toString()
    {
        return "[Analytic result]\r\nContext: " + info[0] + "\r\n"
                + "AppID: " + info[1] + "\r\n"
                + "Title: " + info[2] + "\r\n"
                + "Album_img: " + info[3] + "\r\n"
                + "Artist: " + info[4] + "\r\n"
                + "Album: " + info[5] + "\r\n"
                + "PublishedDate:" + info[6] + "\r\n"
                + "HasMV: " + hasMV + "\r\n"
                + "MVURL: " + mvUrl;
    }

}

这是整个软件很小的一部分,但却是最重要的一部分(MainFrame.logEvent()只是我用于记录软件活动的方法)。软件需要继续维护和升级,还要加入很多新的功能(比如暂时设想的:获取高清或其他分辨率的MV地址,生成歌手信息,获取同一张专辑中其他歌曲等),其余代码就不公开了。

注:带有版权问题的歌曲是不能下载的,毕竟又不是百度网盘(哦呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵)

在进阶的路上,欢迎各位大侠指正。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值