GitHub является одним из богатейших публичных наборов данных в сфере разработки программного обеспечения. Страницы публичных репозиториев содержат название проекта, описание, количество звёзд и форков, основной язык и темы, а страницы публичных профилей обобщают публичное имя разработчика, биографию, количество репозиториев и подписчиков. Эти данные используются во многих легитимных задачах: отслеживание популярности open-source проектов, анализ тенденций в языках и фреймворках, построение дашбордов библиотек, от которых зависит команда.
В этом руководстве показано, как парсить публичные репозитории и профили GitHub с помощью Python через Crawlbase Crawling API, извлекать нужные поля и экспортировать их в JSON и CSV. Всё здесь ограничено публичными страницами, доступными любому без входа в систему. Руководство не затрагивает приватные репозитории, списки участников организаций, адреса электронной почты и ничего за аутентификацией. Прочитайте раздел о правовом аспекте в конце перед тем, как применять это к реальным данным, и обратите внимание, что GitHub предлагает официальный REST API, который является лучшим инструментом для большинства таких задач.
Что вы создадите
Небольшой скрипт Python, который принимает публичный URL репозитория или профиля GitHub, получает страницу через Crawling API, парсит её с помощью BeautifulSoup и записывает структурированные записи в JSON и CSV. Извлекаемые поля:
- Repository name название проекта в заголовке репозитория.
- Description краткое описание в боковой панели.
- Stars публичное количество звёзд.
- Forks публичное количество форков.
- Watchers количество пользователей, следящих за репозиторием.
- Language and topics основной язык и теги тем репозитория.
- Profile fields для URL пользователя: публичное имя, биография, количество публичных репозиториев и подписчиков.
Обратите внимание на то, что намеренно исключено: никаких адресов электронной почты, приватных репозиториев, списков участников частных организаций и никаких попыток составить досье на кого-либо. Данные профиля описывают реальных людей, поэтому скрипт обращается с ними как с персональными данными и ограничивается грубыми публичными полями.
Почему обычный запрос может не работать на GitHub
GitHub отдаёт большинство содержимого репозиториев и профилей в виде серверно-рендеренного HTML, поэтому обычный запрос часто возвращает полезную разметку. Трудности возникают при масштабировании. GitHub агрессивно ограничивает неаутентифицированный трафик, и плотный цикл с одного IP дата-центра быстро попадает под ограничение или вызов. Анонимный просмотр также даёт более скудную страницу, чем сессия с авторизацией, а разметка меняется между авторизованными и неавторизованными видами, что ломает хрупкие селекторы.
Таким образом, надёжный скрапер GitHub требует запросов, выглядящих как обычные посетители и распределённых по множеству IP-адресов, чтобы ни один из них не превысил лимит. Вы можете построить это самостоятельно с помощью пула ротирующих прокси и собственной логики повторных попыток, но поддержание этого стека в рабочем состоянии составляет основную часть работы. Crawling API объединяет это в один вызов: вы отправляете URL, он получает страницу за надёжным ротирующим IP и возвращает готовый HTML для парсинга. Страницы GitHub достаточно статичны, поэтому здесь подходит обычный токен без JavaScript-рендеринга.
Crawlbase предлагает два типа токенов. Обычный токен получает статический HTML; токен JavaScript (JS) сначала рендерит страницу в настоящем браузере. Страницы репозиториев и профилей GitHub рендерятся на сервере, поэтому обычного токена достаточно и он стоит меньше. Используйте JS-токен только если конкретная нужная вам страница зависит от клиентского рендеринга.
Предварительные требования
Несколько вещей, которые нужно подготовить заранее. Ни одна из них не занимает много времени.
Базовый Python. Вы должны уметь запускать скрипты и устанавливать пакеты с помощью pip. Если парсинг HTML для вас нов, наш обзор как использовать BeautifulSoup в Python охватывает сторону извлечения, а парсинг сайта с помощью Python описывает полный процесс.
Python 3.8 или выше. Проверьте командой python --version. При отсутствии установите с python.org.
Аккаунт Crawlbase и токен. Зарегистрируйтесь, откройте панель управления и скопируйте обычный токен со страницы документации аккаунта. Crawlbase включает до 5 000 бесплатных запросов для старта, и вы платите только за успешные запросы. Обращайтесь с токеном как с паролем: не добавляйте его в систему контроля версий.
Настройка проекта
Создайте изолированное виртуальное окружение, затем установите три библиотеки, необходимые скраперу.
python --version python -m venv github_env source github_env/bin/activate pip install crawlbase beautifulsoup4 pandas
На Windows активируйте с помощью github_env\Scripts\activate вместо строки с source. Три зависимости выполняют всю работу: crawlbase является официальным клиентом для Crawling API, beautifulsoup4 парсит возвращаемый HTML, позволяя извлекать поля по селектору, а pandas преобразует записи в CSV в конце.
Шаг 1: получение страницы публичного репозитория
Начните с получения готовой страницы. Импортируйте CrawlingAPI, инициализируйте его с помощью токена и запросите URL публичного репозитория. Проверяйте код статуса перед парсингом, чтобы сбои были заметны, а не скрыты.
from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) def crawl(page_url): response = api.get(page_url) if response["status_code"] == 200: return response["body"].decode("latin1") print(f"Request failed: {response['status_code']}") return None if __name__ == "__main__": page_url = "https://github.com/TheAlgorithms/Java" html = crawl(page_url) print(html[:500] if html else "No HTML returned")
Тело декодируется как latin1, чтобы избежать проблем с редкими байтами вне UTF-8 в отрендеренном HTML репозитория. В примере используется хорошо известный публичный репозиторий, чтобы вы могли подтвердить работу запроса ещё до написания единого селектора. Запустите и вы должны увидеть реальную разметку GitHub в первых 500 символах, что подтверждает: запрос дошёл до страницы через надёжный IP.
Вызов api.get выше делает больше, чем просто HTTP-запрос. GitHub ограничивает неаутентифицированный трафик, и с одного IP дата-центра быстро попадаешь под лимит, поэтому Crawling API получает каждую страницу через ротирующий резидентский IP и обрабатывает повторные попытки и CAPTCHA за вас. Вы не тратите время на управление пулом прокси и логику отката. Сначала попробуйте на публичном репозитории в рамках бесплатного тарифа.
Шаг 2: парсинг полей репозитория
Имея отрендеренный HTML, загрузите его в BeautifulSoup и извлеките поля репозитория. Заголовок репозитория GitHub предоставляет название через атрибут itemprop, описание находится в боковой панели, а количество звёзд, форков и наблюдателей расположено рядом с иконками Octicon SVG, что делает эти иконки надёжными якорями для чисел рядом с ними. Темы являются тегированными ссылками, а основной язык отображается в списке языков.
from bs4 import BeautifulSoup def text_of(soup, selector): el = soup.select_one(selector) return el.text.strip() if el else None def scrape_repository(html): soup = BeautifulSoup(html, "html.parser") topics = [t.text.strip() for t in soup.select('a[data-octo-click="topic_click"]')] return { "name": text_of(soup, 'strong[itemprop="name"] a'), "description": text_of(soup, "div.Layout-sidebar div.BorderGrid-row p.f4.my-3"), "stars": text_of(soup, "svg.octicon-star ~ strong"), "forks": text_of(soup, "svg.octicon-repo-forked ~ strong"), "watchers": text_of(soup, "svg.octicon-eye ~ strong"), "language": text_of(soup, 'span[itemprop="programmingLanguage"]'), "topics": topics, }
Вспомогательная функция text_of возвращает None, когда селектор не даёт совпадений, поэтому одно отсутствующее поле никогда не прерывает весь парсинг. Селекторы звёзд, форков и наблюдателей используют класс иконки Octicon в качестве якоря и комбинатор следующего брата (~ strong) для получения числа рядом с ней, что надёжнее зависимости от глубоко вложенной цепочки классов. Темы собираются из каждой ссылки topic_click в список.
GitHub периодически обновляет разметку, поэтому сегодняшний рабочий селектор может вернуть None позже. Когда поле возвращается пустым, откройте живую страницу в инструментах разработчика браузера и обновите селектор. Привязка к стабильным якорям, таким как itemprop и классы иконок Octicon, а не к авто-генерируемым утилитарным классам, сводит обслуживание к минимуму.
Шаг 3: парсинг страницы публичного профиля
Страница публичного профиля содержит другой набор полей. Из неё можно извлечь публичное отображаемое имя пользователя, никнейм (хэндл), биографию, количество публичных репозиториев и подписчиков. GitHub помечает отображаемое имя и никнейм стабильными классами vcard, а количество репозиториев и подписчиков расположено рядом с собственными иконками Octicon по той же схеме, что и на странице репозитория.
def scrape_profile(html): soup = BeautifulSoup(html, "html.parser") return { "name": text_of(soup, "span.p-name.vcard-fullname"), "username": text_of(soup, "span.p-nickname.vcard-username"), "bio": text_of(soup, "div.p-note.user-profile-bio div"), "repositories": text_of(soup, "svg.octicon-repo ~ span"), "followers": text_of(soup, "svg.octicon-people ~ span.color-fg-default"), }
Это грубые публичные поля, которые профиль показывает любому не авторизованному посетителю. Скрипт намеренно останавливается здесь. Он не читает электронную почту пользователя, его принадлежность к организациям или содержимое репозиториев и не объединяет профили в запись о конкретном человеке. Публичное имя, биография, количество репозиториев и подписчиков являются агрегированными сигналами о публичном присутствии разработчика; личность человека за ними не является объектом для профилирования.
Шаг 4: сборка и экспорт
Теперь свяжите запрос и парсинг в один работоспособный скрипт, который читает репозиторий и профиль, а затем записывает JSON и CSV с помощью pandas.
import json import time import pandas as pd from crawlbase import CrawlingAPI from bs4 import BeautifulSoup api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) def crawl(page_url): response = api.get(page_url) if response["status_code"] == 200: return response["body"].decode("latin1") print(f"Request failed: {response['status_code']}") return None def main(): repo_url = "https://github.com/TheAlgorithms/Java" profile_url = "https://github.com/torvalds" records = [] repo_html = crawl(repo_url) if repo_html: repo = scrape_repository(repo_html) repo["url"] = repo_url records.append(repo) time.sleep(3) profile_html = crawl(profile_url) if profile_html: profile = scrape_profile(profile_html) profile["url"] = profile_url records.append(profile) with open("github_data.json", "w") as f: json.dump(records, f, indent=2, ensure_ascii=False) pd.DataFrame(records).to_csv("github_data.csv", index=False) print(f"Wrote {len(records)} records to JSON and CSV") if __name__ == "__main__": main()
time.sleep(3) между запросами не является украшением. Задание темпа является единственным наиболее важным фактором в поддержании работоспособности прогона на таком цели с ограничением скорости, как GitHub. Скрипт собирает запись репозитория и запись профиля в один список, записывает структурированный результат в github_data.json и позволяет pandas развернуть те же записи в github_data.csv для работы с таблицей. Список topics чисто сериализуется в JSON и отображается как строка в столбце CSV.
Как выглядят результаты
Запустите полный скрипт и получите чистую запись публичных полей, готовую для загрузки в блокнот, базу данных или таблицу.
[ { "name": "Java", "description": "All Algorithms implemented in Java", "stars": "59.1k", "forks": "19.5k", "watchers": "1.3k", "language": "Java", "topics": ["algorithms", "java", "data-structures"], "url": "https://github.com/TheAlgorithms/Java" }, { "name": "Linus Torvalds", "username": "torvalds", "bio": null, "repositories": "8", "followers": "219k", "url": "https://github.com/torvalds" } ]
Точное форматирование звёзд и подписчиков (59.1k, 219k) взято прямо из отображаемых счётчиков GitHub. Если нужны точные целые числа, точное значение обычно находится в атрибуте title элемента; читайте его вместо видимого текста, когда нужно выполнять математические операции с числами.
Масштабирование на множество репозиториев
Скрипт для одной страницы хорошо обобщается. Для изучения набора проектов ведите список URL репозиториев и запускайте тот же вызов scrape_repository в цикле, накапливая записи перед финальным экспортом.
repo_urls = [ "https://github.com/TheAlgorithms/Java", "https://github.com/pallets/flask", "https://github.com/psf/requests", ] records = [] for url in repo_urls: html = crawl(url) if html: record = scrape_repository(html) record["url"] = url records.append(record) time.sleep(3)
Сохраняйте задержку между запросами, следите за кодами статусов и останавливайтесь, получив нужные данные, а не обходите всё исчерпывающе. Более широкое руководство по работе без попадания под ограничения скорости см. в статье как парсить сайты, не попадая в блокировку. Если вы предпочитаете маршрутизировать трафик через ротирующий пул вместо использования управляемого API, Smart AI Proxy предоставляет ту же резидентскую ротацию в виде прокси-эндпоинта, а наш обзор лучших open-source библиотек для парсинга охватывает варианты парсеров и краулеров для создания собственного стека.
Законно ли парсить GitHub?
Это раздел, который следует прочитать перед написанием производственного кода. Парсинг публичных страниц GitHub для личного или образовательного использования, как правило, защитим, поскольку данные опубликованы для чтения любым желающим без входа в систему. Это не делает его безусловным. Политики допустимого использования GitHub регулируют автоматизированный доступ, а его robots.txt указывает краулерам, какие пути закрыты. Прочитайте оба документа и относитесь к ним как к границе. Никогда не трогайте приватные репозитории, контент за логином или что-либо, для доступа к чему нужны учётные данные, и не перегружайте сайт запросами.
Данные профиля требуют особой осторожности, поскольку они описывают реальных людей. Публичное имя, биография и количество подписчиков являются персональными данными, и во многих юрисдикциях законы о конфиденциальности, такие как GDPR и CCPA, применяются в момент сбора и хранения информации об идентифицируемых лицах, даже если эта информация является публичной. Это означает наличие законного основания для сбора данных, хранение только необходимого и соблюдение запросов на удаление. По возможности агрегируйте данные (счётчики и тенденции по многим репозиториям), а не составляйте досье на конкретных разработчиков, и никогда не публикуйте данные отдельного лица повторно и не объединяйте его присутствие в профиль человека.
Для большинства задач лучшим инструментом является официальный GitHub REST API. Он щедрый, бесплатный для обычного использования и предоставляет чистый структурированный JSON для репозиториев, пользователей, звёзд, форков, языков и тем без разбора HTML. Это санкционированный путь, он переживает изменения разметки и поставляется с задокументированными ограничениями скорости, которые можно планировать. Прибегайте к парсингу только когда конкретная публичная страница содержит что-то, чего API не предоставляет, и держите такую работу небольшой, дозированной и ограниченной публичными, не чувствительными полями. Если вашему проекту нужны данные GitHub в каком-либо реальном масштабе, начните с REST API, а не со скрапера.
Ключевые выводы
- GitHub использует серверный рендеринг, но применяет ограничение скорости. Обычный запрос возвращает разметку, но неаутентифицированный трафик с одного IP быстро попадает под ограничение, поэтому маршрутизируйте запросы через ротирующие IP.
- Обычного токена достаточно. Страницы репозиториев и профилей не требуют JavaScript-рендеринга, поэтому более дешёвый обычный токен получает всё необходимое.
-
Привязывайтесь к стабильным якорям. Парсите поля репозитория через атрибуты
itempropи классы иконок Octicon, а поля профиля через классыvcard, а не через авто-генерируемые утилитарные классы. - Относитесь к данным профиля как к персональным данным. Извлекайте грубые публичные поля, агрегируйте, а не профилируйте отдельных людей, и соблюдайте GDPR и CCPA при хранении.
- Предпочитайте GitHub REST API. Он бесплатный, щедрый и структурированный; парсите только публичные страницы, которые он не охватывает, дозированно и в небольших объёмах.
Часто задаваемые вопросы
Нужен ли обычный токен или JS-токен для GitHub?
Обычный токен. GitHub рендерит страницы репозиториев и профилей на сервере, поэтому статический HTML уже содержит название, описание, количество звёзд и форков, язык, темы и публичные поля профиля. JS-токен сначала рендерит страницы в браузере и стоит дороже, он нужен только для редкого вида GitHub, зависящего от клиентского рендеринга.
Какие данные GitHub безопасно парсить?
Публичные данные, которые может видеть любой не авторизованный посетитель: название публичного репозитория, описание, звёзды, форки, наблюдатели, основной язык и темы, а также имя публичного профиля, биография, количество публичных репозиториев и подписчиков. Приватные репозитории, списки участников организаций, адреса электронной почты и всё за аутентификацией закрыты как по условиям GitHub, так и, для персональных данных, по законам о конфиденциальности.
Стоит ли использовать GitHub REST API вместо парсинга?
Для большинства задач, да. Официальный GitHub REST API бесплатный для обычного использования, щедрый по ограничениям скорости и возвращает чистый JSON для репозиториев, пользователей, звёзд, форков, языков и тем без разбора HTML. Это санкционированный путь, он переживает изменения разметки. Прибегайте к парсингу только когда конкретная публичная страница раскрывает что-то, чего API не предоставляет, и держите такую работу небольшой и дозированной.
Как избежать ограничения скорости при парсинге GitHub?
Поддерживайте низкую частоту запросов с одного IP, добавляйте реальные задержки между запросами, как в time.sleep(3) выше, и маршрутизируйте через ротирующие резидентские IP, чтобы ни один адрес не превышал лимит. Crawling API управляет ротацией и повторными попытками за вас. Следите за кодами статусов и сразу снижайте нагрузку, как только начинаете видеть вызовы или ошибки, а не продолжайте давить сильнее.
Почему количество звёзд и подписчиков представлено строками типа "59.1k"?
Потому что это сокращённый текст, который GitHub отображает на странице, и скрипт читает видимый текст. Когда нужны точные целые числа, обратите внимание на атрибут title элемента, который обычно содержит точное число, и читайте его вместо отображаемого текста перед выполнением арифметических операций.
Можно ли парсить приватные репозитории или адреса электронной почты пользователей?
Нет, и данное руководство намеренно не показывает как. Приватные репозитории находятся за аутентификацией, а адреса электронной почты являются персональными данными, которые GitHub не предоставляет анонимным посетителям. Доступ к ним означал бы обход контроля доступа или сбор персональных данных без законного основания, что противоречит условиям GitHub и законам о конфиденциальности. Для доступа к аккаунтам или организациям, которыми вы управляете, используйте аутентификацию через официальный GitHub REST API.
Обходите любой сайт в масштабе, без борьбы с инфраструктурой.
Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.
