Как я могу получить список всех репо publi c GitHub с более чем 20 звездами? - PullRequest
3 голосов
/ 02 февраля 2020

Я хотел бы получить список всех опубликованных репозиториев c GitHub с более чем определенным количеством звезд (скажем, 15 или 20). Я могу использовать GitHub GraphQL API, чтобы получить список репо с более чем 15 звездами:

query {
  search(query: "is:public stars:>15", type: REPOSITORY, first:10) {
    repositoryCount
    edges {
      node {
        ... on Repository {
          nameWithOwner
          stargazers {
            totalCount
          }
        }
      }
    }
  }
}

Результат выглядит следующим образом:

{
  "data": {
    "search": {
      "repositoryCount": 704279,
      "edges": [
        { "node": { "nameWithOwner": "freeCodeCamp/freeCodeCamp", "stargazers": { "totalCount": 308427 } } },
        { "node": { "nameWithOwner": "996icu/996.ICU", "stargazers": { "totalCount": 249062 } } },
        { "node": { "nameWithOwner": "vuejs/vue", "stargazers": { "totalCount": 156364 } } },
        { "node": { "nameWithOwner": "facebook/react", "stargazers": { "totalCount": 143121 } } },
        { "node": { "nameWithOwner": "tensorflow/tensorflow", "stargazers": { "totalCount": 140562 } } },
        { "node": { "nameWithOwner": "twbs/bootstrap", "stargazers": { "totalCount": 138369 } } },
        { "node": { "nameWithOwner": "EbookFoundation/free-programming-books", "stargazers": { "totalCount": 136421 } } },
        { "node": { "nameWithOwner": "sindresorhus/awesome", "stargazers": { "totalCount": 125160 } } },
        { "node": { "nameWithOwner": "getify/You-Dont-Know-JS", "stargazers": { "totalCount": 115851 } } },
        { "node": { "nameWithOwner": "ohmyzsh/ohmyzsh", "stargazers": { "totalCount": 102749 } } }
      ]
    }
  }
}

Есть 704 279 репо, но я могу запросить до 100 репо / запросов и просмотреть результаты с помощью курсора. Так что, казалось бы, при достаточном времени это сработает Но, к сожалению, GitHub GraphQL API ограничивает вас первыми 1000 результатами любого запроса, поэтому это не сработает.

Я могу выполнить несколько запросов, используя диапазоны звезд (stars:1000..1500 ), но это не работает, как только вы попадаете в репо с меньшей мощностью звезды (есть более 1000 репо с ровно 123 звезды ).

Я мог бы разбить запрос на несколько способов (например, по дате репо было создано) но это начинает сходить с ума. Есть ли более простой способ получить полный список пабликов c GitHub с 15 или более звездами?

1 Ответ

0 голосов
/ 11 февраля 2020

Разделение как по дате создания, так и по диапазону звезд («сумасшедшее» решение, упомянутое в вопросе) работает довольно хорошо на практике.

Вы можете использовать запрос GraphQL, как этот, чтобы получить только количество репо с 15-20 звезд, которые были созданы в данном диапазоне дат:

query {
  search(query: "is:public stars:15..20 created:2016-01-01..2016-01-09", type: REPOSITORY, first: 1) {
    repositoryCount
  }
}

ответ:

{ "data": { "search": { "repositoryCount": 534 } } }

Для данного диапазона звезд (скажем, 15–20) вы начинаете с длинной диапазон дат (скажем, 2007–2020) и получите счетчик результатов. Если оно превышает 1000, вы делите диапазон дат на две части и получаете результат для каждого. Продолжайте разделять рекурсивно до тех пор, пока каждый диапазон звезд / интервал дат не достигнет 1000 результатов.

Вот код для этого:

def split_interval(a, b):
    d = int((b - a) / 2)
    return [(a, a + d), (a + d + 1, b)]

def split_by_days(stars, day_start, day_end):
    start_fmt = day_start.strftime('%Y-%m-%d')
    end_fmt = day_end.strftime('%Y-%m-%d')
    q = f'stars:{stars} created:{start_fmt}..{end_fmt}')
    c = get_count(q)
    if c <= 1000:
        query_repos(q, out_file)
    else:
        days = (day_end - day_start).days
        if days == 0:
            raise ValueError(f'Can\'t split any more: {stars} / {day_start} .. {day_end}')
        for a, b in split_interval(0, days):
            dt_a = day_start + timedelta(days=a)
            dt_b = day_start + timedelta(days=b)
            split_by_days(stars, dt_a, dt_b)

ranges = [
    (15, 20), (21, 25), (26, 30),
    # ...
    (1001, 1500), (1501, 5000), (5001, 1_000_000)
]
for a, b in ranges:
    stars = f'{a}..{b}'
    split_by_days(stars, datetime(2007, 1, 1), datetime(2020, 2, 2))

Лучше очищать от нижнего диапазона звезд, так как репо более скорее всего, получит звезды, чем потеряет их в процессе очистки.

Для меня это заняло 1102 различных поиска. Вот CSV-файл (~ 50 МБ), собранный с использованием этого подхода со всеми репо, которые имели 15+ звезд на 2020-02-03. См. этот пост и сопровождающий исходный код для получения более подробной информации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...