likes
comments
collection
share

如何获取 cnpm 的 packages 下载量

作者站长头像
站长
· 阅读数 34

导读

在做前端技术调研的时候,github stars 和 npm downloads 这两个宏观指标是很重要的参考项。因此 star-historynpmtrends 两个网站就派上大用场了。前者可以列出 repo 在 github stars 的变化:

如何获取 cnpm 的 packages 下载量

后者可以列出 package 在 npm 下载量的变化,还有很多其他信息:

如何获取 cnpm 的 packages 下载量

宏观数据的对比可谓一目了然,非常方便。

但是众所周知的原因,国内用默认的 npm 源很多时候会有网络问题,这时候我们通常会使用 cnpm 来解决,所以其实国内的好多 package 的流量都没有被统计到 npm 数据中。

那么有没有什么办法能够统计到 cnpm 的下载量呢?像 npmtrends 一样的网站是没找着,但是机缘巧合之下,找到了 cnpm 获取下载量的 API。这不就完成 99% 了嘛!开搞!

正文

分析

偶然的机会,在一篇帖子的回复里找到了 cnpm downloads 的 API 信息:

  // download times
  app.get('/downloads/range/:range/:name', downloadTotal);
  app.get(/^\/downloads\/range\/([^\/]+)\/(@[\w\-\.]+\/[\w\-\.]+)$/, downloadTotal);
  app.get('/downloads/range/:range', downloadTotal);

从代码里我们能够猜到,只要知道 rangename 的正确格式,就能够获取到数据。name 好说,应该就是 package 的 name,跟 npm 当中一致,关键就是这个 range。于是笔者按图索骥,找到了 dowloadTotal 的具体代码

var DATE_REG = /^\d{4}-\d{2}-\d{2}$/;

module.exports = function* downloadTotal() {
  var range = this.params.range || this.params[0] || '';
  var name = this.params.name || this.params[1];

  range = range.split(':');
  if (range.length !== 2
      || !range[0].match(DATE_REG)
      || !range[1].match(DATE_REG)) {
    this.status = 400;
    const error = '[range_error] range must be YYYY-MM-DD:YYYY-MM-DD style';
    this.body = {
      error,
      reason: error,
    };
    return;
  }
  // ...other code
}

很明显了,YYYY-MM-DD:YYYY-MM-DD 就是我们想要的 range 格式。最后就是 host 了,简单查一下就能得到结果: https://registry.npmmirror.com。所以一个完整的请求 URL 会是下面这个样子:

https://registry.npmmirror.com/downloads/range/2022-11-01:2022-11-02/vue

各位可以在浏览器里打开这个地址,直接就能看到结果,如下:

{
  "downloads": [
    { "day": "2022-11-01", "downloads": 77721 },
    { "day": "2022-11-02", "downloads": 79027 },
    { "day": "2022-11-03", "downloads": 36330 }
  ],
  "versions": {
    "0.0.0": [{ "day": "2022-11-02", "downloads": 1 }],
    // ...other versions
    "3.2.9": [
      { "day": "2022-11-01", "downloads": 6 },
      { "day": "2022-11-02", "downloads": 10 },
      { "day": "2022-11-03", "downloads": 6 }
    ]
  }
}

请求地址、参数、返回数据结构都摸清楚了,接下来就是实现代码了,So Easy~

代码实现

既然把 cnpm 的 API 搞清楚了,我们就简单看下 npm 的 API,不难查到,npm 的 API 格式如下:

https://api.npmjs.org/downloads/point/2022-11-01:2022-11-02/vue

另外,如果不传 name,它会返回所有包的下载总量,这个功能 cnpm 好像还没有。

再说回代码实现。想做一个跟 npmtrends 一样的网站就不必了,我们只需要知道一段时间内(默认 1 年)总下载量的对比就可以了,所以一个最简的无第三方依赖的 nodejs 脚本就可以了。具体代码如下,复制到本地,修改参数,直接调用 getPackagesDownloads 方法就可以了:

const https = require("https");

const parseDownloadsData = (str) => JSON.parse(str).downloads;

const totalDownloads = (arr) =>
  arr.reduce((prev, curr) => {
    return prev + curr.downloads;
  }, 0);

const downloadsPromise = (name, platform='cnpm', range) => {
  let rangeStr; // YYYY-MM-DD:YYYY-MM-DD
  let host = 'registry.npmmirror.com'
  let apiPath = '/downloads/range'
  if (platform == 'npm') {
    host = 'api.npmjs.org'
    apiPath = '/downloads/point'
  }
  if (range) {
    rangeStr = range;
  } else {
    const todayDate = new Date().toISOString().split("T")[0]; // YYYY-MM-DD
    const year = new Date().getFullYear();
    const lastYearDate = todayDate.replace(year, year - 1);
    rangeStr = `${lastYearDate}:${todayDate}`;
  }

  return new Promise((rev, rej) => {
    https
      .request(
        {
          host,
          path: `${apiPath}/${rangeStr}/${name}`,
        },
        (response) => {
          let str = "";

          //another chunk of data has been received, so append it to `str`
          response.on("data", (chunk) => {
            str += chunk;
          });

          //the whole response has been received, so we just print it out here
          response.on("end", () => {
            rev(str);
          });

          response.on("error", (err) => {
            rej(err);
          });
        }
      )
      .end();
  });
};

const getPackagesDownloads = (pkgs) => {
  const result = {};
  return Promise.all(
    pkgs.map((pkg) => Promise.all([
      downloadsPromise(pkg, 'cnpm'),
      downloadsPromise(pkg, 'npm')
    ]).then(resArr=> {
      result[pkg] = {
        cnpm: totalDownloads(parseDownloadsData(resArr[0])),
        npm: parseDownloadsData(resArr[1])
      }
    })
    )
  ).then(() => result);
};

/**
 * Usage:
 *
getPackagesDownloads(["react", "vue", "@angular/core", "angular"]).then(
  (res) => {
    console.log(res);
    // {
    //   angular: { cnpm: 75264, npm: 26791859 },
    //   vue: { cnpm: 17878348, npm: 163975718 },
    //   '@angular/core': { cnpm: 730528, npm: 151297959 },
    //   react: { cnpm: 41750961, npm: 779385565 }
    // }
  }
);
 */

代码非常简单,没有多少好讲的。强调一点,代码中用了两个 Promise.all,这个做法对于提高速度是立竿见影的,有兴趣的同学可以试试改成同步代码,效率差了不止一点半点。

中国 VS. 世界

代码写完了,我们来用一下看看效果,跑一些数据看看。基本上 cnpm 可以代表中国的情况,npm 当然就是全世界了,这种中国 VS 世界的事,想想还有点小兴奋呢~~

注意:以下结论极其不严谨仅做娱乐

整体对比

我们先来看下 npm 和 cnpm 整体的数据对比。从 npm 的 API 我们可以获取到各个时段的总下载量。但是 cnpm 好像不太行,于是从 这篇帖子官网 的 API 中获得了一些数据,整理如下:

20202021.1.1~2021.11.32022.1.1~2022.11.4
npm1,054 B1,324 B1,818 B
cnpm17.6 B22.0 B29.3 B
npm/cnpm59.8960.1662.03

可以看到,npm 的总下载量大概是 cnpm 的 60 倍左右,且比值逐年增大。世界总人口约 80 亿,中国人口约 14 亿,比值大概是 5.66。所以单从这个维度,简单粗暴的来看,可以得到以下结论:

  1. 近些年中国前端的发展增速要缓于全世界;
  2. 中国的程序员还是太少啊,有 10 倍的增长空间呢(是不是应该涨点工资啥的);

再次声明:结论极其不严谨仅做娱乐,后文不再赘述

前端三大框架

注:以下数据均数据截止到 2022-11-03,取 1 年的数据

我们再来看看前端三大框架:

Recent 1 Year Downloadsreactvue@angular/coreangularTotal
npm779,385,565163,975,718151,297,95926,791,8591,121,451,101
cnpm41,740,15517,861,642730,108750,22561,082,130
npm/cnpm18.79.2207.235.718.4

有意思的事情来了,简单总结下三大框架的数据特点:

  1. npm 下载总量是 cnpm18.4 倍,明显低于总体比例,证明中国的前端研发人数占比很高啊;
  2. Angular 在中国极其不流行啊,VueAngular2 在 npm 上的下载量几乎持平,但在 cnpm 上前者是后者的 250 多倍。终于对于 Vue 在中国流行程度有了量化上的概念了;
  3. ReactVue 的比值在 npm 和 cnpm 上分别为 4.82.3,还是从侧面映证了 Vue 在国内的火热,不过 React 还是强啊;
  4. Angular2Angular1 在 npm 的下载量比率为 5.6,有意思的是在 cnpm 上二者数据几乎持平,也许这些流量都是老教程贡献的吧;

Nodejs 后端框架

Recent 1 Year Downloadsexpress@nestjs/coreeggkoafastifynextnuxtmidwayTotal
npm1,205,982,17669,710,2342,483,60764,789,82828,077,027135,010,94021,462,41663,4851,527,579,713
cnpm18,892,661376,093591,0502,575,526160,469997,533499,81910,85224,104,003
npm/cnpm63.8185.44.225.2175.0135.342.95.963.4
  1. npm 下载总量是 cnpm63.4 倍,基本符合总体数据的比例,但是 Express 占的比重太大了,其实细看其它框架的详情,还是有很多额外信息的;
  2. eggmidway 果然只在中国很流行,毕竟是阿里出品嘛。而且后者的下载量其实很少,估计数据有不少是 Demo 和阿里内部自己贡献的;
  3. 估计受 Vue 在国内的流行影响,nuxt 相对来说在中国更流行,但是 next 的绝对值还是有明显的领先的,更流行只是相对全世界而言;
  4. 出乎意料的,nestjs 在中国竟然这么不流行,比值竟然是最高的,高出均值近 3 倍。个人猜测,可能国外的程序员通常都是全栈,有 Sprint 或 Angular 背景的相对较多,更容易接受 nestjs 的设计理念。而国内前端还是比较局限的,这也是国情不同的一个直接体现啊;

结语

本文的缘起是为了做技术调研,调研除了要考虑宏观因素,还要考虑是否符合团队当前的情况,这才是一个负责任的调研。所以,考虑中国国情还是很有必要的,之前一直没有一个靠谱的指标,现在好了,cnpm 的下载量还是有一定代表性的。

最后,还是要再强调一遍,本文总结的结论仅做娱乐,推论极其不严谨,各位勿要太当真~

你执行力的上限,不会超过你对最佳实践理解的下限。—— 一堂