为了请求QQ音乐的数据时的跨域问题,我们使用Node.js + Express搭建一个中间件
可以直接在github: https://github.com/liaoqinwei/qqMusicApi 拔取源码
写的过程中借助文章 整理的接口(需要原生的qq音乐接口即可访问)https://blog.csdn.net/weixin_33874713/article/details/88003925
npm install express 配置服务
npm install body-parser 解析参数
npm install fs 用于读取文件
npm install js-base64 base64解密
npm install mime mime参数类型解析
用于帮我们基于Promise发送Http/https请求、解析参数、配置请求头。
const https = require('https'),
http = require('http');
/* 处理参数
* url: 拼接的路径
* param: 参数[Object]
* */
let urlHandler = (url, param = {}) => {
let result = ''
for (let key in param) {
// 如果是自身属性, 避免遍历到原型
if (param.hasOwnProperty(key)) {
result += `&${key}=${param[key]}`
}
}
return url.indexOf('?') > -1 ? `${url}&${result}` : `${url}?${result.slice(1)}`
}
/*
* 用于发送请求 返回一个promise实例
*
* */
let getData = (config = {}) => {
// 没有传参就抛出异常
if (Object.keys(config).length === 0) {
throw new Error('Please pass in parameters !')
}
/* 解构参数:url请求地址 params请求参数 headers请求头信息 hostname请求的主机名 */
let {url, params = {}, headers = {Connection: 'keep-alive', Accept: '*/*'}, hostname = 'c.y.qq.com'} = config,
path = urlHandler(url, params), // 解析参数
option = {
hostname,
path,
headers
}
return new Promise((resolve) => {
// 发送https请求
https.get(option, res => {
// 接收数据
let chunk = ''
// 数据是流传输 所以我们要监听 data 事件
res.on('data', result => {
// console.log(result)
chunk += result + ''
})
// 数据传输完成触发end 数据完了我们执行 resolve 方法
res.on('end', () => {
resolve(chunk)
})
})
})
}
/*
* 获取文件
* */
let getFile = url => {
if (!url) return;
return new Promise(resolve => {
http.get(url, res => {
let list = [], file
// 我们用数组把所有的流 存储起来
res.on('data', result => {
list.push(result)
})
// 将所有的流 拼接成一个流 然后返回调用 resolve
res.on('end', () => {
file = Buffer.concat(list)
resolve(file)
})
})
})
}
module.exports = {getData, getFile}
帮助我们基于Promise读取文件
/*
* 将fs中的常用 I/O 操作封装为promise版本
* */
let fs = require('fs'),
path = require('path'),
resultObj = {};
let suffixHandle = (pathname) => {
let suffixReg = /\.(PNG|JPG|JPEG|WEBP|ICO|BMP|SVG|MP4|MP3|M3U8|WAV|OGG)$/i
return suffixReg.test(pathname)
}
// READ-FILE / READ-DIR / MK-DIR / RM-DIR / UN-LINK /
/*
* 读取文件时 需要使用编码 以及过滤 富媒体 文件
* */
['readFile', 'readdir', 'mkdir', 'rmdir', 'unlink'].forEach(item => {
resultObj[item] = function (pathname, encoding = 'utf8') {
return new Promise((resolve, reject) => {
let callback = function (err, res) {
!err ? resolve(res) : reject(err)
}
// 如果是富媒体编码就为null
suffixHandle() ? encoding = null : null
pathname = path.resolve(pathname)
if (item !== 'readFile') {
encoding = callback
encoding = null
}
fs[item](pathname, encoding, callback)
})
}
});
// WRITE-FILE / APPEND-FILE
['writeFile', 'appendFile'].forEach(item => {
resultObj[item] = function (pathname, content, encoding = 'utf8') {
// 支持JSON类型数据
(typeof content === 'object' && content !== null) ? content = JSON.stringify(content) : null
// 将传入的内容转为 对象
typeof content !== 'string' ? content += '' : null
return new Promise((resolve, reject) => {
let callback = function (err, res) {
!err ? resolve(res) : reject(err)
}
pathname = path.resolve(pathname)
fs[item](pathname, content, encoding, callback)
})
}
})
// COPYFILE
resultObj['copyFile'] = function (pathname1, pathname2) {
return new Promise((resolve, reject) => {
// 解析路径 为了防止路径错误
pathname1 = path.resolve(pathname1)
pathname2 = path.resolve(pathname2)
let callback = function (err) {
!err ? resolve() : reject(err)
}
fs['copyFile'](pathname1, pathname2, callback)
})
}
module.exports = resultObj
帮助我们请求歌词时加密必要的请求参数
由于该文件是从qq音乐请求中拿下来的,文件经过打包,并非作者所写,所以直接放上文件地址:https://github.com/liaoqinwei/qqMusicApi/blob/master/utils/parseSign.js
说明:当我们请求qq音乐的时候,移动端的数据会放在window.__INIT_DATA__中所以我们需要通过正则截取中间的数据为我们所用(我们将数据放在了json文件中)
let {getData} = require('../utils/promiseHttps'),
{writeFile} = require('../utils/promiseFS');
// 移动端主页数据思路
// 我们把数据获取下来 通过 正则拆分数据
// 将数据保存到json/recommend.json 中
// 为了保证数据真实,我们需要每隔一分钟执行一次这个方法
let saveRecommendData = () => {
getData({url: 'https://i.y.qq.com/n2/m/index.html'}).then(res => {
let reg = /