如何通过GitHub API检索关键词对应超1000条的全部commits
超1000条GitHub commit搜索结果获取方案
GitHub网页端和Search Commits API确实对单次查询做了1000条返回的硬限制,直接翻页最多只能拿到前1000条结果,通过时间分片拆分查询范围的方式可以绕开这个限制,拿到全量匹配结果。
核心原理
1000条上限仅对单次生效的查询条件有效,只要把大的查询范围拆成多个小范围查询,保证每个小范围的匹配结果数不超过1000,最后把所有小范围的结果合并去重,就能拿到全部匹配数据。
操作步骤
- 前置准备:生成GitHub个人访问令牌(PAT)用于接口认证,未认证状态下接口请求限额极低,认证后每小时可发起5000次请求,完全覆盖分片查询的需求。调用Search Commits接口时需要在请求头携带
Accept: application/vnd.github.cloak-preview+json,否则无法正常返回commit搜索结果。 - 初始范围统计:以关键词
"data analysis"为例,先以GitHub首个commit上线时间2008-04-01为起始点、当前日期为结束点,拼接查询条件"data analysis" committer-date:2008-04-01..[当前日期],调用接口获取这个大时间范围下的总匹配条数。 - 递归拆分时间区间:如果某个时间区间的总匹配条数大于1000,就把这个区间对半拆成两个连续的子区间,分别统计两个子区间的匹配数;如果子区间匹配数仍然超过1000就继续对半拆分,直到所有子区间的匹配结果数都≤1000为止。
- 全量拉取合并:遍历所有拆分好的时间子区间,对每个区间做分页拉取,单页最大可设置100条结果,逐页翻到无新数据为止。最后对所有拉取到的commit按commit ID去重,避免时间边界处的commit被相邻区间重复收录,就能得到超过1000条的全量匹配结果。
避坑提示
- 时间筛选优先使用
committer-date字段,和GitHub网页端commit搜索的默认筛选逻辑一致,不要用author-date,否则会出现结果和网页端不匹配、漏数的问题。 - 不要用固定时间粒度(比如每月、每季度一个区间)硬拆分,遇到关键词匹配密度高的时间段,固定粒度的区间仍然可能超过1000条上限触发限制,递归对半拆分的方式适配性最强,不管关键词热度多高都能拆到符合要求的粒度。
- 接口请求要做限流重试,触发限流时可以根据响应头里的
X-RateLimit-Reset字段等待限流重置后再发请求,避免被临时封禁。 - 如果本身需要附加筛选条件(比如限定编程语言、所属组织、仓库星级等),可以直接把条件拼到查询参数里,能有效缩小单次查询的结果集大小,减少拆分的区间数量,降低请求次数。
核心逻辑参考代码
import requests from datetime import datetime, timedelta # 替换为自己的GitHub个人访问令牌 GITHUB_TOKEN = "your_personal_access_token" REQ_HEADERS = { "Accept": "application/vnd.github.cloak-preview+json", "Authorization": f"token {GITHUB_TOKEN}" } SEARCH_KEYWORD = '"data analysis"' def get_range_total(start_date: str, end_date: str) -> int: """获取指定时间范围内的匹配commit总数""" query = f"{SEARCH_KEYWORD} committer-date:{start_date}..{end_date}" resp = requests.get( "https://api.github.com/search/commits", headers=REQ_HEADERS, params={"q": query, "per_page": 1} ) resp.raise_for_status() return resp.json()["total_count"] def split_valid_range(start: str, end: str, slice_pool: list): """递归拆分出所有匹配数≤1000的时间区间""" total = get_range_total(start, end) if total <= 1000: slice_pool.append((start, end)) return # 时间对半拆分 start_dt = datetime.strptime(start, "%Y-%m-%d") end_dt = datetime.strptime(end, "%Y-%m-%d") mid_dt = start_dt + (end_dt - start_dt) / 2 mid_str = mid_dt.strftime("%Y-%m-%d") mid_next_str = (mid_dt + timedelta(days=1)).strftime("%Y-%m-%d") split_valid_range(start, mid_str, slice_pool) split_valid_range(mid_next_str, end, slice_pool) if __name__ == "__main__": # 生成所有合法查询分片 valid_slices = [] split_valid_range( "2008-04-01", datetime.now().strftime("%Y-%m-%d"), valid_slices ) # 后续遍历valid_slices中的每个时间区间,分页拉取所有commits即可
内容的提问来源于stack exchange,提问作者Domin D
相关产品推荐
相关产品推荐

