我给中国奥运金牌数做了可视化(一)

简介: 前言2020东京奥运会已经开幕很多天了,还记得小时候看奥运会的是在2008年的北京奥运会,主题曲是「北京欢迎你」, 那个时候才上小学吧,几乎有中国队的每场必看,当时也是热血沸腾了, 时间转眼已经到了2021年而我也从小学生变成了一个每天不断敲代码的程序员👩‍💻,看奥运的时间又少,但是又想出分力,既然是程序员,想着能为奥运会搞点什么?第一时间想到了就是给奥运奖牌数🏅做可视化,因为单看表格数据,不能体现出我们中国的牛逼🐂, 废话不多说,直接开写。数据获得我们先看下奥运奖牌数的表格,这东西肯定是接口获得的吧,我不可能手写吧,而且每天都是更新的,难道我要每天去改,肯定不是这样的,我当时

前言



2020东京奥运会已经开幕很多天了,还记得小时候看奥运会的是在2008年的北京奥运会,主题曲是「北京欢迎你」, 那个时候才上小学吧,几乎有中国队的每场必看,当时也是热血沸腾了, 时间转眼已经到了2021年而我也从小学生变成了一个每天不断敲代码的程序员👩‍💻,看奥运的时间又少,但是又想出分力,既然是程序员,想着能为奥运会搞点什么?第一时间想到了就是给奥运奖牌数🏅做可视化,因为单看表格数据,不能体现出我们中国的牛逼🐂, 废话不多说,直接开写。


数据获得



我们先看下奥运奖牌数的表格,这东西肯定是接口获得的吧,我不可能手写吧,而且每天都是更新的,难道我要每天去改,肯定不是这样的,我当时脑子里就想着去做爬虫,去用「puppeteer」 去模拟浏览器的行为然后获取页面的原生dom,然后将表格的数据搞出来, 然后我就很兴奋的去搞了,写了下面的代码:


const puppeteer = require('puppeteer')
async function main() {
  // 启动chrome浏览器
  const browser = await puppeteer.launch({
    // // 指定该浏览器的路径
    // executablePath: chromiumPath,
    // 是否为无头浏览器模式,默认为无头浏览器模式
    headless: false,
  })
  // 在一个默认的浏览器上下文中被创建一个新页面
  const page1 = await browser.newPage()
  // 空白页刚问该指定网址
  await page1.goto(
    'https://tiyu.baidu.com/tokyoly/home/tab/%E5%A5%96%E7%89%8C%E6%A6%9C/from/pc'
  )
  // 等待title节点出现
  await page1.waitForSelector('title')
  // 用page自带的方法获取节点
  // 用js获取节点
  const titleDomText2 = await page1.evaluate(() => {
    const titleDom = document.querySelectorAll('#kw')
    return titleDom
  })
  console.log(titleDomText2, '查看数据---')
  // 截图
  //await page1.screenshot({ path: 'google.png' })
  //   await page1.pdf({
  //     path: './baidu.pdf',
  //   })
  browser.close()
}
main()


然后当我很兴奋的想要去结果的时候,结果发现是空。百度是不是做了反爬虫协议, 毕竟我是爬虫菜鸟,搞了很久。还是没搞出来。如果有大佬会,欢迎指点我下哦!


image.png


image-20210731112152170


不过这个「puppeteer」,这个库有点牛皮的,可以实现网页截图、生成pdf、拦截请求,其实有点自动化测试的感觉。感兴趣的同学可以自行了解一下,这不在本篇文章介绍的重点。


接口获得


然后这时候就开始疯狂百度,开始寻找有没有现成的「api」, 真是踏破铁鞋无觅处,得来全不费工夫。被我找到了,原来是有大佬已经开始做了, 这时候我本地直接去请求那个接口是有问题的,前端不得不处理的问题—— 跨域。看着东西我头疼哇, 不过没关系, 我直接node起一个服务器, 我node去请求那个接口,然后后台在配置下跨域, 搞定接口数据就直接获得了, 后台服务我是用的express, 搭建的服务器直接随便搞搞的。代码如下:


const axios = require('axios')
const express = require('express')
const request = require('request')
const app = express()
const allowCrossDomain = function (req, res, next) {
  res.header('Access-Control-Allow-Origin', '*')
  res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE')
  res.header('Access-Control-Allow-Headers', 'Content-Type')
  res.header('Access-Control-Allow-Credentials', 'true')
  next()
}
app.use(allowCrossDomain)
app.get('/data', (req, res) => {
  request(
    {
      url: 'http://apia.yikeapi.com/olympic/?appid=43656176&appsecret=I42og6Lm',
      method: 'GET',
      headers: { 'Content-Type': 'application/json' },
    },
    function (error, response, body) {
      if (error) {
        res.send(error)
      } else {
        res.send(response)
      }
    }
  )
})
app.listen(3030)


这样我就是实现了接口转发,也搞定了跨域问题,前台我直接用 fetch去请求数据然后做一层数据转换,但是这个接口不能频繁请求,动不动就crash, 是真的烦, OK所以直接做了一个操作, 将数据 存到localstorage中,然后做一个定时刷新,时间大概是一天一刷。这样就保证数据的有效性。代码如下:


getData() {
  let curTime = Date.now()
  if (localStorage.getItem('aoyun')) {
    let { list, time } = JSON.parse(localStorage.getItem('aoyun'))
    console.log(curTime - time, '查看时间差')
    if (curTime - time <= 24 * 60 * 60 * 60) {
      this.data = list
    } else {
      this.fetchData()
    }
  } else {
    this.fetchData()
  }
}
fetchData() {
  fetch('http://localhost:3030/data')
    .then((res) => res.json())
    .then((res) => {
      const { errcode, list } = JSON.parse(res.body)
      if (errcode === 100) {
        alert('接口请求太频繁')
      } else if (errcode === 0) {
        this.data = list
        const obj = {
          list,
          time: Date.now(),
        }
        localStorage.setItem('aoyun', JSON.stringify(obj))
      }
    })
    .catch((err) => {
      console.log(err)
    })
}


数据如下图所示 :

image.png

image.gifimage-20210731114644399


柱状图的表示



其实我想了很多表达中国金牌数的方式,最终我还是选择用2d柱状图去表示,并同时做了动画效果,显得每一块金牌🏅来的并不容易。我还是用原生手写柱状图不去使用「Echarts」 库, 我们首先先看下柱状图:


image.png

柱状图


从图中可以分析出一些元素


  1. x轴和y轴以及一些直线,所以我只要封装一个画直线的方法


  1. 有很多矩形, 封装一个画矩形的方法


  1. 还有一些刻度和标尺


  1. 最后就是一进入的动画效果


画布初始化



在页面上创建canvas和获取canvas的一些属性,并对canvas绑上移动事件。代码如下:


get2d() {
    this.canvas = document.getElementById('canvas')
    this.canvas.addEventListener('mousemove', this.onMouseMove.bind(this))
    this.ctx = this.canvas.getContext('2d')
    this.width = canvas.width
    this.height = canvas.height
  }


画坐标轴



坐标轴本质上也是一个直线,直线对应的两个点,不同的直线其实就是对应的端点不同,所以我直接封装了一个画直线的方法:


// 画线的方法
  drawLine(x, y, X, Y) {
    this.ctx.beginPath()
    this.ctx.moveTo(x, y)
    this.ctx.lineTo(X, Y)
    this.ctx.stroke()
    this.ctx.closePath()
  }


可能有的人对canvas不熟悉,这里我还是大概说下, 开启一段路径, 移动画笔到开始的点, 然后画直线到末尾的点,然后描边 这一步是canvas做渲染, 很重要,很多小白不写, 直线就不出来, 然后闭合路径。结束over!


画坐标轴我们首先先确定原点在哪里,我们首先给画布向内缩一个padding距离,然后呢,算出画布实际的宽度和高度。


代码如下:


initChart() {
  // 留一个内边距
  this.padding = 50
  // 算出画布实际的宽度和高度
  this.cHeight = this.height - this.padding * 2
  this.cWidth = this.width - this.padding * 2
  // 计算出原点
  this.originX = this.padding
  this.originY = this.padding + this.cHeight
}


有了原点我们就可以画X轴和Y轴了, 只要加上「实际画布」对应的宽度和高度 就好了 。代码如下:


//设置canvas 样式
  this.setCanvasStyle()
  // 画x轴
  this.drawLine(
    this.originX,
    this.originY,
    this.originX,
    this.originY - this.cHeight
  )
  // 画Y轴
  this.drawLine(
    this.originX,
    this.originY,
    this.originX + this.cWidth,
    this.originY
  )


第一个 函数就是设置canvas画笔的样式的,其实这东西没什么。我们看下效果:

image.gifX轴和Y轴


很多人以为到这里就结束了哈哈哈, 那你想太多了, canvas我设置的画线宽度是1px 为什么看图片的线的宽度像是2px?不仔细观察根本发现不了这个问题, 所以我们要学会思考这到底是什么问题?其实这个问题也是我看「Echarts」源码发现的, 学而不思则罔,思而不学则殆哇!


相关文章
|
SQL 分布式计算 监控
大数据最后一公里——2021年五大开源数据可视化BI方案对比
大数据在经过前几年的野蛮生长以后,开始与数据中台的概念一同向着更实际的方向落地。有人问,数据可视化是不是等同于数据大屏。数据大屏是数据可视化的一部分,其承载更多的是展示与监控的功能。 而真正对业务产生影响的,确是比较低调的自助数据可视化系统(商用的一般称之为BI系统),支撑着公司的指标体系,为业务的发展,企业的数字化驱动提供帮助。
1305 0
大数据最后一公里——2021年五大开源数据可视化BI方案对比
|
3月前
|
存储 数据可视化 JavaScript
基于Echarts构建大数据招聘岗位数据可视化大屏
基于Echarts构建大数据招聘岗位数据可视化大屏
77 0
|
8月前
|
数据采集 数据可视化 中间件
数据采集:亚马逊畅销书的数据可视化图表
亚马逊是全球最大的电子商务平台之一,它提供了各种类别的商品,其中包括图书。亚马逊每天都会更新它的畅销书排行榜,显示不同类别的图书的销量和评价。如果我们想要分析亚马逊畅销书的数据,我们可以使用爬虫技术来获取网页上的信息,并使用数据可视化工具来绘制图表,展示图书的特征和趋势。本文将介绍如何使用Python和Scrapy框架来编写爬虫程序,以及如何使用亿牛云爬虫代理服务来提高爬虫效果。本文还将介绍如何使用Matplotlib库来绘制亚马逊畅销书的数据可视化图表。
数据采集:亚马逊畅销书的数据可视化图表
|
11月前
|
数据可视化 数据挖掘
中国经济数据可视化分析
中国经济数据可视化分析
80 0
|
11月前
|
城市大脑 智能设计 运维
首家!阿里云完成数据可视化服务能力评估
阿里云DataV数据可视化团队历经磨炼,走过了10年的可视化之路,在产品和服务上走出了一条属于自己的道路。
|
数据可视化 搜索推荐
【数据可视化】预制菜行业分析(一)——国内发展情况
近年来,预制菜开始从大型连锁餐饮企业的中央厨房渗透到外卖餐饮平台,并逐渐从 B 端走向 C 端。消费者购买后只需要简单加工即可食用,省去了食材采购、处理步骤,具有便捷、高效、口味保持度高的特点。
|
编解码 数据可视化 定位技术
OpenET ——开放的可视化美国蒸散发平台
OpenET ——开放的可视化美国蒸散发平台
313 0
OpenET ——开放的可视化美国蒸散发平台
|
数据采集 SQL 分布式计算
大数据电影可视化系统
本项目以电影数据为主题,以数据采集、处理、分析及数据可视化为项目流程,可实现百万级电影数据离线处理与计算。功能包括python爬虫,Matplotlib绘图、Echarts数据可视化、结合mysql数据实现hive电影相关数据统计、Mapreduce词频统计、情感分析、词图云等。
459 0
大数据电影可视化系统
|
城市大脑 人工智能 数据可视化
DataV 产品正式通过信通院《数据可视化平台》专项评测,引领大数据可视化产品新赛道
数据可视化的本质是数据时代的人机交互界面。在IT时代,通过简单的统计图表就可以解决少量“数据可见”的问题;在DT时代,可视化需要解决如何与海量、实时数据进行互动;到了数据智能时代,可视分析、时空推演等全新的可视化手段为业务分析带来更为沉浸的体验。
677 0
|
前端开发 数据可视化 API
我给中国奥运金牌数做了可视化(二)
彩蛋——canvas如何画出1PX的直线 在这里我举一个例子, 你就明白了, 假设我要画从(50,10) 到 (200,10)这样的一条直线。为了画这条线,浏览器首先到达初始起点(50,10)。这条线宽1px,所以两边各留0.5px。所以基本上初始起点是从(50,9.5)延伸到(50,10.5)。现在浏览器不能在屏幕上显示0.5像素——最小阈值是1像素。浏览器别无选择,只能将起点的边界延伸到屏幕上的实际像素边界。它会在两边再加0.5倍的“垃圾”。所以现在,最初的起点是从(50,9)扩展到(50,11),所以看起来有2px宽。情况如下:
我给中国奥运金牌数做了可视化(二)