轻松批量爬上万首音乐

轻松批量爬上万首音乐

点点

2021-05-08 20:10 阅读 214 喜欢 0

     从豆瓣FM抓音乐主要是看网站的音乐获取方式,有的可能是异步有的可能是同步,当然大部分还是异步的,有些可能数据要求比较严格,会有一些校验等等会很麻烦,不过还好,找到了一个比较简单的请求接口,可以获取下一首歌曲。

构思

    抓音乐也可以对音乐进行分析,所以准备把从豆瓣上获得的所有信息都存储起来,这里使用的是Mysql数据库,结构也比较简单,有四个表:音乐、歌手、频道、专辑。

      由于接口是下一首,而且是随机的,所以有很大的几率是重复的,需要在处理过程中进行去重,而且有时候一个200首歌的频道,随机半天也不一定会把所有的歌曲全部拿下来,不过考虑到会一直抓取,而且本身歌曲数量就比较多了,也没必要太过完美。

大体的思路就是:

获得豆瓣上的频道ID,可以循环,然后排除即可。 根据获得的频道ID,然后去随机获得音乐 获得音乐,去重,然后保存歌曲、歌手、专辑信息即可。 重复获取音乐,如果连续20+次获得都是重复的歌曲,或者当前频道的歌曲已经达到了90%,那么就换下一个频道。 如果频道不符合,则继续下一个频道,设定一个频道范围。比如:1-200.

module

superagent : https://www.npmjs.com/package/superagent simple-mysql-query : https://www.npmjs.com/package/simple-mysql-query

代码

/*持续抓取豆瓣的音乐并保存到数据库*/

 //https://douban.fm/j/v2/songlist/470992/?kbps=192
//根据歌单进行查找

      const superagent = require('superagent');
     const query = require('simple-mysql-query');
     const tool = require('../util');
          class DouBan {
constructor ( opts ){
    opts = opts || {}
    this.ids = [];
    this.requestOpt = {
        'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
        'Accept-Encoding':'gzip, deflate, sdch',
        'Accept-Language':'zh-CN,zh;q=0.8',
        'Cache-Control':'no-cache',
        'Connection':'keep-alive',
        'Cookie':'bid=RveZRi0m5ds; _ga=GA1.2.1305540715.1533201218; flag="ok"; ac="1533776043"; _gid=GA1.2.228834671.1533776081; _gat=1',
        'Host':'fm.douban.com',
        'Pragma':'no-cache',
        'Upgrade-Insecure-Requests':'1',
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2729.4 Safari/537.3'
    };
    /*channel*/
    this.channelUrl = 'https://fm.douban.com/j/v2/channel_info?id=';
    this.channelStart = 1;//开始channel
    this.channelEnd = 300;//结束channel
    this.channelPer = 3000;//从3000中随机,单位ms
    this.currentChannel = null;//可用的频道,每次启动的时候都自动检查一遍channel,然后更新并缓存。

    //频道相关的歌曲查询

    //请求频率
    this.existNum = 0;//如果连续50次都是已存在的歌,就切换频道
    this.channelError = 0;//超过5次获取失败,则切换频道
    this.maxErrorNum = 50;//最大失败数量

    //启动数据抓取
    this.log('启动数据抓取工具:------------------------',1);
    this.findChannel();
    this.findByChannel();

}
log(msg,iserror){
    tool.log('豆瓣:',msg,iserror);
}
//获得不同程度的频率
getPer (flag){
    if(flag == 0){
        return 30 * 1000;//失败20s再继续
    }else if(flag == 1){
        return  5000 + Math.random() * 20000;
    }else if(flag == 2){
        return 10 * 1000;
    }
}
//获得当前的channel所在的频道查询music的url
getMusicChannelUrl () {
    var that = this;
    return 'https://douban.fm/j/v2/playlist?channel='+that.currentChannel+'&kbps=128&client=s%3Amainsite%7Cy%3A3.0&app_name=radio_website&version=100&type=s&sid=382400&pt=&pb=128&apikey=';
}

//根据频道查找歌曲
findByChannel () {
    this.log('开始根据频道查询歌曲')
    var that = this;
    that.existNum ++ ;
    if(that.currentChannel){
        //1.查询歌曲信息
        superagent.get(that.getMusicChannelUrl())
        .set(that.requestOpt)
        .end( (err,res) => {
            if(err){
                that.channelError ++ ;
                that.log('根据当前可用频道获取歌曲失败,20s后重新请求',0)
                //如果报错,应该是被禁止或者网络超时
                that.log(err,0);
                //20s后重新发起
                setTimeout(function(){
                    that.findByChannel();
                    if(that.channelError > 5){
                        that.channelError = 0;
                        that.findChannel();    
                    }
                },that.getPer(0))
            }else{
                that.channelError = 0;
                that.log('获得可用歌曲,检查歌曲信息',1)
                //获得歌曲信息
                var resobj = JSON.parse(res.text);
                if(resobj.song && resobj.song.length > 0){
                    var song = resobj.song[0];
                    //1.根据歌曲ID检查歌曲是否存在,同时查询当前频道内的歌曲数量和频道歌曲总数
                    var songId = song.sid;
                    query({sql : 'select count(1) as num from music_music where sid=? ',params : [songId]})
                    .then( rs => {
                        var rst = rs[0];
                        if(rst[0].num > 0){//
                            return 0;
                        }else{
                            //检查专辑
                            if(song.release && song.release.id){
                                return query({sql : 'select count(1) as num from music_album where id=?',params : [song.release.id]});
                            }else{
                                return 1;
                            }
                        }
                    }).then( rs => {
                        //处理专辑
                        if(rs=== 0){//忽略该歌曲
                            return 0;
                        }else if(rs == 1){
                            //该歌曲没有专辑
                            return 1;
                        }else{
                            var rst = rs[0];
                            if(rst[0].num > 0){
                                return 1;
                            }else{
                                //插入专辑记录
                                return query({sql : 'insert into music_album (id,link,ssid) values (?,?,?) ',params : [song.release.id,song.release.link,song.release.ssid]});
                            }
                        }
                    }).then( rs=> {
                        //处理歌手
                        if(rs === 0){
                            return 0;//忽略该歌曲
                        }else{
                            //查询歌手是否存在
                            var singers = song.singers;
                            if(singers.length > 0){
                                var sql = 'select * from music_singer where id in (';
                                var params = [];
                                for(var i=0;i<singers.length;i++){
                                    var sing = singers[i];
                                    sql += '?' + (i == singers.length -1 ? '' : ',');
                                    params.push(sing.id);
                                }
                                sql +=')'
                                return query({
                                    sql : sql,
                                    params : params
                                });
                            }else{
                                return 1;
                            }
                        }
                    }).then(rs => {
                        if(rs === 0){
                            return 0;
                        }else if(rs ===1 ){
                            return 1;
                        }else{
                            //处理歌手信息
                            var singers = rs[0] || [];
                            var hasId = {};
                            singers.forEach(function(item){
                                hasId[item.id] = true;
                            });
                            var saveSingers = [];
                            song.singers.forEach(function(item){
                                if(hasId[item.id] !== true){
                                    //不存在
                                    saveSingers.push({
                                        id : item.id,
                                        name : item.name,
                                        "name_usual":item.name_usual,
                                        region : item.region.join(','),
                                        avatar : item.avatar,
                                        genre : item.genre.join('__')
                                    });
                                }
                            })
                            if(saveSingers.length > 0){
                                var mmm = {};
                                var sql = 'insert into music_singer (id,name,avatar,name_usual,region,genre) values ';
                                var params = [];
                                for(var i=0;i<saveSingers.length;i++){
                                    var singer = saveSingers[i];
                                    if(!mmm[singer.id]){
                                        mmm[singer.id] = true;//过滤重复数据
                                        sql += (params.length > 0 ? ',' : '') + '(?,?,?,?,?,?)';
                                        params = params.concat([singer.id,singer.name,singer.avatar,singer.name_usual,singer.region,singer.genre]);
                                    }
                                }
                                return query({sql : sql,params : params})
                            }else{
                                return 1;//没有要保存的歌手信息,或者已经都保存过了
                            }
                        }
                    }).then(rs=>{
                        //开始保存歌曲
                        if(rs === 0){
                            that.log('歌曲重复,当前频道:'+that.channelNow,0);
                            return 0;
                        }else{
                            that.existNum = 0;
                            that.log('保存歌曲:'+song.title,0);
                            var sql = 'insert into music_music (sid,ssid,aid,album,albumtitle,artist,file_ext,kbps,length,picture,public_time,sha256,title,url,albumid,singerid,channelid) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';

                            var singerId= '';
                            var singers = song.singers;
                            if(singers && singers.length >0){
                                singerId = singers.map(item => {
                                    return item.id;
                                }).join(',');
                            }
                            var params = [
                                song.sid,
                                song.ssid,
                                song.aid,
                                song.album,
                                song.albumtitle,
                                song.artist,
                                song.file_ext,
                                song.kbps,
                                song.length,
                                song.picture,
                                song.public_time,
                                song.sha256,
                                song.title,
                                song.url,
                                song.release && song.release.id ? song.release.id : '',
                                singerId,
                                that.currentChannel
                            ];
                            return query({sql : sql,params : params});
                        }
                    }).then(rs=>{
                        //处理歌曲数量信息
                        return query([
                        {
                            sql : 'select count(1) as num from music_music where channelid=? ',
                            params : [that.currentChannel]
                        },{
                            sql : 'select song_num as num from music_channel where channelId=?',
                            params : [that.currentChannel]
                        }
                        ]);
                    }).then(rs=>{
                        //判断歌曲数量和已存数量
                        var rst1 = rs[0],rst2 = rs[1];
                        var num1 = rst1[0].num || 0,num2 = rst2[0].num || 0;
                        num1 = parseInt(num1,10);
                        num2 = parseInt(num2,10);
                        if((num1 > num2 - 50 && num2 > 100) || that.existNum > that.maxErrorNum){
                            that.existNum = 0;
                            //更换channel
                            that.log('当前频道歌曲数量过少,更换频道',1)
                            that.currentChannel = null;
                            that.findChannel();
                            setTimeout(function(){
                                that.findByChannel();
                            },that.getPer(1))
                        }else{
                            that.log('本次查询完毕,5s后继续重新请求当前频道',1)
                            setTimeout(function(){
                                that.findByChannel();
                            },that.getPer(1))
                        }
                    }).catch(err=>{
                        that.log(err);
                        that.log('保存歌曲信息过程中出错',0);
                        setTimeout(function(){
                            that.findByChannel();
                        },that.getPer(0))
                    })
                }else{
                    that.log('获得歌曲,但无信息,重新请求',0);
                    setTimeout(function(){
                        if(that.existNum > 10){
                            that.existNum = 0;
                            that.findChannel();
                        }
                        that.findByChannel();
                    },that.getPer(1));
                }
            }
        })
    }else{
        that.log('未获得可用频道,5s后重新请求',0)
        //当前没有可用频道,则5秒后,重新查询
        setTimeout( () => {
            that.findByChannel();
        },that.getPer(1));
    }
}

//从不同类型里面持续检索歌曲,并请求,请求频率要低

updateChannel ( channelObj ){
    var that = this;
    const sqlA = {
        sql : 'select * from music_channel where channelId=?',
        params : [channelObj.channelId]
    };
    return query(sqlA).then(function(rs){
        var rst = rs[0];
        if(rst.length == 0){//不存在,插入
            var sqlB = {
                sql : 'insert into music_channel (channelId,name,intro,cover,song_num) values (?,?,?,?,?)',
                params : [channelObj.channelId,channelObj.name,channelObj.intro,channelObj.cover,channelObj.song_num]
            };
            that.currentChannel = channelObj.channelId;//更新当前可用channelID
            that.log('该频道是新频道,设为可用频道')
            return query(sqlB);
        }else{
            //判断下num
            var obj = rst[0];
            if(channelObj.song_num > obj.song_num){
                var sqlC = {
                    sql : 'update music_channel set song_num=? where channelId=?',
                    params : [channelObj.song_num,channelObj.channelId]
                };
                that.currentChannel = channelObj.channelId;//更新可用ID。
                that.log('当前频道为旧频道,但有歌曲添加,设为可用频道')
                return query(sqlC);
            }else{
                //
                that.currentChannel = channelObj.channelId;//更新可用ID。
                that.log('当前频道为旧频道,无变化,设为可用频道')
                return true;//
            }
        }
    });
}

/*从0 - 300 检查可用channel*/
findChannel () {
    this.log('开始重新查找可用频道')
    const that = this;
    if(!that.channelNow){
        that.channelNow = that.channelStart;    
    }
    if(that.channelNow >= that.channelEnd){
        //结束查询channel
        that.channelNow = that.channelStart;
    }
    superagent.get(that.channelUrl+that.channelNow).set(that.requestOpt).end(function(err,res){
        that.channelNow++;
        if(err){
            that.log('查找可用频道报错')
            that.findChannel();
        }else{
            const resObj = JSON.parse(res.text);
            const data = resObj.data;
            if(data && data.channels && data.channels.length > 0){
                const item = data.channels[0];
                const channelObj = {
                    name : item.name,
                    cover : item.cover,
                    intro : item.intro,
                    song_num : item.song_num,
                    channelId : item.id
                };
                that.log('获得可用频道准备更新数据库')
                //更新到数据库中,并判断当前频道是否可采集,如果可采集则停止,否则进入下一个循环。
                that.updateChannel(channelObj);
            }else{
                that.findChannel();
            }
        }
    })
}
     }
     module.exports = DouBan;

以上所有全部用于学习、研究使用,若有侵权请联系本站管理员删除。

转载请注明出处: http://sdxlp.cn/article/payinrue.html


如果对你有用的话,请赏给作者一个馒头吧 ...或帮点下页面底部的广告,感谢!!

赞赏支持
提交评论
评论信息(请文明评论)
暂无评论,快来快来写想法...
推荐
我们经常会想要删除很久之前微信中的聊天图片和视频,也有很多是没有被我们清除的,但是很多小伙伴们都想要知道微信怎么清理图片和视频,想要彻底删除一些好友之间的聊天照片和视频,那么下面就让点点给小伙伴们介绍一下最新的方法。
随着电子技术的不断发展,即时通许软件已经成为我们工作和生活的一部分了。那在电脑上使用微信接收的图片,都保存在哪个文件夹呢?如果想要清理这些图片的话,又有哪些微信图片的清理方法呢?
现在的社交软件有很多,但是实用面积广的就哪几个,里边QQ是最早的功能也是比较全的,QQ是现在十分常用的游戏社交软件之一,有些小伙伴不知道怎么找到手机中的QQ文件,接下来小编就给小伙伴们介绍一下具体的操作步骤。
日常生活中,咱们有好多用微信的小伙伴会收到好多好多的广告,那怎样去关闭哪?让我们减轻烦恼,跟点点来看看吧!
微信怎么设置主题背景?很多小伙伴们长时间的使用微信会觉得微信的界面和主题会有一点的单调,想要丰富一下自己的微信界面,但是很多小伙伴不知道微信怎么设置主题背景?那么下面就让点点给小伙伴们介绍一下。
现在小伙伴们都离开不了手机,它逐渐成为我们生活中必不可少的一样生活用品。很多小伙伴在用了 iPhone6 以后,遇到系统提示内存不足的问题。对于智能手机,相信小伙伴们都知道,同款机型不同内存大小,价格会相差很多,特别是对应iPhone这样的土豪手机,不同的内存版本更是相差多则上千。而很多当初选择了小内存的手机用户,发现在使用过程中彻底不够用。
今天主讲电脑版QQ音乐,给生活带来了极大的便利和乐趣,深受小伙伴们欢迎,有的小伙伴们想知道如何将电脑版QQ音乐的歌曲音乐下载到U盘里,小编为大小伙伴们解惑。
有时候抓图有点不好抓啊!现在教各位一个简单的东东。嘿