Como fazer raspagem de dados em repositórios do GitHub usando Python

Já se perguntou como fazer raspagem de dados no GitHub? Descubra aqui!
18 min read
Github scraping guide

Este tutorial abordará:

Por que raspar dados dos repositórios do GitHub?

Existem vários motivos para fazer raspagem de dados de repositórios do GitHub. Os mais populares são:

  • Seguir as tendências tecnológicas: ao acompanhar as estrelas e os lançamentos do repositório, você pode acompanhar as tendências atuais em linguagens de programação, estruturas e bibliotecas. Fazer raspagem de dados no GitHub permite analisar quais tecnologias estão ganhando popularidade para monitorar seu crescimento e identificar tendências emergentes. Esses dados podem orientar as decisões sobre adoção de tecnologia, desenvolvimento de habilidades e alocação de recursos.
  • Obtenha acesso a uma rica base de conhecimento de programação: o GitHub é um tesouro de projetos de código aberto, amostras de código e soluções. Isso significa que você pode reunir uma grande quantidade de conhecimento de programação e melhores práticas na plataforma. Isso é útil para fins educacionais, melhorando as habilidades de codificação e entendendo como diferentes tecnologias são implementadas.
  • Obtenha informações valiosas sobre o desenvolvimento colaborativo: os repositórios mostram como os desenvolvedores colaboram por meio de pull requests, problemas e discussões. Ao coletar esses dados, você pode estudar padrões de colaboração para ajudar a criar estratégias de trabalho em equipe, melhorar o gerenciamento de projetos e aperfeiçoar os processos de desenvolvimento de software.

O GitHub não é a única plataforma baseada em nuvem para hospedar repositórios git, e há muitas alternativas. No entanto, continua sendo a escolha preferida para raspagem de dados devido a:

  1. Grande base de usuários
  2. Alta atividade de usuários
  3. Reputação estabelecida

Em particular, os dados do GitHub são valiosos para monitorar tendências tecnológicas, descobrir bibliotecas e estruturas e melhorar o processo de desenvolvimento de software. Essas informações desempenham um papel fundamental para se manter à frente da concorrência no mundo da TI.

Bibliotecas e ferramentas de raspagem de dados do GitHub

O Python é amplamente considerado uma excelente linguagem para raspagem de dados na web, graças à sua sintaxe simples, natureza amigável ao desenvolvedor e ampla variedade de bibliotecas. Veja por que é a linguagem de programação recomendada para fazer raspagem de dados no GitHub. Saiba mais em nosso guia detalhado sobre como fazer raspagem de dados usando Python.

A próxima etapa é selecionar as bibliotecas de raspagem de dados mais adequadas entre a ampla variedade de opções disponíveis. Para tomar uma decisão informada, você deve primeiro explorar a plataforma em seu navegador. Abra o DevTools e dê uma olhada nas chamadas AJAX feitas pelas páginas do repositório no GitHub. Você notará que a maioria delas pode ser ignorada. Na verdade, a maioria dos dados da página está incorporada nos documentos HTML retornados pelo servidor.

Isso implica que uma biblioteca para fazer requisições HTTP ao servidor, combinada com um analisador de HTML, será suficiente para a tarefa. Então, você deve optar por:

  • Solicitações: a biblioteca de cliente HTTP mais popular no ecossistema Python. Isso simplifica o processo de envio de solicitações HTTP e o tratamento de suas respostas correspondentes. 
  • Beautiful Soup: uma biblioteca abrangente de análise de HTML e XML. Ela oferece uma API robusta de navegação DOM e extração de dados para raspagem de dados na web.

Graças ao Requests e ao Beautiful Soup, você pode realizar a raspagem de dados do GitHub de forma eficaz usando Python. Vamos entrar nos detalhes de como fazer isso!

Construa um raspador de dados de repositórios do GitHub com o Beautiful Soup

Siga este tutorial passo a passo e aprenda como fazer raspagem de dados no GitHub com Python. Quer pular todo o processo de codificação e raspagem de dados? Compre um conjunto de dados do GitHub em vez disso.

Passo 1: configuração do projeto em Python

Antes de começar, certifique-se de atender aos pré-requisitos abaixo:

Agora você tem tudo o que é necessário para configurar um projeto em Python!

Execute os seguintes comandos no terminal para criar uma pasta github-scraper e inicializá-la com um ambiente virtual Python


mkdir github-scraper
cd github-scraper
python -m venv env

No Windows, execute o comando abaixo para ativar o ambiente:

env\Scripts\activate.ps1

Enquanto estiver no Linux ou macOS, execute:

./env/bin/activate

Em seguida, adicione um arquivo scraper.py contendo a linha abaixo na pasta do projeto:

print('Hello, World!')

No momento, seu raspador de dados do GitHub imprime apenas “Hello, World!” mas em breve conterá a lógica para extrair dados de repositórios públicos.

Você pode iniciar o script com:

python scraper.py

Se tudo correr conforme o planejado, ele deve imprimir esta mensagem no terminal:

Hello, World!

Agora que você sabe que funciona, abra a pasta do projeto em seu IDE Python favorito.

Perfeito! Prepare-se para escrever códigos em Python. 

Passo 2: instalar as bibliotecas de raspagem de dados

Conforme mencionado anteriormente, o Beautiful Soup e Requests ajudam você a realizar raspagem de dados no GitHub. No ambiente virtual ativado, execute o seguinte comando para adicioná-los às dependências do projeto:

pip install beautifulsoup4 requests

Limpe scraper.py e importe os dois pacotes com estas linhas: 

import requestsfrom bs4 import BeautifulSoup
# scraping logic...

Certifique-se de que seu IDE Python não reporte nenhum erro. Você pode receber alguns avisos por causa de importações não utilizadas. Ignore-os, pois você está prestes a usar essas bibliotecas de raspagem de dados para extrair dados do repositório do GitHub!

Passo 3: baixe a página escolhida

Selecione um repositório do GitHub do qual você deseja recuperar dados. Neste guia, você verá como fazer raspagem de dados no repositório luminati-proxy. Lembre-se de que qualquer outro repositório funcionará, pois a lógica de raspagem de dados será a mesma.

Aqui está a aparência da página escolhida no navegador:

Guia de seleção do repositório do GitHub

Armazene o URL da página de destino em uma variável:

url = 'https://github.com/luminati-io/luminati-proxy'

Em seguida, baixe a página com requests.get():

page = requests.get(url)

Nos bastidores, o requests faz uma solicitação HTTP GET para essa URL e salva a resposta produzida pelo servidor na variável page. Você deve focar sua atenção em seu atributo de texto. Ele contém o documento HTML associado à página da web escolhida. Verifique isso com uma instrução simples de print:

print(page.text)

Execute o raspador de dados e você verá isso no terminal:


<!DOCTYPE html>
<html lang="en" data-color-mode="dark" data-light-theme="light" data-dark-theme="dark"  data-a11y-animated-images="system" data-a11y-link-underlines="false">
      <head>
        <meta charset="utf-8">
      <link rel="dns-prefetch" href="https://github.githubassets.com">
      <link rel="dns-prefetch" href="https://avatars.githubusercontent.com">
      <link rel="dns-prefetch" href="https://github-cloud.s3.amazonaws.com">
      <link rel="dns-prefetch" href="https://user-images.githubusercontent.com/">
      <link rel="preconnect" href="https://github.githubassets.com" crossorigin>
      <link rel="preconnect" href="https://avatars.githubusercontent.com">
<!-- Omitted for brevity... -->
Awesome! Let’s now learn how to parse this

Etapa 4: analisar o documento HTML

Para analisar o documento HTML recuperado acima, passe-o para o Beautiful Soup:

soup = BeautifulSoup(page.text, 'html.parser')

O construtor BeautifulSoup() recebe dois argumentos:

  1. A string contendo o conteúdo HTML: aqui armazenada na variável page.text.
  2. O analisador que o Beautiful Soup usará: “html.parser” é o nome do analisador HTML embutido do Python.

BeautifulSoup() irá analisar o HTML e retornar uma estrutura em árvore explorável. Em detalhes, a variável soup fornece métodos eficazes para selecionar elementos da árvore DOM, como:

  • find(): retorna o primeiro elemento HTML correspondente à estratégia seletora passada como parâmetro.
  • find_all(): retorna a lista de elementos HTML que correspondem à estratégia do seletor de entrada.
  • select_one(): retorna o primeiro elemento HTML correspondente ao seletor CSS passado como parâmetro.
  • select(): retorna a lista de elementos HTML que correspondem ao seletor CSS de entrada.

Observe que esses métodos também podem ser chamados em um único nó na árvore. Além deles, um objeto de nó do Beautiful Soup também expõe:

  • find_next_sibling(): retorna o primeiro nó HTML dentro dos irmãos do elemento que corresponde ao seletor CSS fornecido.
  • find_next_siblings(): retorna todos os nós HTML dentro dos irmãos do elemento que correspondem ao seletor CSS passado como parâmetro.

Graças a essas funções, você está pronto para usar o GitHub. Vamos ver como!

Etapa 5: familiarize-se com a página de destino

Antes de mergulhar na codificação, há outra etapa crucial a ser concluída. Fazer raspagem de dados em um site consiste em selecionar os elementos HTML de interesse e extrair dados deles. Definir uma estratégia de seleção eficaz nem sempre é fácil, e você deve passar algum tempo analisando a estrutura da sua página escolhida. 

Assim, abra a página escolhida do GitHub no navegador e familiarize-se com ela. Clique com o botão direito do mouse e selecione “Inspecionar” para abrir o DevTools:

Inspecionando a página do GitHub com DevTools

Ao examinar o código HTML, você perceberá que o site não qualifica muitos de seus elementos com classes ou atributos exclusivos. Portanto, geralmente é difícil navegar até o elemento desejado e pode ser necessário passar pelos irmãos de maneira complicada.

No entanto, não se preocupe. Criar estratégias de seleção eficazes para o GitHub pode não ser fácil, mas também não é impossível. Continue inspecionando a página no DevTools até se sentir pronto para raspá-la!

Etapa 6: extrair os dados do repositório

O objetivo é extrair dados úteis do repositório do GitHub, como estrelas, descrição, última confirmação e assim por diante. Então, você precisa inicializar um dicionário Python para acompanhar esses dados. Adicione ao seu código:

repo = {}

Primeiro, inspecione o elemento do nome:

Inicializando o dicionário Python para extração de dados do Repositório

Note que ele tem um atributo distinto itemprop="name” . Selecione-o e extraia seu conteúdo de texto com:

name_html_element = soup.select_one('[itemprop="name"]')name = name_html_element.text.strip()

Dado um nó do Beautiful Soup, use o método get_text() para acessar seu conteúdo de texto. 

Se você inspecionar name_html_element.text no depurador, você verá:

\nluminati-proxy\n

Os campos de texto do GitHub tendem a conter espaços e novas linhas. Livre-se deles com a função Python strip().

Logo abaixo do nome do repositório, está o seletor de ramificação:

Extraindo o nome do repositório usando o Beautiful Soup

Observe que não há uma maneira fácil de selecionar o elemento HTML que armazena o nome da ramificação principal. O que você pode fazer é selecionar o nó .octicon-git-branch e depois procurar o alvo span em seus irmãos:

git_branch_icon_html_element = soup.select_one('.octicon-git-branch')
main_branch_html_element = git_branch_icon_html_element.find_next_sibling('span')
main_branch = main_branch_html_element.get_text().strip()

O padrão para alcançar um elemento de interesse por meio dos irmãos de um ícone é bastante eficaz no GitHub. Você o verá usado várias vezes nesta seção.

Agora, foque no cabeçalho da ramificação:

Selecionar o nome da ramificação principal por meio do elemento irmão no GitHub

Extraia o último horário de confirmação com:

relative_time_html_element = boxheader_html_element.select_one('relative-time')
latest_commit = relative_time_html_element['datetime']

Dado um nó, você pode acessar seus atributos HTML como em um dicionário Python.

Outra informação importante nesta seção é o número de confirmações:

Colete com o padrão de ícone descrito anteriormente:

history_icon_html_element = boxheader_html_element.select_one('.octicon-history')
commits_span_html_element = history_icon_html_element.find_next_sibling('span')
commits_html_element = commits_span_html_element.select_one('strong')
commits = commits_html_element.get_text().strip().replace(',', '')

Note que find_next_sibling() dá acesso somente aos irmãos de nível superior. Para selecionar um de seus elementos filhos, você deve primeiro obter o elemento irmão e depois chamar select_one()conforme feito acima.

Como os números acima de mil contêm uma vírgula no GitHub, remova-a com o método replace() do Python.

Em seguida, coloque sua atenção na caixa de informações à direita:

Selecione com:

bordergrid_html_element = soup.select_one('.BorderGrid')

Inspecione o elemento de descrição:

Selecionando e inspecionando o elemento de descrição no GitHub

Novamente, você pode selecioná-lo por meio de um irmão:

about_html_element = bordergrid_html_element.select_one('h2')
description_html_element = about_html_element.find_next_sibling('p')
description = description_html_element.get_text().strip()

Em seguida, aplique o padrão de ícone para recuperar as estrelas, watchers e forks do repositório.

Foque nos ícones e depois em seus irmãos de texto:

star_icon_html_element = bordergrid_html_element.select_one('.octicon-star')
stars_html_element = star_icon_html_element.find_next_sibling('strong')
stars = stars_html_element.get_text().strip().replace(',', '')

eye_icon_html_element = bordergrid_html_element.select_one('.octicon-eye')
watchers_html_element = eye_icon_html_element.find_next_sibling('strong')
watchers = watchers_html_element.get_text().strip().replace(',', '')

fork_icon_html_element = bordergrid_html_element.select_one('.octicon-repo-forked')
forks_html_element = fork_icon_html_element.find_next_sibling('strong')
forks = forks_html_element.get_text().strip().replace(',', '')

Muito bem! Você acabou de raspar dados de um repositório do GitHub.

Etapa 7: raspe o arquivo readme

Outra informação essencial a ser recuperada é o arquivo README.md . Esse é um arquivo de texto opcional que descreve o repositório do GitHub e explica como usar o código.

Se você clicar no arquivo README.md e depois no botão “Raw”, você será redirecionado para a URL abaixo:

https://raw.githubusercontent.com/luminati-io/luminati-proxy/master/README.md
Extraindo contagens de estrelas, watchers e forks, e acessando o README.md no GitHub

Portanto, pode-se inferir que a URL do arquivo readme de um repositório do GitHub segue o formato abaixo:

https://raw.githubusercontent.com/<repo_id>/<repo_main_branch>/README.md

Como você tem a informação <repo_main_branch> armazenada na variável main_branch , você pode construir programaticamente esse URL com um f-string do Python:

readme_url = f'https://raw.githubusercontent.com/luminati-io/luminati-proxy/{main_branch}/README.md'

Em seguida, use o requests para recuperar o conteúdo bruto em Markdown do README:

readme_url = f'https://raw.githubusercontent.com/luminati-io/luminati-proxy/{main_branch}/README.md'
readme_page = requests.get(readme_url)

readme = None
# if there is a README.md file
if readme_page.status_code != 404:
    readme = readme_page.text

Observe a verificação de 404 para evitar o armazenamento do conteúdo da página 404 do GitHub quando o repositório não tiver um arquivo README.

Etapa 8: amazenar os dados raspados

Não se esqueça de adicionar as variáveis de dados raspadas ao dicionário repo :

repo['name'] = name
repo['latest_commit'] = latest_commit
repo['commits'] = commits
repo['main_branch'] = main_branch
repo['description'] = description
repo['stars'] = stars
repo['watchers'] = watchers
repo['forks'] = forks
repo['readme'] = readme

Use print(repo) para garantir que o processo de extração de dados funcione conforme desejado. Execute o raspador de dados em Python do GitHub e você obterá:

{'name': 'luminati-proxy', 'latest_commit': '2023-08-09T08:25:15Z', 'commits': '1079', 'main_branch': 'master', 'description': 'Luminati HTTP/HTTPS Proxy manager', 'stars': '645', 'watchers': '55', 'forks': '196', 'readme': '# Proxy manager\n\n (omitted for brevity...)'}

Perfeito! Você sabe como raspar dados do GitHub!

Passo 9: exportar os dados extraídos para JSON

A etapa final é tornar os dados coletados mais fáceis de compartilhar, ler e analisar. A melhor maneira de conseguir isso é exportar os dados em um formato legível por humanos, como JSON:

import json

# ...

with open('repo.json', 'w') as file:
    json.dump(repo, file, indent=4)

Importe json da biblioteca padrão do Python, inicialize um arquivo repo.json com open()e, finalmente, use json.ump() para preenchê-lo. Confira nosso guia para aprender mais sobre como analisar JSON em Python.

Perfeito! É hora de dar uma olhada em todo o raspador de dados em Python do GitHub.

Passo 10: juntar tudo

Essa é a aparência do arquivo scraper.py completo:

import requests
from bs4 import BeautifulSoup
import json

# the URL of the target repo to scrape
url = 'https://github.com/luminati-io/luminati-proxy'

# download the target page
page = requests.get(url)
# parse the HTML document returned by the server
soup = BeautifulSoup(page.text, 'html.parser')

# initialize the object that will contain
# the scraped data
repo = {}

# repo scraping logic
name_html_element = soup.select_one('[itemprop="name"]')
name = name_html_element.get_text().strip()

git_branch_icon_html_element = soup.select_one('.octicon-git-branch')
main_branch_html_element = git_branch_icon_html_element.find_next_sibling('span')
main_branch = main_branch_html_element.get_text().strip()

# scrape the repo history data
boxheader_html_element = soup.select_one('.Box .Box-header')

relative_time_html_element = boxheader_html_element.select_one('relative-time')
latest_commit = relative_time_html_element['datetime']

history_icon_html_element = boxheader_html_element.select_one('.octicon-history')
commits_span_html_element = history_icon_html_element.find_next_sibling('span')
commits_html_element = commits_span_html_element.select_one('strong')
commits = commits_html_element.get_text().strip().replace(',', '')

# scrape the repo details in the right box
bordergrid_html_element = soup.select_one('.BorderGrid')

about_html_element = bordergrid_html_element.select_one('h2')
description_html_element = about_html_element.find_next_sibling('p')
description = description_html_element.get_text().strip()

star_icon_html_element = bordergrid_html_element.select_one('.octicon-star')
stars_html_element = star_icon_html_element.find_next_sibling('strong')
stars = stars_html_element.get_text().strip().replace(',', '')

eye_icon_html_element = bordergrid_html_element.select_one('.octicon-eye')
watchers_html_element = eye_icon_html_element.find_next_sibling('strong')
watchers = watchers_html_element.get_text().strip().replace(',', '')

fork_icon_html_element = bordergrid_html_element.select_one('.octicon-repo-forked')
forks_html_element = fork_icon_html_element.find_next_sibling('strong')
forks = forks_html_element.get_text().strip().replace(',', '')

# build the URL for README.md and download it
readme_url = f'https://raw.githubusercontent.com/luminati-io/luminati-proxy/{main_branch}/README.md'
readme_page = requests.get(readme_url)

readme = None
# if there is a README.md file
if readme_page.status_code != 404:
    readme = readme_page.text

# store the scraped data 
repo['name'] = name
repo['latest_commit'] = latest_commit
repo['commits'] = commits
repo['main_branch'] = main_branch
repo['description'] = description
repo['stars'] = stars
repo['watchers'] = watchers
repo['forks'] = forks
repo['readme'] = readme

# export the scraped data to a repo.json output file
with open('repo.json', 'w') as file:
    json.dump(repo, file, indent=4)

Em menos de 100 linhas de código, você pode construir um rastreador da web para coletar dados de repositórios.

Execute o script com:

python scraper.py

Aguarde a conclusão do processo de raspagem de dados e você encontrará um arquivo repo.json na pasta raiz do seu projeto. Abra e você verá:

{
    "name": "luminati-proxy",
    "latest_commit": "2023-08-09T08:25:15Z",
    "commits": "1079",
    "main_branch": "master",
    "description": "Luminati HTTP/HTTPS Proxy manager",
    "stars": "645",
    "watchers": "55",
    "forks": "196",
    "readme": "# Proxy manager\n\n[![dependencies Status](https://david-dm.org/luminati-io/luminati-proxy/status.svg)](https://david-dm.org/luminati-io/luminati-proxy)\n[![devDependencies Status](https://david-dm.org/luminati-io/luminati-proxy/dev-status.svg)](https://david-dm..."
}

Parabéns! Você começou com dados brutos contidos em uma página da web e agora possui dados semi-estruturados em um arquivo JSON. Você acaba de aprender a construir um raspador de dados de repositórios do GitHub em Python!

Conclusão

Neste guia passo a passo, você entendeu os motivos pelos quais deveria criar um raspador de dados de repositórios do GitHub. Especificamente, você aprendeu como raspar dados do GitHub em um tutorial guiado. Como mostrado aqui, isso leva apenas algumas linhas de código.

Ao mesmo tempo, mais e mais sites estão adotando tecnologias anti-raspagem. Eles podem identificar e bloquear solicitações por meio de proibição de IP e limitação de taxa, impedindo que seu scraper acesse o site. A melhor maneira de evitá-los é usar um proxy. Explore a vasta oferta da Bright Data de serviços de proxy de alto nível e os proxies dedicados do GitHub.

A Bright Data controla os melhores proxies para raspagem de dados na web, atendendo empresas da Fortune 500 e mais de 20.000 clientes. Sua rede de proxies mundial envolve:

No geral, a Bright Data é uma das maiores e mais confiáveis redes de proxies voltadas para raspagem de dados no mercado. Converse com um de nossos representantes de vendas e veja qual dos produtos da Bright Data melhor atende às suas necessidades.

Não é necessário cartão de crédito

Observação: este guia foi rigorosamente testado pela nossa equipe no momento da escrita, mas como os sites frequentemente atualizam seu código e estrutura, alguns passos podem não funcionar como esperado.