掘金排行前5000大佬 | 掘金文章排行 看这里

17,414 阅读16分钟

文章教你如何做掘金站内数据抓取,数据解析,最后形成排序后的排名。

0821更新: 掘金总点赞量前 5000 排行发布 | 掘金总关注量前 5000 排行(20190821)

0827更新: 【第三方掘金功能】掘金个人数据统计,第三方实现的掘金用户 dashboard

项目起因是我突然想看看掘金站内有哪些优质作者,为了不错过每一个大佬,我选择直接抓取站内所有的文章信息找到作者并进行排名。各位关注 + 文章阅读 一条龙走起!

项目地址 juejin-spider 欢迎 star issue

掘金 spider 和数据分析,主要关注了下面几个排行和统计,排行点击直接查看

先上掘金前50排名,关注一波???? 前5000排名看这里

🎉 等级,👦 关注数,🏠公司

脚本

全站标签抓取

获取掘金站内所有标签信息

npm run tagList

会把标签信息写入到 src/assets/tagList/tagList.json,每个标签包含下面的信息,主要是 titleid

{
  "id": "5597a063e4b08a686ce57030",
  "title": "后端",
  "createdAt": "2015-07-04T00:59:16Z",
  "updatedAt": "2017-06-18T23:34:00Z",
  "color": "#C679FF",
  "icon": "https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/leancloud-assets/d83da9d012ddb7ae85f4.png~tplv-t2oaga2asx-image.image",
  "background": "",
  "showOnNav": true,
  "relationTagId": "",
  "alias": "backend houduan",
  "isCategory": true,
  "entryCount": 19840,
  "subscribersCount": 295562,
  "isSubscribe": false
},

全站文章抓取

将会采集全站所有标签下面的所有文章,采集过程会因为网速和机器性能表现出差异,请各位耐心等待采集完成

这一步采集的数据非常重要,是后面所有分析的基础

采集到的文件会存放在 src/assets/articleData 下面,包含有很多 json 文件,每个文件包含这个标签下的所有专栏文章元信息

npm run allTagData

数组中每个对象

{
  "collectionCount": 5, // 点赞数
  "userRankIndex": 5.4006856695164,
  "buildTime": 1565582852.8327,
  "commentsCount": 2, // 评论数
  "gfw": false,
  "objectId": "5d40d29d518825221b4cbb40",
  "checkStatus": true,
  "isEvent": false,
  "entryView": "",
  "subscribersCount": 0, // 无用
  "ngxCachedTime": 1565627197,
  "verifyStatus": true,
  "tags": [
    {
      "ngxCachedTime": 1565627193,
      "ngxCached": true,
      "title": "React.js",
      "id": "555e99ffe4b00c57d99556aa"
    }
  ],
  "updatedAt": "2019-08-12T04:07:32.818Z",
  "rankIndex": 0.005346156248974,
  "hot": false,
  "autoPass": false,
  "originalUrl": "https://juejin.cn/post/6844903903058739213", // 文章的 url
  "verifyCreatedAt": "2019-07-31T01:36:14.238Z",
  "createdAt": "2019-07-31T01:36:14.238Z",
  "user": {
    "community": {
      "weibo": { "uid": "5345591282", "nickname": "岁月痕迹A88" },
      "wechat": {
        "avatarLarge": "http://thirdwx.qlogo.cn/mmopen/vi_32/cabLXAUXiavVhiaDh2050AOOEToUvnZTWsSNqqKZC4hzPzHABC7fxwv6VxwebIxfKdaRkYDZoic8UXfonLDyiafuiaw/132"
      },
      "github": {
        "username": "lxfriday",
        "avatarLarge": "https://avatars0.githubusercontent.com/u/20264467?v=4",
        "uid": "20264467"
      }
    },
    "collectedEntriesCount": 154, // 点赞数
    "company": "xxx", // 公司
    "followersCount": 35, // 被关注数
    "followeesCount": 70, // 关注数
    "role": "guest", // 用户角色
    "postedPostsCount": 19, // 发布的专栏数
    "level": 2, // 用户等级
    "isAuthor": false,
    "postedEntriesCount": 2, // 分享数?
    "totalCommentsCount": 16, // 总评论数
    "ngxCachedTime": 1565627197,
    "viewedEntriesCount": 1347, // 查看的文章数
    "jobTitle": "前端", // 工作:前端
    "subscribedTagsCount": 166, // 关注的标签数
    "totalCollectionsCount": 120, // 总收藏数
    "username": "云影sky", // 用户名
    "avatarLarge": "https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/7/14/16bf1155693d96c2~tplv-t2oaga2asx-image.image",
    "objectId": "57a0c28979bc440054958498" // 用户 id
  },
  "author": "",
  "screenshot": "https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/7/29/16c3e3d979a96831~tplv-t2oaga2asx-image.image",
  "original": true,
  "hotIndex": 21.2095,
  "content": "给 PureComponent 重新指向构造函数之后,_assign 复制对象属性时, Component 构造函数不会覆盖 PureComponent 构造函数,看下面的例子就明白了。 把 PureComponent 变成 Component,userInfo 可正常变化。",
  "title": "React 源码系列-Component、PureComponent、function Component 分析",
  "lastCommentTime": "2019-08-03T16:53:20.577Z",
  "type": "post",
  "english": false,
  "category": {
    "ngxCached": true,
    "title": "frontend",
    "id": "5562b415e4b00c57d9b94ac8",
    "name": "前端",
    "ngxCachedTime": 1565627098
  },
  "viewsCount": 267, // 浏览量
  "summaryInfo": "经过 处理之后,三个组件的区别就是 type 不一样了 和 看不懂可以看下这篇文章 https://www.zhihu.com/question/34183746 js 中 和 的区别和关系 函数的 属性对象上的 是不可枚举的,所以下面两句 给 PureComponent 重新指向构造函数之后, ...",
  "isCollected": false
}

关注量排行

获取站内浏览量

npm run follower

脚本执行完成会产生两个文件

  • src/assets/calcUserRank/用户followerRank.json 是排行后的元信息
  • src/assets/calcUserRank/用户followerRank.md 按排名编排的 md 文档

点赞排行

获取站内点赞排行

npm run dianzan

脚本执行完成会产生两个文件

  • src/assets/calcDianzanRank/点赞rank.json 是排行后的元信息
  • src/assets/calcDianzanRank/点赞rank.md 按排名编排的 md 文档

例子

全站文章浏览量排行

获取站内浏览量

npm run view

脚本执行完成会产生两个文件

  • src/assets/calcViewRank/浏览量rank.json.json 是排行后的元信息
  • src/assets/calcViewRank/浏览量rank.json.md 按排名编排的 md 文档

全站文章评论量排行

获取站内浏览量

npm run comment

脚本执行完成会产生两个文件

  • src/assets/calcCommentRank/calcCommentRank.json 是排行后的元信息
  • src/assets/calcCommentRank/calcCommentRank.md 按排名编排的 md 文档

技术解析

  • async 并发控制
  • chalk 多彩命令行
  • request 发送 http 请求
  • request-promise 把 request promise 化,方便使用 async

项目辅助工具 dev assistant

  • commitlint 规范 commit message
  • eslint 大家都懂
  • prettier 自动格式化代码
  • husky 提供 git 钩子
  • lint-staged 只对当前变动的文件执行格式化和 eslint 校验
  • jest 测试排序算法正确性

前 1000、前 5000 在20w条数据中是如何计算的

构建小顶堆,不断往堆中添加数据,比堆顶小的直接抛弃,比堆顶大的,替换成堆顶并对二叉树进行调整,维持小顶堆。遍历所有数据之后 小顶堆就是我们要的所有最大值排行,再对这个数组排序依次就可以获取排名了!!!


// 最小值上浮
function heapify(arr, len, i, compareVal) {
  let min = i
  const l = 2 * i + 1
  const r = 2 * i + 2

  if (l < len && compareVal(arr[l]) < compareVal(arr[min])) min = l
  if (r < len && compareVal(arr[r]) < compareVal(arr[min])) min = r

  if (min !== i) {
    swap(arr, i, min)

    heapify(arr, len, min, compareVal)
  }
}

/**
 * 对 target 建堆
 * @param {array} target 堆数组
 * @param {*} compareVal 从 dataUnit 对象获取比对值
 */
function createHeap(target, compareVal = v => v) {
  for (let i = Math.floor((target.length - 1) / 2); i >= 0; i--) {
    heapify(target, target.length, i, compareVal)
  }
}

function findMaxPrev(dataUnit, target, compareVal = v => v) {
  if (compareVal(dataUnit) > compareVal(target[0])) {
    target[0] = dataUnit
    heapify(target, target.length, 0, compareVal)
  }
}

排行

浏览量排行

👀 浏览量,📌 标签

点赞量排行

👍 点赞数,📌 标签

'掘金' === '前端社区' ????

评论量排行

🐶 评论数,📌 标签

分析的内容就是这些了,我还统计了掘金站内的总的文章数和在标签下发布文章的用户总数

  • 掘金站内去重后总的文章数:10w 左右,可能统计有很大误差,去重前是 20+w
  • 标签下发布文章的用户总数:1.5w 左右

看看 npm scripts 开始玩耍吧

npm run all 一行命名抓取数据分析全流程走完,整个流程处理的数据量比较大,需要半个小时左右

"scripts": {
    "all": "npm run tagList && npm run allTagData && npm run dianzan && npm run view && npm run comment && npm run follower",
    "start": "npm run tagList",
    "tagList": "TASK=tagList node App.js",
    "allTagData": "TASK=allTagData node App.js",
    "composeArticleData": "TASK=composeArticleData node App.js",
    "userData": "TASK=userData node App.js",
    "dianzan": "TASK=dianzan node App.js",
    "view": "TASK=view node App.js",
    "comment": "TASK=comment node App.js",
    "follower": "TASK=follower node App.js",
    "lint": "eslint .",
    "test": "jest"
},

最后欢迎大家关注我的 github 和 微信公众号