Divide and Conquer
[Github] 내 깃허브 레포 전부 크롤링하기 본문
728x90
한이음으로 깃랩 알게됐는데 USB 없이 파일 공유할 수 있는 것도 편하고 이전 버전 확인할 수 있는 것도 편한데 뭔가 비밀이 많은 친구인 것 같다...
아무튼 깃허브에 내용이 너무 중구난방이라 이름도 정리하고!!! 내용도 정리하려고 한다
뭐랄까 템플릿 만들어서 규칙을 좀 정하려고... 공개/비공개인지 같이 한 사람이 있는지 뭐 이런 거 파악하려는데 레포가 너무 많고 어지러워서 코드를 한 번 짜보려고 한다
이번에 마모도 조사도 끝나는데 이것도 파이어베이스처럼 연동하거나 아니면 깃허브에 실시간으로 반영되게 하면 진짜 편할 듯
1. 설치
pip install requests
pip install pandas
pip install openpyxl
#pip install beautifulsoup4
2. 접근 권한 갱신
링크에 바로 들어갈 수 있으면 좋은데 안 될 수 있음
참고로 링크 주소 https://github.com/settings/personal-access-tokens/3992309
못들어가면 그냥 세팅에 들어가서 처리해도 된다
- Settings
- Personal access tokens
- Fine-grained personal access tokens
- Regenerate token 눌러서 갱신하고
- personal_info.py의 TEST_TOKEN 수정
3. 실행

python3 main.py
실행은 이렇게만 하면 되게끔 내가 코드를 짰다!!!
동작에 필요한 코드를 배포한다
이것도 깃허브로 공유하고 싶은데 내 개인정보가 들어가야 하는 코드가 있어서 비공개로 올려뒀다
참고로 윈도우에서 동작하게끔 짜뒀다!!!해당 부분을 수정하면 된다
main.py
from personal_info import PersonalInfo
github_id = PersonalInfo.ID
access_token = PersonalInfo.TEST_TOKEN
#from get_repo_list import get_github_repos # 레포 리스트 불러오기
from get_repo_list import get_github_repos_options
from save_excel import save_repos_to_excel
options = {
'sort_by': 'updated_at', # 정렬 기준: 'updated_at', 'name', 'language' 등
#'order': 'desc', # 정렬 방향: 'asc'(오름차순), 'desc'(내림차순)
#'private': True, # 공개/비공개 여부 필터링
#'archived': True, # 보관 여부 필터링
#'fork': False, # 포크 여부 필터링
#'template': False, # 템플릿 여부 필터링
#'language': 'Python', # 특정 언어 필터링
#'stars': 0, # 최소 별 수 기준 필터링
# save_excel에서 옵션과 무관하게 저장하도록 내가 수정해 둠
#'contributors': True, # 기여자 수 확인 여부
#'issues': True, # 열린 이슈 수 확인 여부
#'wiki': True, # 위키 존재 여부
#'projects': True # 프로젝트 존재 여부
}
repos = get_github_repos_options(github_id, access_token, options)
print(f"\nTotal repositories owned by {github_id}: {len(repos)}")
for index, repo in enumerate(repos, 1):
#print(f"{index}. {repo['name']} (⭐ {repo['stargazers_count']})")
print(f"{index}. {repo['name']}")
save_repos_to_excel(repos, "github_repos.xlsx", options)
print("\nRepositories saved to 'github_repos.xlsx'")
personal_info.py
아래 부분을 수정하면 된다!
난 테스트 해본다고 뭐가 많은 거라 필요한 것만 추가해도 된다
class PersonalInfo:
ID = "본인것"
PW = "본인것"
ACCESS_TOKEN = "본인것"
TEST_TOKEN = "본인것"
get_repo_list.py
import requests
# 그냥 출력만 함
def get_github_repos(github_id, access_token):
"""
사용자의 깃허브 레포지토리 목록을 최신 업데이트순으로 반환하는 함수
Args:
github_id (str): 깃허브 사용자 ID
access_token (str): 깃허브 개인 액세스 토큰
Returns:
list: 최신 업데이트순으로 정렬된 레포지토리 목록
"""
# API 요청 URL
url = f'https://api.github.com/user/repos'
# API 요청 헤더
headers = {
'Authorization': f'token {access_token}'
}
# 페이지네이션을 처리하여 모든 레포지토리를 불러오는 함수
def fetch_all_repos(url, headers, github_id):
repos = []
page = 1
while True:
# 페이지 번호를 쿼리 파라미터로 추가 (한 번에 30개씩 가져옴)
response = requests.get(url, headers=headers, params={'page': page, 'per_page': 30})
if response.status_code != 200:
print(f"Error: {response.status_code}, {response.text}")
break
data = response.json()
if not data:
break # 더 이상 레포지토리가 없으면 종료
# 본인 소유의 레포지토리만 필터링
for repo in data:
if repo['owner']['login'] == github_id:
repos.append(repo)
page += 1
return repos
# 소유한 레포지토리 불러오기 (기본 순서 유지)
repos = fetch_all_repos(url, headers, github_id)
# 최신 업데이트순으로 정렬
sorted_repos = sorted(repos, key=lambda repo: repo['updated_at'], reverse=True)
# 레포지토리 이름 출력 및 전체 레포지토리 수 출력
#print(f"\nTotal repositories owned by {github_id}: {len(sorted_repos)}")
#for index, repo in enumerate(sorted_repos, 1):
# print(f"{index}. {repo['name']}")
return sorted_repos
# 리스트에 대한 옵션 처리
def get_github_repos_options(github_id, access_token, options=None):
"""
사용자의 깃허브 레포지토리 목록을 옵션에 따라 반환하는 함수.
Args:
github_id (str): 깃허브 사용자 ID
access_token (str): 깃허브 개인 액세스 토큰
options (dict, optional): 레포지토리 필터 및 정렬 옵션
Returns:
list: 정렬 및 필터링된 레포지토리 목록
"""
url = 'https://api.github.com/user/repos'
headers = {'Authorization': f'token {access_token}'}
def fetch_all_repos(url, headers, github_id):
repos = []
page = 1
while True:
response = requests.get(url, headers=headers, params={'page': page, 'per_page': 30})
if response.status_code != 200:
raise Exception(f"Error: {response.status_code}, {response.text}")
data = response.json()
if not data:
break
# 본인 소유의 레포지토리만 필터링
for repo in data:
if repo['owner']['login'] == github_id:
repos.append(repo)
page += 1
return repos
# 레포지토리 가져오기
repos = fetch_all_repos(url, headers, github_id)
# 옵션 처리
if options:
# 정렬 옵션
if 'sort_by' in options:
reverse = options.get('order', 'desc') == 'desc'
repos = sorted(repos, key=lambda repo: repo[options['sort_by']], reverse=reverse)
# 필터 옵션
if 'private' in options:
repos = [repo for repo in repos if repo.get('private') == options['private']]
if 'archived' in options:
repos = [repo for repo in repos if repo.get('archived') == options['archived']]
if 'language' in options:
repos = [repo for repo in repos if repo.get('language') == options['language']]
if 'fork' in options:
repos = [repo for repo in repos if repo.get('fork') == options['fork']]
if 'template' in options:
repos = [repo for repo in repos if repo.get('is_template') == options['template']]
if 'stars' in options:
repos = [repo for repo in repos if repo.get('stargazers_count', 0) >= options['stars']]
return repos
save_excel.py
import requests
import pandas as pd
import os
from openpyxl import load_workbook
from openpyxl.worksheet.hyperlink import Hyperlink
# 엑셀로 저장
def save_repos_to_excel(repos, filename="github_repos.xlsx", options=None):
"""
레포지토리 목록을 엑셀 파일로 저장하는 함수.
Args:
repos (list): 깃허브 레포지토리 목록
filename (str): 저장할 엑셀 파일 이름 (기본값: github_repos.xlsx)
options (dict, optional): 저장할 필드 옵션
"""
# 경로가 지정되지 않았으면 바탕화면에 저장
if not os.path.isabs(filename):
desktop_path = os.path.join(os.path.expanduser("~"), 'Desktop')
filename = os.path.join(desktop_path, filename)
data = []
for repo in repos:
record = {
'Name': repo['name'],
'URL': repo['html_url'],
#'Private': repo.get('private', False),
#'Archived': repo.get('archived', False),
'Private': '비공개' if repo.get('private', False) else '공개',
'Archived': 'O' if repo.get('archived', False) else '',
#'Fork': repo.get('fork', False),
'Fork': 'O' if repo.get('fork', False) else '',
#'Language': repo.get('language', 'Unknown'),
#'Last Updated': repo['updated_at'],
#'Stars': repo.get('stargazers_count', 0),
#'Stars': repo.get('stargazers_count', 0) if repo.get('stargazers_count', 0) > 7 else '', # Stars가 7보다 크면 표시, 아니면 공백
'Stars': repo.get('stargazers_count', 0) if repo.get('stargazers_count', 0) > 0 else '',
# 원래 아래 옵션으로 처리해야 하는데 내가 무조건 추가하게 작성해 둠
#'Template': repo['is_template'],
#'Template': 'O' if repo.get('is_template', False) else '', # Template이 True인 경우 'O', 아니면 공백
'Template': 'O' if repo['is_template'] else '',
'Wiki': 'O' if repo.get('has_wiki', False) else '', # Wiki가 True인 경우 'O', 아니면 공백
'Projects': 'O' if repo.get('has_projects', False) else '',
}
# 이 부분만 살려두는 게 맞아
if options:
if 'contributors' in options and options['contributors']:
contributors_url = repo['contributors_url']
contributors = requests.get(contributors_url).json()
record['Contributors'] = len(contributors) if isinstance(contributors, list) else 0
if 'issues' in options and options['issues']:
record['Issues'] = repo.get('open_issues_count', 0)
if 'wiki' in options and options['wiki']:
record['Wiki'] = repo.get('has_wiki', False)
if 'projects' in options and options['projects']:
record['Projects'] = repo.get('has_projects', False)
data.append(record)
df = pd.DataFrame(data)
df.to_excel(filename, index=False)
# 엑셀 파일을 열어서 URL을 하이퍼링크로 변환
wb = load_workbook(filename)
ws = wb.active
# URL 열을 하이퍼링크로 변환
for row in range(2, len(repos) + 2): # 첫 번째 행은 헤더이므로 2번째 행부터 시작
url_cell = ws.cell(row=row, column=2) # 'URL'은 두 번째 열에 해당
url = url_cell.value
if url:
ws.cell(row=row, column=2).hyperlink = Hyperlink(url, url)
ws.cell(row=row, column=2).style = 'Hyperlink'
# 엑셀 파일 저장
wb.save(filename)
반응형
'2025 > CICD' 카테고리의 다른 글
| [Github] 기존의 폴더를 Git Repo에 연결 (0) | 2023.08.03 |
|---|---|
| Clean Code 클린코드, 로버트 C. 마틴 Robert C. Martin (0) | 2022.05.31 |
| UML 작성 사이트 (0) | 2022.02.04 |
| [Github] 깃허브랑 구글 Colab 연동하기 (0) | 2022.02.03 |
| Notion to [Github] (0) | 2022.02.03 |
Comments