1. 程式人生 > >Java: 根據網易雲音樂URL下載歌曲、歌詞、專輯封面和MV

Java: 根據網易雲音樂URL下載歌曲、歌詞、專輯封面和MV

先看最終效果圖:

前提請下載:

(1)jsoup-1.11.3.jar

(2)JMF 2.1.1e

然後使用開源的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):

用Java下載檔案即可。同理下載專輯封面和MV也是如此分析。

獲取歌詞網址如下:

這樣就獲得了歌詞。如果有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地址,生成歌手資訊,獲取同一張專輯中其他歌曲等),其餘程式碼就不公開了。

注:帶有版權問題的歌曲是不能下載的,畢竟又不是百度網盤(哦呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵)

在進階的路上,歡迎各位大俠指正。