Node.JS - 爬取echo回声音乐

一、echo

Echo回声.记得14年的时候就有了,它家最出名的就是独家首创3D音乐、和立体环境音.
好久没听了,刚好下几首歌到HIFI清一波耳朵.


index.png

找了半天找不到下载口,外放可没有推耳屎感.
没关系我们切换到移动端下载,再导入到hifi中.


mobile.jpg

poor.png

好家伙一首歌5个币.
本想以普通人的身份相处,没办法

只能把发际线露出来了.

echo回声搜索 凳子骑,随便点一首歌进去


play.png

点击播放按钮加载音频文件
F12开发者模式


detail.png

找到Network下选项卡Media
找到较大size文件 打开新的窗口
audio.png

右键另存为
搞定
本次教程结束

silent.png

开玩笑的...
我还没敲代码呢
如果只是少量歌曲需要的话以上步骤就够了


先上成果展示图


echo1.png
echo2.png
music.png

只需键入
1.歌曲名/作者名
2.加载的页数(1页大概8~9首歌)
即可实现批量下载

二、思路

逆推

首先查看最里面详情页body代码


body.png

信息少的可怜
页面是由前端渲染完成的
查看xhr请求
这里省略一段时间
·
·
·


source.png

不费吹灰之力找到了.............
还是比鹅厂的简单的多

在info?id=749415&comment=1响应结果,info下的source

观察一下请求头


query.png

再随便打开一首歌,查看query


query_.png

接下来就是大家来找茬时间.......

很简单query中有id,comment两个参数
comment是评论的意思
试着把comment去掉直接打开这段链接


data_.png

果然,依然可以获得数据,并且有我们想要的source
所以ID起请求数据的决定性因素

继续逆推回到搜索页

search.png

我的眼睛........


eyes.png

找到了
在sound?keyword=******响应中
data列表下面有歌曲id
拼接id和链接可以发送请求获得歌曲的source下载

http://www.app-echo.com/api/sound/info?id=

那id列表是怎么获得的呢?
观察sound?keyword***请求头

http://www.app-echo.com/api/search/sound?keyword=%E9%82%93%E7%B4%AB%E6%A3%8B&page=1&limit=10&src=0

下拉网页列表点击更多


more.png

网页又发送了一次ajax请求

查看请求头

http://www.app-echo.com/api/search/sound?keyword=%E9%82%93%E7%B4%AB%E6%A3%8B&page=2&limit=10&src=0

观察时间..........
把keyword参数后的编码%E9%82%93%E7%B4%AB%E6%A3%8B
用UrlEncode解码


urlencode.png

好的,结束了
keyword参数为搜索内容
page 为搜索页面
通过url

http://www.app-echo.com/api/search/sound?
keyword=encodeURI(邓紫棋)
&page=?&limit=10&src=0

即可获取歌曲id,通过歌曲id获取source,下载音乐

三、代码实现

只需一个axios库即可完成简单的爬取音乐

注意事项

  • try-catch捕获加载失败发出的异常
  • await等待数据写入完成
  • 正则过滤歌曲名的特殊符号
  • 避免使用forEach迭代,unexpected error
  • axios 设置timeout,也可配置代理
const axios = require("axios")
const fs = require("fs")
const path = require("path")
const readline = require("readline")

async function echo() {
  // 获取页面列表
  const pagesList = await getPagesList()
  const songsList = await getSongsList(pagesList)
  download(songsList)
}

// 创建readline 接口实例
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
})

async function getPagesList() {
  const pagesList = []
  const songTitle = await readLine("请输入歌曲名称/歌手?\n")
  const pages = await readLine("请输入要爬取的页面数?\n")
  // 关闭 readline
  rl.close()
  // 创建 music目录
  fs.mkdir("./music", (err) => {})
  // 创建 分类目录
  fs.mkdir(`./music/${songTitle}`, (err) => {})
  for (let i = 1; i <= pages; i++) {
    const url = `http://www.app-echo.com/api/search/sound
            ?keyword=${encodeURI(songTitle)}
            &page=${i}&limit=10&src=0`.replace(/[\s\n]/gis, "")
    pagesList.push(url)
  }
  return pagesList
}

async function getSongsList(pagesList) {
  const songsList = []
  for (let i = 0; i < pagesList.length; i++) {
    // 捕获请求列表异常
    try {
      const res = await axios({
        url: pagesList[i],
        timeout: 5000,
        // 加速请求代理,可不设置
        // proxy:{
        //   host:"112.74.200.147",
        //   port:80
        // }
      })
      const keyword = res.data.keyword
      const list = res.data.data
      const obj = {
        keyword,
        list,
      }
      songsList.push(obj)
      console.log(`get Pages url${i + 1} Successful\n`)
    } catch (err) {
      console.log(`get Pages url${i + 1} Failure\n`)
      continue
    }
  }
  return songsList
}

async function download(list) {
  const keyword = list[0].keyword
  for (let c of list) {
    const songsList = c.list
    for (let j = 0; j < songsList.length; j++) {
      // filter 歌曲名中特殊符号
      const reg = /[/\\:*?"<>|\s]/gis
      const songTitle = songsList[j].name.replace(reg, "")
      // get 音频文件url
      const songSource = songsList[j].source
      // 捕获歌曲source异常
      try {
        await downloadSongs(songSource, keyword, songTitle)
        console.log(`${songTitle} Download Successful\n`)
      } catch (err) {
        console.log(`${songTitle} Download Failure\n`)
      }
    }
  }
}

async function downloadSongs(songSource, keyword, songTitle) {
  // get 音频文件后缀名
  const _reg = /[^?]*/
  const extName = _reg.exec(path.extname(songSource))[0] || ".mp3"
  const res = await axios({
    url: songSource,
    timeout: 3000,
    responseType: "stream",
  })
  // build read stream
  const rs = res.data
  // build write stream
  const ws = fs.createWriteStream(`./music/${keyword}/${songTitle}${extName}`, {
    emitClose: true,
    autoClose: true,
  })
  rs.pipe(ws)
  // 等待歌曲写入完成
  await new Promise((resolve) => {
    rs.on("end", () => {
      ws.end()
      resolve()
    })
  })
}

async function readLine(question) {
  return new Promise((resolve) => {
    rl.question(question, (res) => {
      resolve(res)
    })
  })
}

echo()

仅用于学习交流使用,切勿用于商业用途~

觉得有用点个赞呗!

你可能感兴趣的:(Node.JS - 爬取echo回声音乐)