最近在Github上看到一个项目,使用Vue搭建了一个Web播放器,可以播放一些需要vip的歌曲——NetEasyMusic.

想从上面下载一些杰伦的歌😁。无奈,杰伦的歌太多了,所以利用它所使用的QQ音乐Api,自己写了一个Java程序来批量下载歌曲。
本项目仅供学习使用,请尊重版权,请勿利用此项目从事商业行为! 本项目仅供学习使用,请尊重版权,请勿利用此项目从事商业行为! 本项目仅供学习使用,请尊重版权,请勿利用此项目从事商业行为!
所需技术:
- JSON对象的处理
- HttpURLConnection获取网络资源
1. QQ音乐Api
请大家移步QQ音乐Api
因为我这里的目的是下载歌曲,所以关系的接口是
- 搜索

- 下载链接

注意这里的完整链接是https://api.qq.jsososo.com/search?key=周杰伦
2. 流程

3. 实现
3.1 获取关键字
新建BootStrap类作为程序入口
1 2 3 4 5 6 7 8 9 10 11
| import java.util.Scanner;
public class BootStrap { public static void main(String[] args) throws IOException{ Scanner sc = new Scanner(System.in); System.out.println("请输入歌名或歌手关键字:"); String keyword = sc.next(); new Crawl().crawlMusic(keyword); } }
|
3.2 根据关键字构造URL
使用字符串拼接即可,代码和3.3放在一起。
3.3 从返回的JSON数据中获取歌曲的下载ID
新建Crawl类,并新建crawlMusic方法
引入jsoup包,maven设置为
1 2 3 4 5
| <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.10.2</version> </dependency>
|
这里使用jsoup获取网页响应(注意不是DOM对象),需要忽略格式,并执行execute方法,再获取response的body
1 2 3 4 5
| public void crawlMusic(String keyword) throws IOException{ String url = String url = "https://api.qq.jsososo.com/search?key=" + key; Response res = jsoup.connect(url).ignoreContentType(true).execute(); String body = res.body(); }
|
接下来需要对JSON对象进行操作,引入fastjson,maven设置为
1 2 3 4 5
| <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.28</version> </dependency>
|
将字符串转换为JSON对象,使用JSONObject静态方法parseObject
1 2 3 4 5 6 7 8
| public void crawlMusic(String keyword) throws IOException{ String url = String url = "https://api.qq.jsososo.com/search?key=" + key; Response res = jsoup.connect(url).ignoreContentType(true).execute(); String body = res.body();
JSONObject json = JSONObject.parseObject(body); }
|
到这里,先分析一下JSON数据的格式。
发现提交搜索请求之后,返回的JSON数据中,只要取data的值,再取list值就可以得到歌曲信息的JSON列表。
然后分析列表中的元素

这里先达到目的就先提取下载ID的值就可以了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public void crawlMusic(String keyword) throws IOException{ String url = String url = "https://api.qq.jsososo.com/search?key=" + key; Response res = jsoup.connect(url).ignoreContentType(true).execute(); String body = res.body();
JSONObject json = JSONObject.parseObject(body); JSONObject data = json.getJSONObject("data"); JSONArray list = data.getJSONArray("list"); for (int i = 0; i < list.size(); i++) { String downloadApi = list.getJSONObject(i).getString("songmid"); String downloadURL = downloadURL(downloadApi); downloadMusic(downloadURL); } }
|
3.4 根据下载ID构造URL下载歌曲
现在来完成downloadURL和downloadMusic两个方法。
downloadURL通过下载ID构造链接,请求到歌曲的真正下载链接。

data的值即是真正的下载链接。
1 2 3 4 5 6 7
| public String downloadURL(String downloadApi) { String downloadApi = "https://api.qq.jsososo.com/song/url?id=" + downloadApi; Response res = jsoup.connect(downloadApi).ignoreContentType(true).execute(); String body = res.body(); JSONObject json = JSONObject.parseObject(body); return json.getString("data"); }
|
根据真正下载链接下载歌曲,这里的流程是这样的
- 新建URL对象。
- 建立连接HttpURLConnection。
- 获取网络输入流。
- 通过缓存流写入到输出流当中。
- 关闭流,关闭连接。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public void downloadMusic(String url) { String path = "D:\\"; FileOutputStream fileOut = null; HttpURLConnection con = null; InputStream inputStream = null;
try { URL httpUrl = new URL(url); con = (HttpURLConnection) httpUrl.openConnection(); con.connect(); inputStream = con.getInputStream(); BufferedInputStream bis = new BufferedInputStream(inputStream); fileOut = new FileOutputStream(path+"song.mp3"); BufferedOutputStream bos = new BufferedOutputStream(fileOut); byte[] buf = new byte[4096]; int length = bis.read(buf); while (length != -1) { bos.write(buf, 0, length); length = bis.read(buf); } bos.close(); bis.close(); con.disconnect(); } catch (Exception e) { e.printStackTrace(); } finally { System.out.println("下载完毕!"); } }
|
到此,整个根据关键字下载歌曲的程序就基本写完了,可以运行BootStrap来下载歌曲啦😀
4. 优化
以上完成了下载歌曲的程序,但没有在控制台输出歌曲信息,而且我把下载歌曲的名字写死了,这样很不方便。另外也没有跳转下一页的功能,所以我优化了一下,完整代码如下。
新增功能
- 根据需要选择下载的歌曲。
- 询问是否查看下一页歌曲。
- 默认下载flac音质,没有flac源则下载mp3格式音乐。
- 设置歌曲名为下载文件名。
BootStrap.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package com.ssl;
import java.io.IOException; import java.util.Scanner;
public class Bootstrap {
public static void main(String[] args) throws IOException { Scanner sc = new Scanner(System.in); System.out.println("请输入歌名或歌手:"); String keyword = sc.next(); int pageNo = 1; while (true) { new Crawl().crawlMusic(keyword, pageNo); System.out.println("是否查看下一页歌曲?(y/n): "); String select = sc.next(); if ("y".equals(select)) { pageNo++; System.out.println("查看下一页歌曲~"); } else { System.out.println("欢迎下次再会~"); break; } }
}
}
|
Crawl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
| package com.ssl;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import org.jsoup.Connection.Response; import org.jsoup.Jsoup;
import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.util.*;
public class Crawl {
public void crawlMusic(String key, int pageNo) throws IOException { String url = "https://api.qq.jsososo.com/search?key=" + key + "&pageNo=" + pageNo; Response res = Jsoup.connect(url).ignoreContentType(true).execute(); String body = res.body(); JSONObject json = JSONObject.parseObject(body); JSONObject data = json.getJSONObject("data"); JSONArray list = data.getJSONArray("list");
Map<Integer, List> getSong = new HashMap<>();
for (int i = 0; i < list.size(); i++) { JSONObject songInfo = list.getJSONObject(i); String songName = getSongName(songInfo); String singer = getSinger(songInfo); String downloadUrl = getDownloadApi(songInfo); int sizeflac = getSizeFlac(songInfo); List<String> getSongSub = saveSong(songName, singer, downloadUrl, String.valueOf(sizeflac)); getSong.put(i+1, getSongSub); } selectMusic(getSong); }
public String getDownloadApi(JSONObject o) { String downloadApi = "https://api.qq.jsososo.com/song/url?id=" + o.getString("songmid"); return downloadApi; }
public String getSongName(JSONObject o) { return o.getString("songname"); }
public int getSizeFlac(JSONObject o) { return o.getInteger("sizeflac"); }
public String getSinger(JSONObject o) { return o.getJSONArray("singer").getJSONObject(0).getString("name"); }
public void goThroughMap(Map map) { Set<Integer> indexSet = map.keySet(); for (int index: indexSet) { System.out.println(index + ", " + map.get(index).toString()); } }
public List saveSong(String... s) { List<String> getSongSub = new ArrayList<>(); for (String s1: s) { getSongSub.add(s1); } return getSongSub; }
public String getDownloadUrl(String downloadApi) throws IOException{ Response res = Jsoup.connect(downloadApi).ignoreContentType(true).execute(); String body = res.body(); JSONObject json = JSONObject.parseObject(body); System.out.println(json.getString("data")); return json.getString("data"); }
public void selectMusic(Map map) { goThroughMap(map); Scanner sc = new Scanner(System.in); System.out.println("请输入想要下载音乐对应的序号:(如果输入0,则下载以上全部歌曲; 输入-1,则不下载当前页面歌曲。)"); int index = sc.nextInt(); if (0 < index) { download(map, index); } else if (0 == index) { for (int i = 1; i <= map.size(); i++) { download(map, i); } } else { System.out.println("不下载当前页歌曲!"); } }
public void downloadSong(String url, String songName, String format) { String path = "D:\\Media\\Music\\"; FileOutputStream fileOut = null; HttpURLConnection con = null; InputStream inputStream = null;
try { URL httpUrl = new URL(url); con = (HttpURLConnection) httpUrl.openConnection(); con.connect(); System.out.println("正在下载中..."); inputStream = con.getInputStream(); BufferedInputStream bis = new BufferedInputStream(inputStream); fileOut = new FileOutputStream(path + songName + "." + format); BufferedOutputStream bos = new BufferedOutputStream(fileOut);
byte[] buf = new byte[4096]; int length = bis.read(buf); while(length != -1) { bos.write(buf, 0, length); length = bis.read(buf); } bos.close(); bis.close(); con.disconnect();
} catch (Exception e) { e.printStackTrace(); } finally { System.out.println("下载完毕!"); } }
public void download(Map map, int index) { List songInfo = (List) map.get(index); String downloadApi = (String) songInfo.get(2); String sizeFlac = (String) songInfo.get(3); System.out.format("下载的歌曲是:%s \n", songInfo.get(0).toString()); try { if ("0".equals(sizeFlac)) { String downloadUrl = getDownloadUrl(downloadApi); System.out.println("下载mp3歌曲"); downloadSong(downloadUrl, (String) songInfo.get(0), "mp3"); } else { String downloadUrl = getDownloadUrl(downloadApi + "&type=flac"); System.out.println("下载flac歌曲"); downloadSong(downloadUrl, (String) songInfo.get(0), "flac"); }
} catch (IOException e) { e.printStackTrace(); } }
}
|
使用过程

没有采用多线程,怕大佬的服务器扛不住,也怕封我的IP😂,可以听杰伦就好啦,要什么自行车~
一定不要商用!
下载其他歌手的歌也行哈。