QQ音乐API爬取全过程
1. 前序:
在以前的QQ音乐的API爬取中,是非常容易的,采用的是jsonp的请求方式,但现在QQ音乐请求已经全部改成XHR的ajax的方式发送请求,所以要爬取QQ音乐的数据就需要破解QQ音乐发送请求的参数。
2. 对于前端:
在前端发送XHR请求的话会有请求端跨域限制,所以对于前端不能直接发送XHR请求到QQ音乐后端,而是要自己另外搭一个服务器,用我们的服务器去向QQ音乐发送请求。
3. QQ音乐接口分析:
以获取QQ音乐的焦点图为例。
这是我们要爬取的图片。在浏览器中按F12,然后查看network的xhr请求,可以看到。
QQ音乐是从这里拿到的数据,所以对于这个接口分析。
Request Header:
在这个请求的header中,是有服务端referer和origin限制的,所以为了骗过QQ音乐的服务器,我们在我们后台发的请求的header中也要把origin和referer设置成和QQ音乐一样。
Query String Parameters:
可以看到QQ音乐的params中是有’-'和sign签名的,我们要拿到这个数据,首先就是要这两个的加密的获取方式,而在其他中,实际上都是一些静态数据,直接复制上去就好了。
4. ‘-’和Sign的获取
要知道怎么获取’-‘或者sign就必须知道这两个加密是怎么产生的,所以在浏览器的控制台中按ctrl+shift+f。
首先我们搜索recom,在’-'中recom是一个不变的量。
在ctrl+shift+f搜索到的文件中,先把它格式化,然后按ctrl+f就可以在文件中搜索我们要的数据。
在这个请求中我们可以看到recom的获取是通过。
'recom' + (Math.random() + '').replace('0.', '')
这么一个方法。所以我们这个加在请求中就可以了。
然后是sign的获取,sign的获取会些许麻烦。
用相同的方法,我们可以看到sign是从一个getSecuritySign获取的,然后这个方法使用通过一个js文件获取,链接挂在下面。
'地址y.qq.com/component/m/qmfe-security-sign/index.umd.js?max_age=2592000'
先把这个js格式化之后,可以大概的看一下这个js文件。
可以看到,封装了一个__sign_hash_20200305的function。
所以我们把这个js复制下来,放到自己的文件当中,然后自己写一个方法去获取这个sign的值,然后把这个方法导出去。
export default function getSecuritySign(data) {let str = 'abcdefghijklmnopqrstuvwxyz0123456789'let count = Math.floor(Math.random() * 7 + 10)let sign = 'zza'for (let i = 0; i < count; i++) {sign += str[Math.floor(Math.random() * 36)]}sign += window.__sign_hash_20200305('CJBPACrRuNy7' + JSON.stringify(data))return sign
}
这个data,就是我们在前面分析的Query String Parameters中的data数据。
5.代码实现
既然加密的数据我们都有自己的获取方式了,我们就可以写请求来获取数据了。
笔者是是用vue-cli加上webpack打包写的,所以我前端的代码是封装了一个js文件,然后后端使用nodejs写在webpack.dev.config.js的before(app)中。
前端:
export function getRecommendAndDicsList () {const url = '/api/getTopBanner'let dataValue = {'comm': { 'ct': 24 },'category': { 'method': 'get_hot_category', 'param': { 'qq': '' }, 'module': 'music.web_category_svr' },'recomPlaylist': {'method': 'get_hot_recommend','param': { 'async': 1, 'cmd': 2 },'module': 'playlist.HotRecommendServer'},'playlist': {'method': 'get_playlist_by_category','param': { 'id': 8, 'curPage': 1, 'size': 40, 'order': 5, 'titleid': 8 },'module': 'playlist.PlayListPlazaServer'},'new_song': { 'module': 'newsong.NewSongServer', 'method': 'get_new_song_info', 'param': { 'type': 5 } },'new_album': {'module': 'newalbum.NewAlbumServer','method': 'get_new_album_info','param': { 'area': 1, 'sin': 0, 'num': 10 }},'new_album_tag': { 'module': 'newalbum.NewAlbumServer', 'method': 'get_new_album_area', 'param': {} },'toplist': { 'module': 'musicToplist.ToplistInfoServer', 'method': 'GetAll', 'param': {} },'focus': { 'module': 'music.musicHall.MusicHallPlatform', 'method': 'GetFocus', 'param': {} }}let sign = getSecuritySign(dataValue)const data = Object.assign({}, commonParams, {'-': 'recom' + (Math.random() + '').replace('0.', ''),platform: 'yqq.json',loginUin: 0,hostUin: 0,sign: sign,needNewCode: 0,inCharset: 'utf8',format: 'json',data: dataValue})return axios.get(url, {params: data}).then((res) => {return Promise.resolve(res.data)})
}
在这里是请求我们后端的一个/api/getTopBanner的接口。我们把需要的数据封装到请求的params中,然后在服务端,我们可以通过query来拿到需要的数据。
在build/webpack.dev.config.js中写我们的nodejs代码
app.get('/api/getTopBanner', function (req, res) {const url = 'https://u.y.qq.com/cgi-bin/musics.fcg'axios.get(url, {headers: {referer: 'https://y.qq.com/',origin: 'https://y.qq.com'},params: req.query}).then((response) => {response = response.datalet category = response.category.data.category[0].itemslet dicsList = response.recomPlaylist.data.v_hotlet content = response.focus.data && response.focus.data.shelf.v_niche[0].v_cardlet toplist = response.toplist.data.groupif (dicsList.length > 10) {let newDicsList = []for (let i = 0; i < 10; i++) {newDicsList[i] = dicsList[i]}dicsList = newDicsList}let newCategory = []let randomNumList = []while (newCategory.length < 4) {let randomNum = Math.floor(Math.random() * 16)if (randomNumList.indexOf(randomNum) > -1) {continue}randomNumList.push(randomNum)newCategory.push(category[randomNum])}category = newCategoryres.json({code: 0,data: {slider: content,dicsList: dicsList,category: category,toplist: toplist}})}).catch((e) => {console.log(e)})})
在这里我们会拿到所需要的数据然后用res.json返回json数据给前端了。
然后我们打印一下拿到的数据
这就是爬取QQ音乐的全过程了!
from Tyreal