惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Microsoft Security Blog
Microsoft Security Blog
Jina AI
Jina AI
量子位
博客园 - 叶小钗
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
IT之家
IT之家
S
SegmentFault 最新的问题
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
小众软件
小众软件
Hugging Face - Blog
Hugging Face - Blog
雷峰网
雷峰网
博客园 - 聂微东
美团技术团队
Last Week in AI
Last Week in AI
罗磊的独立博客
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 三生石上(FineUI控件)
WordPress大学
WordPress大学
宝玉的分享
宝玉的分享
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园_首页
V
Visual Studio Blog
大猫的无限游戏
大猫的无限游戏
The Cloudflare Blog

BlogFinder

日常漫步 Vol.24 之漫步前山河 - 雅余 周报 #1-聊聊本周的收获 - Edwin's Blog 我的OpenCode必装插件与Skill Write Something 掌中之物未必在掌握之中 · CRIVU PiliNara,一个更顺手的 PiliPlus 分支 「NekoEcho」:做一个必有回响的猫娘主题博客 2026-05 书影音总结 简化博客主题 - 安迪 我第一次发布 npm 包 拾花小记#45:中考前的二三事 – 小改学习志 黛西花园5月游 #18 枇杷又熟了的五月月报 一些奇奇怪怪的需求?word仿方正书版的几个小操作 - Xiobb's Blog 0419 御温泉之旅 修复了一些bug,网站基本上趋于稳定了 - 新锐博客 又回到四十年前 如何定义成功 迷鹿屋2026已重新上线 科技冰火两重天+一周回顾 ${title} 热度退了,我反而用得更深了-咕咚同学 我到底该不该换个域名? 随身WIFI折腾记 - 安迪 博客撰写体验提升——hexo pro插件 为什么不用相机把屏幕上的接关密码拍下来? 国清寺与天台山 – Ouroboros ★★★★☆《挽救计划》——久违的经济上行感 - Davidの3号基地 删除右键“打开方式”里多余选项 第三周刊_No.53|一切都会被支付两次
Astro 构建阶段自动同步 GitHub 统计数据
I Am I · 2026-05-31 · via BlogFinder

写在前面

目前博客首页 About 区域展示了一些 GitHub 统计信息,例如 Followers、Stars 和 Commit 贡献数

image-20260601191901802

这些数据目前直接写在 config.yaml 中维护,每当数据发生变化时,都需要手动修改配置并重新部署

考虑到博客本身已经通过 GitHub Actions 自动构建,博主打算在 Astro 构建阶段直接调用 GitHub API 拉取最新数据,并将结果编译进最终生成的静态页面

获取 Followers

Followers 的获取最为简单,可以直接通过 Github 用户接口获取

GET https://api.github.com/users/{username}

返回结果中包含当前账号粉丝总量:

{
  "followers": 123
}

获取 Stars

Stars 的获取稍微麻烦一些,GitHub 并没有提供用户总 Star 数接口

因此只能遍历当前用户的所有仓库,并累加每个仓库的 Star

GET https://api.github.com/users/{username}/repos

接口返回的数据中 stargazers_count 即为单个仓库的 Star 数量

[
  {
    "name": "likeMe",
    "stargazers_count": 15
  },
  {
    "name": "MoeGate",
    "stargazers_count": 8
  }
]

该接口默认单次仅返回 30 条数据,我们可以通过请求参数进一步调用

  • per_page=100:将单页数据量提升至接口允许的最大值
  • type=owner:仅筛选当前用户创建的仓库,排除 Fork 仓库

由于 Fork 仓库的 Star 数通常不属于个人项目成果,因此这里通过 type=owner 仅统计当前用户拥有的仓库。

GET https://api.github.com/users/{username}/repos?per_page=100&type=owner

遍历仓库列表并累加 stargazers_count 即可得到当前页的 Star 总数

不过这里还有一个问题,虽然单页100 条数据已经是 GitHub API 允许的最大值,但如果用户拥有超过 100 个仓库,那么一次请求仍然无法获取全部数据。

翻阅官方文档发现,当存在多页数据时,GitHub 会在响应头中返回一个 Link 字段

<https://api.github.com/user/repos?page=2>; rel="next",
<https://api.github.com/user/repos?page=3>; rel="last"

image-20260601153941414

因此我们可以通过正则解析 Link Header 获取最后一页页码

[&?]page=(\d+)>;\s*rel="last"

拿到总页数后,使用并发请求批量拉取剩余分页数据,最后整合所有分页数据,汇总后即可得到总 Star 数

获取 Commits

原本博主以为 Commit 数获取会像前两个数据获取一样简单,结果翻遍 GitHub 官方文档后才发现,并不存在类似的接口

网上较为通用的方案是使用搜索接口,通过返回结果中的 total_count 字段统计提交次数

GET https://api.github.com/search/commits?q=author:{username}

但实际测试后发现,该结果与个人主页显示的贡献数据并不一致

一方面,该接口不会统计私有仓库贡献;另一方面,GitHub 官方文档中也明确说明

Find commits via various criteria on the default branch (usually master)

也就是说,该接口主要基于默认分支进行搜索,很难作为个人总提交次数的准确统计依据

博主甚至考虑直接抓取 GitHub Profile 页面中的绿墙数据,不过这种方案需要解析 HTML 页面,长期维护成本过高,最终放弃该思路

image-20260601155335700

既然 REST API 无法提供准确结果,那么只能继续寻找 GitHub 内部用于生成贡献图的数据来源

后续在研究 GitHub GraphQL 文档时,最终找到了 ContributionsCollection 对象

注意:调用 GitHub GraphQL 接口需要配置个人 Token;若需统计私有仓库提交,Token 需开启 repo 读取权限。

该对象中的 totalCommitContributions 字段,可以精准统计指定时间段内的所有提交贡献,统计口径和 GitHub 贡献绿墙完全一致

query {
  user(login: "5ime") {
    contributionsCollection(
      from: "2026-01-01T00:00:00Z"
      to: "2026-12-31T23:59:59Z"
    ) {
      totalCommitContributions
    }
  }
}

返回结果如下:

{
  "data": {
    "user": {
      "contributionsCollection": {
        "totalCommitContributions": 123
      }
    }
  }
}

不过 totalCommitContributions 仅支持时间区间统计,而我们需要获取账号从注册至今的累计提交数,因此需要按年份拆分统计再汇总

此前获取 Followers 时调用的的用户接口中本身就包含账号创建时间

{
  "followers": 422,
  "created_at": "2016-03-28T07:21:11Z"
}

我们可以直接将账号创建年份作为统计起点

const startYear = new Date(user.created_at).getFullYear();

根据起始年份与当前年份,动态生成需要统计的年份列表

function getCommitYears(startYear: number): number[] {
	const endYear = new Date().getFullYear();

	return Array.from(
		{ length: endYear - startYear + 1 },
		(_, i) => startYear + i,
	);
}

如果按常规思路,每一年单独发起一次 GraphQL 请求,账号注册年限越久,请求次数就越多,写法冗余且效率偏低

好在 GraphQL 支持字段别名,我们可以在单次请求中拼接多个时间段查询,一次性获取全年度数据,极大简化逻辑、减少请求

动态拼接后的多年份查询示例如下

query($login: String!) {
  user(login: $login) {
    y2020: contributionsCollection(
      from: "2020-01-01T00:00:00Z"
      to: "2020-12-31T23:59:59Z"
    ) {
      totalCommitContributions
    }

    y2021: contributionsCollection(
      from: "2021-01-01T00:00:00Z"
      to: "2021-12-31T23:59:59Z"
    ) {
      totalCommitContributions
    }

    y2022: contributionsCollection(
      from: "2022-01-01T00:00:00Z"
      to: "2022-12-31T23:59:59Z"
    ) {
      totalCommitContributions
    }
  }
}

对于绝大多数个人账号,仅需一次请求即可完成全年度数据拉取

最后将每一年的 totalCommitContributions 累加,就能得到账号累计 Commit 贡献数

接入 Astro

完成数据获取后,剩下的工作就比较简单了

博主直接将 GitHub 用户名以及失败时的兜底数据放在 config.yaml 中维护

github:
  username: "5ime"
  fallbacks:
    stars: "4,000"
    commits: "1,000"
    followers: "400"

由于 Astro Frontmatter 会在构建阶段执行,因此这里的 GitHub API 请求仅发生在构建过程中,不会暴露到用户浏览器中

---
import { loadHomeConfig } from '@/utils/config';
import { resolveGitHubStats } from '@/utils/github/github-stats';

const config = loadHomeConfig();
const stats = await resolveGitHubStats(config.content.github);
---

随后将 stats 传递给 About 组件渲染即可

这样一来,每次 GitHub Actions 触发构建时都会自动同步最新统计数据

如果 GitHub API 请求失败,则回退到 config.yaml 中配置的 fallback 值,避免因网络异常或接口限流导致构建中断

写在后面

至此,Followers、Stars 以及 Commit 贡献数都已经能够在 Astro 构建阶段自动获取

对于大多数个人账号而言,整个统计过程通常仅需 2~3 次 API 请求即可完成

并且所有数据都会被直接编译进静态页面,不会增加运行时请求开销~

完整 Github 用户统计信息代码详见 5ime/github-stats