关于Node异步爬虫cheerio.js的使用

221 阅读2分钟
温馨提示:本文章主要是我当笔记,本身我也是菜鸡,如有不对之处欢迎指正。

第一步:安装cheerio包

相信这个都比较简单,都不用我写了吧。为了防止出意外还是贴一下代码吧 介绍一下:这玩意儿其实我感觉就是用来解析网页dom信息的,想了解的话移步去官网吧。

    npm i cheerio

第二步:安装aixos

这个大家都熟悉吧,这只是个http请求库而已,其实还有其他的,不一定非要用这个,我只是顺手写一下。

    npm i axios

开始啦,这里主要是我自己想要获取对应的forge相关信息,以此为例吧。

其实这儿也算是爬虫啦,我想要获取游戏版本只能去网站爬才能拿到,这返回的是一个游戏版本数组。

/** 获取游戏版本 */
async function getMcVersion() {
  //获取网站的返回的HTML
  const response = await axios.get('https://files.minecraftforge.net/net/minecraftforge/forge');
  // 利用cheerio解析response中data的HTML
  let $ = cheerio.load(response.data)
  // 我想要的信息在 元素类 li-version-list>nav-collapsible>li 下
  let result = $('.li-version-list .nav-collapsible li')
  //留一个数组存放所有游戏版本信息
  let arr = []
  //获取到所有元素节点循环
  result.each((index, item) => {
    //正则并得到想要的游戏版本 text()是获取节点中的文本信息
    const mcVsersion = $(item).text().replace(/[ ]|[\r\n]/g, "")
    arr.push({
      mc: mcVsersion,
      url: `https://files.minecraftforge.net/net/minecraftforge/forge/index_${mcVsersion}.html`
    })
  })
  // 返回所有游戏版本信息,及相关网站
  return arr
}

接着这个是解析函数是针对各种不同游戏版本获取的相应信息,这个其实你们不用管,这个只是针对我个人需求,写的函数。

function getForgeInfo(data) {
  let $ = cheerio.load(data)
  let versionDomeList = $('.download-list tbody tr')[0]
  let obj = {}
  $(versionDomeList).children().each((index, item) => {
    if (index == 0) {
      obj.version = $(item).text().replace(/[ ]|[\r\n]/g, "")
    } else if (index == 1) {
      obj.date = $(item).text().replace(/[ ]|[\r\n]/g, "")
    } else {
      const liList = $(item).children().children()
      $(liList).each((index, sonItem) => {
        $(sonItem).each((index, sonItemA) => {
          const strList = $(sonItemA).children()
          const name = $(strList[0]).text().replace(/[ ]|[\r\n]/g, "").toLowerCase()
          obj[name] = {
            name
          }
          let path;
          path = $(strList[1]).attr('href')
          if (!path) {
            path = $(strList[0]).attr('href')
          }
          obj[name]['path'] = path.replace('https://maven.minecraftforge.net', 'https://maven.fastmirror.net/repositories/minecraft')
          const hexWord = $(strList[2]).text().replace(/[ ]|[\r]/g, "")
          hexWord.split('\n').forEach(keyText => {
            if (keyText.indexOf('MD5') !== -1) {
              obj[name]['md5'] = keyText.split('MD5:')[1]
            } else if (keyText.indexOf('SHA1') !== -1) {
              obj[name]['sha1'] = keyText.split('SHA1:')[1]
            }
          })
        })
      })
    }
  })
  console.log('obj信息', obj)
  return obj
}

发送请求获取对应的HTML信息,很简单吧,这个主要是在构键函数数组使用

async function getForgeHtml(url) {
  let {data} = await axios.get(url)
  return data
}

开启异步任务,最大并发数可由自己设置

/**
 * 
 * @param {*} fnList 函数数组
 * @param {*} max 最大并发数
 * @param {*} taskName 任务名称
 * @returns 
 */
async function startRun(fnList = [], max = 5, taskName = '未命名') {
  if (!fnList.length) return;
  log(chalk.blue(`开始执行多个异步任务,最大并发数:${max}`))
  /** 存储结果 */
  const dataList = [];
  const count = fnList.length;
  // 开始事件
  const startTime = new Date().getTime();
  let current = 0;
  // 任务执行函数
  const asyncTask = async (index) => {
    return new Promise(async (resolve) => {
      try {
        const fn = fnList[index]
        if (!fn) return resolve()
        const data = await fn()
        dataList[index] = data
        log(`${taskName} 任务进度 ${((++current / count) * 100).toFixed(2)}%`)
        await asyncTask(index + max)
        resolve()
      } catch (error) {
        console.log('错误:', error)
        resolve()
      }
    })
  };
  const taskList = new Array(max).fill(0).map((_, index) => asyncTask(index))
  // 通过Promise.all()同时发送多个请求
  await Promise.all(taskList)
  const cost = (new Date().getTime() - startTime) / 1000
  log(chalk.green(`执行完成,最大并发数:${max},耗时:${cost}s`))
  return dataList
}

最终结果:如下

(async () => {
  const forgeFnList = (await getMcVersion()).map((item) => () => getForgeHtml(item.url));
  const infoData = await startRun(forgeFnList, 5, '获取forge页面')
  const forgeInformation = infoData.map(item => { return getForgeInfo(item) })
  fs.writeFile('../ceshi.json', JSON.stringify(forgeInformation), err => {
    if (err) throw err;
    console.log('写入成功');
  })
  console.log('结果:', forgeInformation)
})()

获取后的结果信息会被写入ceshi.json文件中,下图就是json文件截图:

image.png