Парсинг данных из GitHub (активность крипто-проектов)
GitHub-активность — один из немногих on-chain заменимых сигналов фундаментального анализа крипто-проектов. Количество коммитов, число активных контрибьюторов, частота релизов, скорость закрытия issues — всё это публично доступно и коррелирует с реальной разработкой. Сервисы вроде Santiment и Electric Capital используют именно эти метрики как часть developer activity score.
GitHub REST API vs GraphQL API
GitHub предоставляет два API. Для сбора данных о репозиториях лучше подходит GraphQL API v4 — позволяет за один запрос получить данные, на которые REST потратил бы 5–10 запросов:
query RepoActivity($owner: String!, $repo: String!) {
repository(owner: $owner, name: $repo) {
stargazerCount
forkCount
defaultBranchRef {
target {
... on Commit {
history(first: 100) {
totalCount
nodes {
committedDate
author { name email }
additions
deletions
}
}
}
}
}
releases(last: 5) {
nodes { tagName createdAt }
}
issues(states: OPEN) { totalCount }
pullRequests(states: MERGED, last: 30) {
nodes { createdAt mergedAt }
}
}
}
Один запрос — и вы имеете коммиты за 100 последних дней, releases, open issues, merged PRs. REST API потребовал бы отдельных запросов к /commits, /releases, /issues, /pulls.
Аутентификация: Personal Access Token (PAT) с public_repo scope. Без токена rate limit — 60 req/час, с токеном — 5000 req/час для REST, 5000 points/час для GraphQL. PAT создаётся через Settings → Developer settings → Fine-grained tokens.
Что именно собирать
Для developer activity dashboard стандартный набор метрик:
| Метрика | Endpoint / поле | Частота сбора |
|---|---|---|
| Commit count (30d/90d) | defaultBranchRef.target.history.totalCount |
Ежедневно |
| Active contributors | defaultBranchRef.target.history.nodes[].author |
Ежедневно |
| Code churn (additions + deletions) | history.nodes[].additions/deletions |
Ежедневно |
| Stars / forks | stargazerCount, forkCount |
Ежедневно |
| Issues velocity | Open issues + closed last 30d | Еженедельно |
| PR merge time (median) | pullRequests.mergedAt - createdAt |
Еженедельно |
| Release cadence | Даты последних releases |
Еженедельно |
Contributor deduplication: один разработчик может коммитить с разными email. Нормализация через GitHub login (если коммит ассоциирован с аккаунтом) или fuzzy matching по имени. Боты (Dependabot, renovate, github-actions) нужно исключать из подсчёта человеческих контрибьюторов.
Обработка rate limits и большого числа репозиториев
Crypto-индекс из 200 проектов — каждый с 5–10 репозиториями — это 1000–2000 репозиториев. При ежедневном сборе и 5000 points/час лимите нужно управлять бюджетом запросов.
Стоимость GraphQL запроса = sum(requested_nodes). Сложный запрос с 100 коммитами стоит ~100 points. Для 2000 репозиториев нужно ~200k points — это 40 часов при одном PAT токене.
Решения:
-
Несколько PAT токенов с ротацией. Пул из 10 токенов даёт 50k points/час — достаточно для ежедневного сбора 2000 репозиториев.
-
Incremental collection: не собирать всю историю каждый раз. Хранить
last_collected_at, запрашивать только новые коммиты черезsinceпараметр. -
Prioritization: "горячие" репозитории (высокая активность, большой TVL проекта) собираются чаще, "холодные" — раз в неделю.
import httpx
import asyncio
from collections import deque
class GitHubRateLimiter:
def __init__(self, tokens: list[str]):
self.tokens = deque(tokens)
self.current_remaining = {t: 5000 for t in tokens}
async def get_token(self) -> str:
# Rotate to token with most remaining points
token = max(self.tokens, key=lambda t: self.current_remaining[t])
if self.current_remaining[token] < 100:
await asyncio.sleep(3600) # Wait for reset
return token
async def graphql(self, query: str, variables: dict) -> dict:
token = await self.get_token()
async with httpx.AsyncClient() as client:
resp = await client.post(
"https://api.github.com/graphql",
json={"query": query, "variables": variables},
headers={"Authorization": f"Bearer {token}"},
)
# Update remaining from response headers
cost = resp.json().get("data", {}).get("rateLimit", {}).get("cost", 1)
self.current_remaining[token] -= cost
return resp.json()
Маппинг проектов к репозиториям
Составление и поддержание списка project → github_repos — отдельная задача. Источники:
- Electric Capital Developer Report публикует open-source маппинг проектов к репозиториям на GitHub
-
DeFiLlama имеет поле
githubв данных протоколов:GET https://api.llama.fi/protocolsвозвращает список протоколов с github URL - Manual curation: для новых проектов или проектов с нестандартными github org именами
Важный нюанс: крупные проекты (Ethereum, Solana, Uniswap) имеют десятки репозиториев в организации. Суммировать активность по всей org нужно с фильтрацией — repo-зеркала, форки, документационные репозитории искажают метрики.
Хранение и агрегация
TimescaleDB или ClickHouse для time-series метрик. Схема:
CREATE TABLE github_metrics (
project_id INTEGER,
repo_full_name TEXT,
measured_date DATE,
commits_30d INTEGER,
contributors_30d INTEGER,
additions_30d BIGINT,
deletions_30d BIGINT,
stars INTEGER,
open_issues INTEGER,
PRIMARY KEY (repo_full_name, measured_date)
);
-- Агрегат по проекту (все его репозитории)
CREATE VIEW project_dev_activity AS
SELECT project_id, measured_date,
SUM(commits_30d) AS total_commits,
COUNT(DISTINCT repo_full_name) AS active_repos,
SUM(contributors_30d) AS total_contributors
FROM github_metrics
GROUP BY project_id, measured_date;
Нормализованный developer activity score: log(commits + 1) × log(contributors + 1) — логарифмирование сглаживает outliers (один проект с 10k commits не должен доминировать в рейтинге).







