本文在 CSDN 同步更新
年初,我写了 Java爬取喜马拉雅非付费音频 这篇文章;后来代码一直没有维护过。前段时间,有个哥们下载了我的代码,发现运行失败,我觉得有点儿对不住这个哥们。但因为前段时间太忙,没顾得上,因此今天抽空重新研究了下。
具体前期研究过程,我就不多说了,大家可以前往 Java爬取喜马拉雅非付费音频 查看。总的来说,这篇文章和程序的目的是使用java批量下载喜马拉雅某个专辑的音频。
回顾一下:
注:albumId为10710983的专辑名为:离婚 老舍先生的中篇小说
大家可以把url拷贝到浏览器,仔细看看json数据,于是我们可以知道,第一个url可以拿到某个专辑的基本信息,从而确定页码数;第二个url可以拿到具体某一个音频的专属trackId;第三个url可以通过trackId拿到音频的网络路径。经过此三步,我们便可以下载音频到本地。
整体逻辑是没有问题的,我看了下我上一篇文章的代码。发现我获取列表的接口是下面这个:
https://www.ximalaya.com/revision/play/album
在这个接口里原本是可以直接获取到列表信息,最主要的是可以直接拿到下载路径;可是,现在这个接口返回的数据为:
[SIGN] no sign or wrong sign
这也是上面那个哥们拿不到json数据的原因。
因此,我们爬取平台的数据的代码是要经常维护的,每个平台都有可能将之前暴露对外的接口进行改造。所以,在爬虫这件事情上我们应该学习分析方法,而不是依赖于一成不变的代码。通过 抓包、分析、模拟请求、获取数据 一整套流程拿到的数据才是真的数据。
这次的改造主要体现在调用接口上。当然,为了提高效率,我把之前的单线程下载改成了多线程下载。想一睹为快的童鞋可以前往我的 git仓库 。
1.调用处修改
package temt.download;
import temt.bean.AudioBean;
import temt.util.AudioDealUtil;
/**
* @author temt
* @time 2018年12月13日21:45:34
* @time 2019年12月29日14:25:42 修改
*/
public class Main implements Runnable{
private String albumId;//专辑ID
public Main(){}
public Main(String albumId){
this.albumId = albumId;
}
@Override
public void run() {
try {
downAudio(albumId);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void downAudio(String albumId) throws InterruptedException{
//初始化音频列表,修改专辑ID便可下载该专辑的音频内容(非付费)
AudioBean audioBean = AudioDealUtil.initBean(
"https://www.ximalaya.com/revision/album",
"albumId="+albumId);
//修改下载路径
AudioDealUtil.initDownloadAudio("https://www.ximalaya.com/revision/album/v1/getTracksList",
audioBean,
"D://ximalaya03//");
}
public static void main(String[] args) throws InterruptedException{
Main thread01 = new Main("6661211");//《杉杉来吃》顾漫
Main thread02 = new Main("12040477");//人間失格
Main thread03 = new Main("31844110");//老舍名著《茶馆》地道北京话演绎
Main thread04 = new Main("10710983");//离婚 老舍先生的中篇小说
Thread th01 = new Thread(thread01);
Thread th02 = new Thread(thread02);
Thread th03 = new Thread(thread03);
Thread th04 = new Thread(thread04);
th01.start();
th02.start();
th03.start();
th04.start();
}
}
2.获取下载链接的方法修改
/**
* 初始化音频下载列表数据
* @param url
* @param audioBean
* @throws InterruptedException
*/
public static void initDownloadAudio(String url,AudioBean audioBean,String targetUrl) throws InterruptedException{
int trackTotalCount = audioBean.getTrackTotalCount();
int count = 0;
for(int j = 0 ; j < audioBean.getTotalPage() ; j++){
//参数
String param = "albumId="+audioBean.getAlbumId()+"&pageNum="+(j+1)+"&sort=-1";
String result = HttpUtil.sendGet(url,param);
JSONObject resultJson = JSONObject.fromObject(result);
JSONObject dataJson = resultJson.getJSONObject("data");
JSONArray tracksAudioPlayJSONArray = dataJson.getJSONArray("tracks");
for(int i = 0 ; i < tracksAudioPlayJSONArray.size() ; i++){
JSONObject object = tracksAudioPlayJSONArray.getJSONObject(i);
String trackName = object.getString("title");
String trackId = object.getString("trackId");
String audioFinal = HttpUtil.sendGet("https://www.ximalaya.com/revision/play/v1/audio","id="+trackId+"&ptype=1");
JSONObject audioFinalJson = JSONObject.fromObject(audioFinal).getJSONObject("data");
String downloadUrl = audioFinalJson.getString("src");
Thread.sleep(100L);
downloadAudio(downloadUrl,targetUrl,trackName,audioBean.getAlbumTitle());
count++;
System.out.println("还剩"+(trackTotalCount-count)+"条");
}
}
}
在这里,我设置了线程休眠时间为100ms。
3.增加了windows目录特殊字符替换
/**
* 特殊字符处理
* @time 2019年12月29日14:26:32
* @param fileName
* @return
*/
private static String dealFileName(String fileName){
////:*?<>|
String regEx="[////:*?<>|]";
fileName = fileName.replaceAll(regEx,"");
return fileName;
}
| 因为windows目录中不允许出现///:*?<> | 这些字符,因此将这些字符替换成空字符。 |
欢迎关注我的微信公众号: 一辈子的码农先生 ,接下来会有非常多的干货总结,这也是我对自己几年工作的一种总结和交代。谢谢大家!