网易云音乐批量下载工具编写过程


最近编写了一个 网易云音乐下载工具,感觉效果不错,就造成了这期博客的诞生。详细的源代码和可执行程序在github上面下载: https://github.com/Lmh-java/163MusicDownloaderV1.1 喜欢的star fork 一下。总的来说技术难点分为两部分:1.爬虫 2.下载IO操作

part 1 软件的分层以及准备工作

   软件分为四个package 分别是 main(用来存储主要的类) view (存储GUI类)util(工具类的存放) model(存储内部使用的模型类)
   大致可以分为4层。
上图
类和包的架构

   调用到的外部类库:
commons.jar
dom4j.jar
fastjson.jar(因为涉及到json和xml数据结构所以都要用)
httpClient.jar
httpcore.jar
httpmime.jar
Jsoup.jar
…(还有一些无关紧要的jar包)
直接上图

有一些jar包我也不太清楚是哪里来的。。。
有一些包已经弃用了,以后会优化代码并且删除。

part 2 软件核心工作流程

先上流程图,后面会阐述关键API1、2和关键代码。

Created with Raphaël 2.2.0 开始下载 构造包访问API 1 获取歌单信息(所有歌曲id) 根据歌曲id再次调用API 2 获取单曲下载地址 发送给下载模块进行下载 End

part3 API 1、2介绍

本软件最重要的部分就是网络API部分,大家可以自己调用这两个api。

API 1

http://music.163.com/api/playlist/detail?id=
功能:通过歌单id解析获取歌曲id
参数:id=歌单id
调用方法:get 就可以了

上代码

public static ArrayList<Music> crawl(String listId,String cookie) throws ParseException, IOException {
		CloseableHttpClient httpclient = HttpClients.createDefault();
		CloseableHttpResponse response = null;
		HttpGet httpGet = new HttpGet("http://music.163.com/api/playlist/detail?id=" + listId);
		// httpGet.addHeader("Referer", "http://music.163.com/");
		httpGet.addHeader("User-Agent",
				"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36");
		try {
			response = httpclient.execute(httpGet);
		} catch (IOException e) {
			//处理异常
		}
		HttpEntity entity = response.getEntity();
		if (entity != null) {
			String json = EntityUtils.toString(entity, "utf-8").toString();
			JSONObject jsStr = JSON.parseObject(json);
			JSONObject results = jsStr.getJSONObject("result");
			JSONArray tracks = results.getJSONArray("tracks");
			for(int x=0;x<tracks.size();x++){
				String name = (String)tracks.getJSONObject(x).getString("name");
				String id = (String)tracks.getJSONObject(x).getString("id");
				result.add(new Music(id,name));
				}
			}
		return result;
	}

主要就是get这个API然后通过返回的json来解析id
这里使用了一个json格式化校验小工具,向大家推荐一下:https://www.json.cn/
由于代码太长,无法所见,大家自己点进去看一下:http://music.163.com/api/playlist/detail?id=519471484
json数据展示
在result的tracks下面储存的全部都是歌曲信息,包括我们需要的id信息。

API 2

地址:http://music.163.com/weapi/song/enhance/player/url?csrf_token=7c8c959657fc0ee7f906a43143bf124
提交方法:POST
参数:cookie (可以抓包去自己获取) encSecKey params …
功能:获取歌曲对应的真实下载地址

		//a为歌曲id
		a = a.split(":::")[0];
		CloseableHttpClient httpclient = HttpClients.createDefault();
		CloseableHttpResponse response = null;
		String first_param = "{ids:\"[" + a + "]\", br: \"320000\", csrf_token:\"\"}";
		String secKey = "FFFFFFFFFFFFFFFF";
		// 两遍ASE加密
		String encText = aesEncrypt(aesEncrypt(first_param, "0CoJUm6Qyw8W8jud"), secKey);
		String encSecKey = rsaEncrypt();
		HttpPost httpPost = new HttpPost(
				"http://music.163.com/weapi/song/enhance/player/url?csrf_token=7c8c959657fc0ee7f906a43143bf124c");
		httpPost.addHeader("Referer", "http://music.163.com/");
		httpPost.addHeader("User-Agent",
				"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36");
		httpPost.addHeader("Cookie",cookie);
		List<NameValuePair> ls = new ArrayList<NameValuePair>();
		ls.add(new BasicNameValuePair("params", encText));
		ls.add(new BasicNameValuePair("encSecKey", encSecKey));
		UrlEncodedFormEntity paramEntity = new UrlEncodedFormEntity(ls, "utf-8");
		httpPost.setEntity(paramEntity);
		response = httpclient.execute(httpPost);
		HttpEntity entity = response.getEntity();
		if (entity != null) {
			String json = EntityUtils.toString(entity, "utf-8").toString();
			JSONObject jsStr = JSON.parseObject(json);
			String json1 = jsStr.getString("data").replace("[", "").replace("]", "");
			JSONObject jsStr1 = JSON.parseObject(json1);
			result = jsStr1.getString("url");
		}
		response.close();
		httpclient.close();
		return result;
	}
	public static String aesEncrypt(String src, String key) throws Exception {
		String encodingFormat = "UTF-8";
		String iv = "0102030405060708";
		Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
		byte[] raw = key.getBytes();
		SecretKeySpec secretKeySpec = new SecretKeySpec(raw, "AES");
		IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
		
		// 使用CBC模式,需要一个向量vi,增加加密算法强度
		cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
		byte[] encrypted = cipher.doFinal(src.getBytes(encodingFormat));
		return new BASE64Encoder().encode(encrypted);
		
	}

	public static String rsaEncrypt() {
		String secKey = "257348aecb5e556c066de214e531faadd1c55d814f9be95fd06d6bff9f4c7a41f831f6394d5a3fd2e3881736d94a02ca919d952872e7d0a50ebfa1769a7a62d512f5f1ca21aec60bc3819a9c3ffca5eca9a0dba6d6f7249b06f5965ecfff3695b54e1c28f3f624750ed39e7de08fc8493242e26dbc4484a01c76f739e135637c";
		return secKey;

	}
	
	public static ArrayList<Music> doPost(ArrayList<Music> name,String cookie) throws Exception{
		for(int x=0;x<name.size();x++){
			String url =api(name.get(x).getId(),cookie);
			name.get(x).setUrl(url);
		}
		return name;
	}

大概的原理就是通过api获取的参数进行几次加密,具体的过程我只会分析enctype这个参数,params参数是我看了一个大佬的文章做出来的:http://element-ui.cn/news_show_25945.shtml

这次的博客大概就这样了吧,有bug或者提问可以在github https://github.com/Lmh-java/163MusicDownloaderV1.1 或者本文评论里面留言,谢谢,希望有更多大佬来指点迷津。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值