Skip to content

Instantly share code, notes, and snippets.

@Maes95
Created September 21, 2020 13:58
Show Gist options
  • Save Maes95/24bcbc9ed16cc32ee19c890acf826904 to your computer and use it in GitHub Desktop.
Save Maes95/24bcbc9ed16cc32ee19c890acf826904 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
#!/usr/bin/env python
# coding: utf-8
# # Mineria de Repositorios con GitHub
# La mayoria de librerias de este ejemplo están disponibles de manera nativa con Python 3.7+ (si lo tienes instalado). Solo será necesario instalar la librería que hace uso de la API de GitHub:
# ```bash
# $ pip install PyGithub
# ```
import os
import datetime
import random
import json
import pickle
from github import Github
# Generamos un Token para consultar la API de GitHub a través de la libreria GitHub
# - Necesitamos tener una cuenta en GitHub
# - Seguimos [este sencillo tutorial](https://docs.github.com/es/github/authenticating-to-github/creating-a-personal-access-token) para generarlo
# - El token NO es necesario para realizar las consultas, pero la cuota de peticiones que podemos hacer es significatimanete mayor y nos ahorrará mucho tiempo de espera entre peticiones
token ="<token>"
g = Github("maes95",token)
# Podemos realizar consultas sencillas utilizando una serie de parámetros definidos por la librería (que se incluirán en la consulta a la API). Podemos ver en detalle la documentación el [repositorio de la librería](https://github.com/PyGithub/PyGithub) o en su [documentación oficial](https://pygithub.readthedocs.io/en/latest/examples/MainClass.html#search-repositories-by-language)
#
# Al ejecutar un consulta, obtendremos un objeto generador
# - No realiza ninguna consulta o búsqueda
# - Comienza a realizarla al iterar sobre el generador
query="""
language:java
stars:>=500
forks:>=300
created:<2015-01-01
pushed:>2020-01-01
archived:false
is:public
"""
generator = g.search_repositories(query=query)
# Podemos convertir en generador en una lista de repositorios de manera sencilla en Python. En este casi SI se realizará la consulta
# Al iterar (internamente) el generador, crea una lista a partir de la búsqueda
repositories= list(generator)
# Guardamos la información de los repositorios recuperados en un archivo binario de Python
# - Utilizamos la librería pickle
# - Las búsquedas en la API de GitHub pueden variar con el tiempo, podemos obtener más o menos repositorios al realizar la misma búsqueda
# - Lo guardamos con un timestamp para diferenciarlo inequivocamente
date=str(datetime.datetime.now())
with open('repos_%s.pickle'%date, 'wb') as f:
pickle.dump(repositories, f)
print("Total repos: %d"%len(repositories))
# Leemos el archivo binario. Si hemos cerrado y abierto de nuevo el notebook, el timestamp habrá cambiado y tendremos que ponerlo a mano.
repos = 'repos_%s.pickle'%date
#repos = 'repos_2020-09-21 12:03:13.421536.pickle'
with open(repos, 'rb') as f:
repositories = pickle.load(f)
# Sobre los repositorios encontramos, podemos realizar filtros. Para empezar, vamos a filtrar los repositorios por su número de commits (para quedarnos solo con ciertos repositorios)
MAX_COMMITS = 10000
MIN_COMMITS = 1000
filtered_repos = []
for repo in repositories:
commits = repo.get_commits().totalCount
if commits >= MIN_COMMITS and commits <= MAX_COMMITS:
filtered_repos.append(repo)
print("Total projects %d"%len(filtered_repos))
# Realizamos un nuevo filtro. Vamos a quedarnos solo con los proyectos que tienen almenos uno de los distintos sistemas de construcción más típicos en Java.
#
# - Maven (pom.xml)
# - Gradle (build.gradle)
# - Ant (build.xml)
#
# Esta consulta es algo más laboriosa que la anterior, ya que tiene que comprobar que alguno de los archivos del repositorio coincide con los archivos de configuración que definimos. Tenemos que tener cuidado en el orden de las consultas, para que resulten lo más optimas posible.
filtered_repos_2 = []
for repo in filtered_repos:
contents = repo.get_contents("")
for content_file in contents:
if content_file.path in ["build.gradle", "pom.xml", "build.xml"]:
filtered_repos_2.append(repo)
break
print("Total repos: %d"%len(filtered_repos_2))
# Además de filtrar los proyectos por ficheros que contengan, también podemos inspeccionar ficheros concretos (incluso grupos de ficheros, por ejemplo, con terminación .java)
filtered_repos_3 = []
for repo in filtered_repos_2:
contents = repo.get_contents("")
isAndroid = False
for file in contents:
if file.path == "build.gradle":
isAndroid = 'com.android.tools.build' in str(repo.get_contents("build.gradle").decoded_content)
break
if not isAndroid: filtered_repos_3.append(repo)
print("Total projects %d"%len(filtered_repos_3))
# Puede que a pesar de los filtros que realizamos, obtengamos un gran número de repositorios, demasiados para el experimento que queremos realizar. Por ello, podemos limitar la muestra de repositorios escogiendo un número significativo al azar
# Seleccionamos 30 proyectos de manera aleatoria
sampling = random.choices(filtered_repos_3, k=30)
for project in sampling:
project_name = project.full_name.split("/")[1]
print(project.full_name)
# Ya tenemos seleccionados los repositorios. Pero GitHub no nos garantiza que estos repositorios siempre sigan ahí, podrían:
# - Ser borrados
# - Convertirse en repositorios privados
# - Desaparecer commits o ramas de su histórico
#
# Por ello, una buena idea nada más seleccionarlos es clonarlos y tener una copia en local. Para ellos creamos una carpeta dónde guardarlos.
#
# La creación de una carpeta con cualquier lenguaje de programación es trivial, especialmente con las librerias actuales, pero me gustaría ilustrar un concepto sencillo, pero eficaz: no sobrescribir nunca los recursos creados. Podemos perder información previa al sobrescribirla con la nueva. En el caso de un directorio, simplemente no nos dejara crearlo, pero a la hora de crear archivos, los reemplazará sin preguntarnos.
folder_name = 'repositories'
if not os.path.exists(folder_name):
print("Folder %s created!"%folder_name)
os.mkdir("repositories")
else:
print("Folder %s already exist"%folder_name)
# Clonamos de forma iterativa los repositorios en la nueva carpeta. Si ejecutamos de nuevo la siguiente celda, no clonará de nuevo los proyectos ya existentes
for project in sampling:
project_name = project.full_name.split("/")[1]
project_folder = "%s/%s" % (folder_name, project_name)
# CHECK IF PROJECT EXISTS
if os.path.exists(project_folder):
print(" -> Project %s already exist in local folder!"%project.full_name)
else:
get_ipython().system('git clone $project.clone_url $project_folder')
print(" -> Project %s cloned!"%project_name)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment