• 数据采集实战(三)-- 王者荣耀2021世冠数据


    1. 概述

    王者荣耀是一直都挺喜欢的一个手游,玩了好几年,最近一段开始喜欢看比赛,所以想着采集点数据看看各个战队或者选手的情况。
    顺便也练习练习 puppeteer 的使用。

    数据来源于:尚牛电竞

    2. 采集流程

    王者荣耀最近正在进行的最大比赛就是 2021世冠杯,所以就选择采集这个赛事的数据。
    尚牛电竞 网站上,已经按照战队,选手和英雄分好类了,并且网站不需要登录就能看到数据。

    image.png

    三组数据直接对应不同的URL进行采集即可,没有复杂的流程,唯一需要注意的地方是对Logo头像的小图片的保存。

    2.1 各个数据的采集

    image.png

    积分榜的数据是空的,战队榜选手榜英雄榜的数据可以获取。

    3种数据的URL分别为:

    const urls = [
      {
        url: "https://www.shangniu.cn/gdall/kog?tab=0&pid=40008&tid=45",
        name: "战队榜",
      },
      {
        url: "https://www.shangniu.cn/gdall/kog?tab=1&pid=40008&tid=45",
        name: "选手榜",
      },
      {
        url: "https://www.shangniu.cn/gdall/kog?tab=2&pid=40008&tid=45",
        name: "英雄榜",
      },
    ];
    

    2.1.1 战队数据的采集和解析

    // 战队数据
    const teamData = async (browser, page, url) => {
      await page.goto(url);
    
      // 解析页面
      /*
       * 0. logo: 战队logo
       * 1. name: 战队名称
       * 2. matchCount: 比赛场次
       * 3. matchBoxCount: 比赛局数
       * 4. averageTime: 场均时长
       * 5. winRate: 总胜率
       * 6. blueWinRate: 蓝方胜率
       * 7. redWinRate: 红方胜率
       * 8. KDA: KDA
       * 9. averageKill: 场均击杀
       * 10. averageDie: 场均死亡
       * 11. averageAssit: 场均助攻
       * 12. averageOutput: 分均输出
       * 13. averageEconomic: 分均经济
       * 14. liveRate: 生存率
       * 15. firstBloodRate: 一血率
       * 16. firstTowerRate: 一塔率
       * 17. averageTower: 场均推塔
       * 18. averageCoverTower: 场均被推塔
       * 19. averageTyrants: 场均暴君
       * 20. tyrantsControlRate: 暴君控制率
       * 21. averageDominates: 场均主宰
       * 22. dominatesControlRate: 主宰控制率
       */
    
      const data = [];
      const rows = await page.$$("#scroll-table > .tbody > .row");
    
      for (const row of rows) {
        let line = [];
        const cols = await row.$$(".td");
    
        line[0] = await cols[1].$eval("a > img", (node) =>
          node.getAttribute("src")
        );
        line[1] = await cols[1].$eval(
          ".right-name > .item-name",
          (node) => node.innerText
        );
        line[2] = await page.evaluate((node) => node.innerText, cols[2]);
        line[3] = await page.evaluate((node) => node.innerText, cols[3]);
        line[4] = await page.evaluate((node) => node.innerText, cols[4]);
        line[5] = await page.evaluate((node) => node.innerText, cols[5]);
        line[6] = await page.evaluate((node) => node.innerText, cols[6]);
        line[7] = await page.evaluate((node) => node.innerText, cols[7]);
        line[8] = await cols[8].$eval("div > .kda", (node) => node.innerText);
        line[9] = await page.evaluate((node) => node.innerText, cols[9]);
        line[10] = await page.evaluate((node) => node.innerText, cols[10]);
        line[11] = await page.evaluate((node) => node.innerText, cols[11]);
        line[12] = await page.evaluate((node) => node.innerText, cols[12]);
        line[13] = await page.evaluate((node) => node.innerText, cols[13]);
        line[14] = await page.evaluate((node) => node.innerText, cols[14]);
        line[15] = await page.evaluate((node) => node.innerText, cols[15]);
        line[16] = await page.evaluate((node) => node.innerText, cols[16]);
        line[17] = await page.evaluate((node) => node.innerText, cols[17]);
        line[18] = await page.evaluate((node) => node.innerText, cols[18]);
        line[19] = await page.evaluate((node) => node.innerText, cols[19]);
        line[20] = await page.evaluate((node) => node.innerText, cols[20]);
        line[21] = await page.evaluate((node) => node.innerText, cols[21]);
        line[22] = await page.evaluate((node) => node.innerText, cols[22]);
        data.push(line.join(","));
        await downloadImage(
          browser,
          "./output/wzry/team-logo",
          `${line[1]}.png`,
          line[0]
        );
      }
    
      await saveContent(
        `./output/wzry`,
        `world_cup_2021_team.csv`,
        data.join("
    ")
      );
    };
    

    2.1.2 选手数据的采集和解析

    // 选手数据
    const memberData = async (browser, page, url) => {
      await page.goto(url);
    
      // 解析页面
      /*
       * 0. logo: 选手头像
       * 1. name: 选手名称
       * 2. matchCount: 比赛场次
       * 3. matchBoxCount: 比赛局数
       * 4. winRate: 总胜率
       * 5. KDA: KDA
       * 6. participationRate: 参团率
       * 7. averageKill: 场均击杀
       * 8. averageDie: 场均死亡
       * 9. averageAssit: 场均助攻
       * 10. averageOutput: 分均输出
       * 11. averageEconomic: 分均经济
       * 12. averageBear: 分均承伤
       * 13. outputRate: 输出占比
       * 14. economicRate: 经济占比
       * 15. bearRate: 承伤占比
       */
    
      const data = [];
      const rows = await page.$$("#scroll-table > .tbody > .row");
    
      for (const row of rows) {
        let line = [];
        const cols = await row.$$(".td");
    
        line[0] = await cols[1].$eval("a > img", (node) =>
          node.getAttribute("src")
        );
        line[1] = await cols[1].$eval(
          ".right-name > .item-name",
          (node) => node.innerText
        );
        line[2] = await page.evaluate((node) => node.innerText, cols[2]);
        line[3] = await page.evaluate((node) => node.innerText, cols[3]);
        line[4] = await page.evaluate((node) => node.innerText, cols[4]);
        line[5] = await cols[5].$eval("div > .kda", (node) => node.innerText);
        line[6] = await page.evaluate((node) => node.innerText, cols[6]);
        line[7] = await page.evaluate((node) => node.innerText, cols[7]);
        line[8] = await page.evaluate((node) => node.innerText, cols[8]);
        line[9] = await page.evaluate((node) => node.innerText, cols[9]);
        line[10] = await page.evaluate((node) => node.innerText, cols[10]);
        line[11] = await page.evaluate((node) => node.innerText, cols[11]);
        line[12] = await page.evaluate((node) => node.innerText, cols[12]);
        line[13] = await page.evaluate((node) => node.innerText, cols[13]);
        line[14] = await page.evaluate((node) => node.innerText, cols[14]);
        line[15] = await page.evaluate((node) => node.innerText, cols[15]);
        data.push(line.join(","));
        await downloadImage(
          browser,
          "./output/wzry/member-logo",
          `${line[1]}.png`,
          line[0]
        );
      }
    
      await saveContent(
        `./output/wzry`,
        `world_cup_2021_member.csv`,
        data.join("
    ")
      );
    };
    

    2.1.3 英雄数据的采集和解析

    // 英雄数据
    const heroData = async (browser, page, url) => {
      await page.goto(url);
    
      // 解析页面
      /*
       * 0. logo: 英雄头像
       * 1. name: 英雄名称
       * 2. appearCount: 出场次数
       * 3. appearRate: 出场率
       * 4. winCount: 胜场
       * 5. winRate: 胜率
       * 6. banCount: 禁用次数
       * 7. banRate: 禁用率
       * 8. KDA: KDA
       */
    
      const data = [];
      const rows = await page.$$("#scroll-table > .tbody > .row");
    
      for (const row of rows) {
        let line = [];
        const cols = await row.$$(".td");
    
        line[0] = await cols[1].$eval("a > img", (node) =>
          node.getAttribute("src")
        );
        line[1] = await cols[1].$eval(
          ".right-name > .item-name",
          (node) => node.innerText
        );
        line[2] = await page.evaluate((node) => node.innerText, cols[2]);
        line[3] = await page.evaluate((node) => node.innerText, cols[3]);
        line[4] = await page.evaluate((node) => node.innerText, cols[4]);
        line[5] = await cols[5].$eval(".winRate > span", (node) => node.innerText);
        line[6] = await page.evaluate((node) => node.innerText, cols[6]);
        line[7] = await page.evaluate((node) => node.innerText, cols[7]);
        line[8] = await page.evaluate((node) => node.innerText, cols[9]);
        data.push(line.join(","));
        await downloadImage(
          browser,
          "./output/wzry/hero-logo",
          `${line[1]}.png`,
          line[0]
        );
      }
    
      await saveContent(
        `./output/wzry`,
        `world_cup_2021_hero.csv`,
        data.join("
    ")
      );
    };
    

    2.2 logo和头像的保存

    在html页面中,logo和头像都是图片的url,为了下载实际的图片,封装了个小函数 downloadImage

    // 下载图片
    const downloadImage = async (browser, dirname, filename, imgSrc) => {
      console.log("image src: ", imgSrc);
      const page = await browser.newPage();
    
      try {
        const imgResp = await page.goto(imgSrc);
        const buffer = await imgResp.buffer();
        const imgBase64 = buffer.toString("base64");
    
        if (!mkdirsSync(dirname)) {
          console.error("mkdir save page dir ERROR!");
          return;
        }
        fs.writeFileSync(path.join(dirname, filename), imgBase64, "base64");
      } catch (e) {
        console.error("download image error: ", e);
      } finally {
        await page.close();
      }
    };
    

    3. 总结

    以上通过 puppeteer 采集2021世冠比赛数据的实战中,技术要点主要有:

    1. 解析页面元素中的值
    2. 循环获取html table 中 tr/td 中的内容
    3. 下载网页中图片

    4. 注意事项

    爬取数据只是为了研究学习使用,本文中的代码遵守:

    1. 如果网站有 robots.txt,遵循其中的约定
    2. 爬取速度模拟正常访问的速率,不增加服务器的负担
    3. 只获取完全公开的数据,有可能涉及隐私的数据绝对不碰
  • 相关阅读:
    第三节 单因素方差分析
    第四十一节 ORM介绍和用元类实现
    第四十节 通过type创建复杂的类,元类应用
    第二节 检验方法使用条件考察
    HDFS HA误删namenode后报错Nameservice testCluster has no SecondaryNameNode or High-Availability partner的恢复
    spark sql cache时发现的空字符串问题
    centos7环境下ELK部署之elasticsearch
    CDH升级 5.7.5 --> 5.13.3(tar包方式)
    CDH部署(以5.7.5为例)
    人生苦短,Let's Go
  • 原文地址:https://www.cnblogs.com/wang_yb/p/15168674.html
Copyright © 2020-2023  润新知