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

推荐订阅源

V
Visual Studio Blog
阮一峰的网络日志
阮一峰的网络日志
博客园_首页
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Last Week in AI
Last Week in AI
罗磊的独立博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - 【当耐特】
T
Tailwind CSS Blog
美团技术团队
Y
Y Combinator Blog
I
InfoQ
C
Check Point Blog
Microsoft Security Blog
Microsoft Security Blog
G
Google Developers Blog
Google DeepMind News
Google DeepMind News
博客园 - Franky
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
G
GRAHAM CLULEY
爱范儿
爱范儿
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Google DeepMind News
Google DeepMind News
F
Fortinet All Blogs
A
Arctic Wolf
Hugging Face - Blog
Hugging Face - Blog
S
Security Affairs
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
小众软件
小众软件
云风的 BLOG
云风的 BLOG
酷 壳 – CoolShell
酷 壳 – CoolShell
Recent Announcements
Recent Announcements
H
Heimdal Security Blog
博客园 - 司徒正美
Latest news
Latest news
H
Hacker News: Front Page
H
Help Net Security
Know Your Adversary
Know Your Adversary
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
Secure Thoughts
AWS News Blog
AWS News Blog
V
Vulnerabilities – Threatpost
NISL@THU
NISL@THU
C
Cyber Attacks, Cyber Crime and Cyber Security
L
LangChain Blog
The GitHub Blog
The GitHub Blog
有赞技术团队
有赞技术团队
The Cloudflare Blog
I
Intezer
N
News and Events Feed by Topic

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|一切都会被支付两次 安卓APP通话记录与录音上传踩坑记录 - 子舒的博客 天量下跌 inBox 笔记 2.3.8,把工具栏交给了你-咕咚同学 我把小龙虾搬到了微信-咕咚同学 安好 - 响石潭 Compound Engineering Plugin:让每个工程单元都比上一个更容易 MOSS-TTS Family:开源高质量语音与声音生成模型家族深度解析 Crawl4AI:专为 LLM 设计的开源 Web 爬虫与数据抓取工具 Build Your Own X:从零实现你最喜欢的技术——程序员进阶的终极资源清单 Anthropic Skills:用文件夹教 Claude 专业技能的开源框架 1年的去月球(下) - 梅之夏 欢迎回来。 简单讲讲 ASN.1 与 OID DTV - 直播聚合客户端 5.22-5.27 – 不兴江 还没去过鸭川 – 不兴江 张晶晶同学三刷林志颖 关于我 – 不兴江 爱与嫉妒 – 不兴江 港股被持续做空 备案码花了四百块-咕咚同学 一句话生成封面:我给公众号做了4种风格的AI封面生成技能 「官」方認證 再谈费曼学习法 2026-05-28T00:34:11+08:00 2026-05-28T00:28:45+08:00 离谱的英语学习指南:基于AI的英语进阶系统方法论 iii:零集成架构的后端统一运行时 Claude Code Harness:让 Claude Code 工作有迹可循的工程化框架 Heretic:全自动移除大语言模型审查机制的开源工具 MarkItDown:微软开源的万能文档转 Markdown 利器 Harness:让 Claude Code 秒变多智能体协作工厂 这段时间尽折腾AI Agent了,确实极大地提高了效率 近期动态:两个新站点正式上线啦 误判解除!zhouayuan.com 腾讯安全申诉成功 - 周阿源|玩具设计・插画日常・生活随笔 Ralph:让 AI 编码工具自主循环跑完所有 PRD 任务的量产神器 全都违法 – 个人工作记录 关于zhouayuan.com被误判 “含违规信息” 的说明与申诉记录 - 周阿源|玩具设计・插画日常・生活随笔 小米 MiMo v2.5 Pro 白嫖 最大的人间清醒,兜里有钱,但是不花。 夜晚靓歌(12):于文文现场solo - 王志勇的Blog 今日插画:风扬起的倔强 - 周阿源|玩具设计・插画日常・生活随笔 回门习俗 独立网卡 - 忘记了回忆 500亿入股人工智能企业 从命令行到桌面智能体-咕咚同学 第一性原理读书笔记 行者微评论223-加班の守株待兔-博客|政治与时事-风雨行者 ZOZO开源物理接触求解器:GPU加速的可扩展仿真引擎 OpenStock:开源股票市场交易平台技术深度解析 MoneyPrinterTurbo:基于AI的全自动短视频生成工具深度解析 Claude-Mem:为 Claude Code 构建的持久化记忆压缩系统 Twenty:可代码化定制的企业级开源 CRM 平台技术深度解析 2026-05-26T22:59:17+08:00 企业级开源大模型部署平台 GPUStack 实战教程 1年的去月球(上) - 梅之夏 Sevalla - 静态网站托管服务 不用翻墙、不用注册、不用月费,普通人也能用上 Claude Code 装修灯具要注意⚠️ 黄梅天先锋 - 游子微博 公安备案顺利办结,站点备案全部完成 - 周阿源|玩具设计・插画日常・生活随笔 第三次兑换天猫超市卡了宗宗酱-三维狐少儿编程 Don't think, feel. - Rolen's Blog 人这一辈子,到底图个什么 博客迁移 - Edwin's Blog 情感赛道写作模板 再现本轮行情的典型特征 裁员与平常心-咕咚同学 别让“偷懒”,成为隐私泄露的破绽 片刻 - Jdeal | Life is like a Design.
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