PythonでGitHubリポジトリをスクレイピングする方法

GitHubをどうスクレイピングするのか疑問に思ったことはありませんか?ここで答えが見つかります!
6 min read
Github scraping guide

このチュートリアルで学べること:

GitHubリポジトリをスクレイピングするべき理由

GitHubリポジトリをスクレイピングするべき理由はいくつかあります。最も大きな理由は:

  • テクノロジートレンドのフォロー:リポジトリのStarやリリースを追跡することで、プログラミング言語、フレームワーク、ライブラリの現在の傾向を追跡できます。GitHubをスクレイピングすることで、人気上昇中のテクノロジーを分析し、その成長を監視して、新たなトレンドを特定できます。このデータは、テクノロジーの採用、スキルの開発、およびリソースの割り当てに関する意思決定の指針となります。
  • 豊富なプログラミング知識ベースにアクセスできる:GitHubは、オープンソースプロジェクト、コードサンプル、ソリューションの宝庫です。つまり、プラットフォームから膨大な量のプログラミング知識とベストプラクティスを収集できるということです。これは、教育上の目的、コーディングスキルの向上、さまざまなテクノロジーの実装方法の理解に役立ちます。
  • 共同開発に関する分析情報の取得:リポジトリは、開発者がpull requests、Issues、Discussionsを通じて共同作業を行う方法についての分析情報を提供します。このデータを収集することで、チームワーク戦略の考案、プロジェクト管理の改善、ソフトウェア開発プロセスの完成に役立つ共同作業パターンを研究できます。

GitHubはGitリポジトリをホストする唯一のクラウドベースのプラットフォームではありません。他にも多くの選択肢があります。ただし、次の理由から、データスクレイピングには依然として最適な選択肢です。

  1. 大規模なユーザーベース
  2. 活発なユーザーアクティビティ
  3. 確立された評判

GitHubのデータは特に、テクノロジートレンドの監視、ライブラリとフレームワークの検出、ソフトウェア開発プロセスの改善に役立ちます。この情報は、IT業界で競合他社を出し抜くために重要な役割を果たします。

GitHubスクレイピングライブラリとツール

Pythonは、わかりやすい構文、開発者にとっての使いやすさ、豊富なライブラリにより、Webスクレイピングに最適な言語として広く認識されています。これが、PythonがGitHubのスクレイピングに推奨されるプログラミング言語である理由です。詳細については、「PythonでWebスクレイピングを行う方法」に関する詳細ガイドをご覧ください。

次のステップでは、利用可能な幅広い選択肢の中から、最適なスクレイピングライブラリを選択します。十分な情報に基づいて決定するには、まずお使いのブラウザでプラットフォームを確認する必要があります。開発ツールを開き、GitHubのリポジトリページから行われたAJAX呼び出しを覗いてみます。それらの大部分は無視してよいことに気づくでしょう。実際、ページデータのほとんどは、サーバーから返されたHTMLドキュメントに埋め込まれています。

つまり、このタスクには、サーバーにHTTPリクエストを行うライブラリとHTMLパーサーの組み合わせで十分ということです。したがって、以下を選択する必要があります。

  • Requests:Pythonエコシステムで最も人気のあるHTTPクライアントライブラリです。HTTPリクエストを送信し、それに対応するレスポンスを処理するプロセスを効率化します。
  • Beautiful Soup:包括的なHTMLおよびXML解析ライブラリです。Webスクレイピング用の強力なDOMナビゲーションとデータ抽出APIを提供します。

RequestsとBeautiful Soupの機能により、Pythonを使用したGitHubスクレイピングを効果的に実行できます。それでは早速、実際のやり方を詳しく見ていきましょう。

Beautiful Soupを使用したGitHubリポジトリスクレイパーの構築

このステップバイステップのチュートリアルに従って、PythonでGitHubをスクレイピングする方法を学びましょう。コーディングとスクレイピングのプロセス全体をスキップしたいですか?その場合はGithubデータセットをご購入ください。

ステップ1:Pythonプロジェクトをセットアップする

始める前に、次の前提条件を満たしていることを確認してください。

  • Python 3+がコンピュータにインストールされているインストーラをダウンロードして実行し、指示に従ってください。
  • お好きなPython IDE:Visual Studio CodeにPython拡張機能を入れたものか、PyCharm Community EditionがおすすめのIDEです。

これで、Pythonでのプロジェクトの設定に必要なすべてのものが揃いました!

ターミナルで次のコマンドを起動してgithub-scraperフォルダーを作成し、それをPython仮想環境で初期化します。


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

Windowsでは、次のコマンドを実行して環境を有効にしてください。

env\Scripts\activate.ps1

LinuxまたはmacOSを使用している場合は、以下を実行してください。

./env/bin/activate

次に、以下の行を含むscraper.pyファイルをプロジェクトフォルダーに追加します。

print('Hello, World!')

現在、GitHubスクレイパーには「Hello, World!」としか出力されません。しかし、まもなく公開リポジトリからデータを抽出するロジックが含まれるようになります。

次の方法でスクリプトを起動できます。

python scraper.py

すべてが計画通りに進めば、ターミナルに次のメッセージが表示されるはずです。

Hello, World!

動作することを確認したので、お好みのPython IDEでプロジェクトフォルダを開きます。

できました!Pythonコードを書く準備をしましょう。

ステップ2:スクレイピングライブラリをインストールする

以前述べたように、Beautiful SoupRequestsはGitHubでWebスクレイピングを実行するのに役立ちます。アクティブ化された仮想環境で、次のコマンドを実行してプロジェクトの依存関係に追加します。

pip install beautifulsoup4 requests

scraper.pyをクリアしてから、次の行を使用して2つのパッケージをインポートしてください。

import requestsfrom bs4 import BeautifulSoup
# scraping logic...

お使いのPython IDEがエラーを報告しないことを確認してください。未使用のインポートが原因で、警告が表示される場合があります。GitHubからリポジトリデータを抽出するために、これらのスクレイピングライブラリを使用しようとしているわけですから、その警告は無視してください。

ステップ3:ターゲットページをダウンロードする

データを取得したいGitHubリポジトリを選択してください。このガイドでは、luminati-proxyリポジトリをスクレイピングする方法を説明します。スクレイピングのロジックは同じなので、他のリポジトリでも同様に使えることを覚えておいてください。

ターゲットページはブラウザで次のように表示されます。

GitHubリポジトリセレクションガイド

ターゲットページのURLを変数に保存します。

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

次に、requests.get()を使用してページをダウンロードします。

page = requests.get(url)

バックグラウンドでは、requestsがそのURLにHTTP GETリクエストを行い、サーバーから返された応答をページ変数に格納します。注目すべきは、そのtext属性です。これには、対象のWebページに関連付けられたHTMLドキュメントが含まれます。簡単なprint指示で確認できます。

print(page.text)

スクレイパーを実行すると、ターミナルに以下が表示されます。


<!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

ステップ4:HTMLドキュメントを解析する

上記で取得したHTMLドキュメントを解析するには、それをBeautiful Soupに渡します。

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

BeautifulSoup()コンストラクタは2つの引数を受け取ります。

  1. HTMLコンテンツを含む文字列:ここではpage.text変数に格納されています。
  2. Beautiful Soupが使用するパーサー:”html.parser“はPythonに組み込みのHTMLパーサーの名前です。

BeautifulSoup()はHTMLを解析し、調査可能なツリー構造を返します。詳しく説明すると、soup変数は、DOMツリーから要素を選択するための効果的な方法を提供します。たとえば、

  • find():パラメータとして渡されるセレクタ戦略に一致する最初のHTML要素を返します。
  • find_all():入力セレクタ戦略に一致するHTML要素のリストを返します。
  • select_one():パラメータとして渡されるCSSセレクタに一致する最初のHTML要素を返します。
  • select():入力CSSセレクタと一致するHTML要素のリストを返します。

これらのメソッドは、ツリー内の単一ノードに対しても呼び出せることに注意してください。これらに加えて、Beautiful Soupノードオブジェクトは以下も公開しています。

  • find_next_sibling():指定されたCSSセレクタに一致する要素の兄弟要素の中で、最初のHTMLノードを返します。
  • find_next_siblings():パラメータとして渡されたCSSセレクタに一致する要素の兄弟要素の中で、、すべてのHTMLノードを返します。

これらの機能のおかげで、GitHubをスクレイピングする準備が整いました。その方法を見てみましょう!

ステップ5:対象となるページの理解を深める

コーディングに取り掛かる前に、完了すべき重要なステップがもう1つあります。サイトからデータをスクレイピングするというのは、関心のあるHTML要素を選択し、そこからデータを抽出することです。効果的な選択戦略を定義するのは必ずしも簡単なことではなく、対象となるWebページの構造を分析するにはある程度の時間を費やす必要があります。 

したがって、対象となるGitHubページをブラウザで開き、理解を深めてください。右クリックして[検証]を選択し、開発ツールを開きます:

開発ツールを使用してGitHubページを検証中

HTMLコードを掘り下げてみると、このサイトの要素の多くに固有のクラスや属性が付与されていないことがわかります。そのため、通常は目的の要素に移動するのが難しく、トリッキーな方法で兄弟要素をたどる必要があるかもしれません。

とはいえ、ご心配なく。GitHubの効果的なセレクタ戦略を考案するのは簡単ではありませんが、不可能なわけではありません。スクレイピングの準備ができるまで、開発ツールでページの検証を続けてください。

ステップ6:リポジトリデータを抽出する

目標は、Star、説明文、最後のコミットなど、GitHubリポジトリから有用なデータを抽出することです。そのため、このデータを追跡するにはPythonディクショナリを初期化する必要があります。コードに追加する:

repo = {}

まず、名前要素を検証してください。

リポジトリデータ抽出用のPythonディクショナリを初期化中

これにはitemprop="name"という独特の属性があることに注意してください。選択して、次のコードを使用してテキストコンテンツを抽出してください。

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

Beautiful Soupノードが付与されたら、get_text()メソッドを使用してそのテキストコンテンツにアクセスしてください。

デバッガでname_html_element.textを検証すると、次のように表示されます。

\nluminati-proxy\n

GitHubテキストフィールドにはスペースと改行が含まれる傾向があります。strip()Python関数でそれらを取り除いてください。

リポジトリ名のすぐ下に、ブランチセレクタがあります。

Beautiful Soupを使用してリポジトリ名を抽出中

メインブランチの名前を格納しているHTML要素を選択する簡単な方法はないことに注意してください。できることは、.octicon-git-branchノードを選択し、その兄弟要素でターゲットのspanを探すことです。

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()

アイコンの兄弟要素を通じて目的の要素に到達するパターンは、GitHubでは非常に効果的です。このセクションで何度か使用されるのを目にするでしょう。

さて、次はブランチヘッダーに注目してください。

GitHubで兄弟要素を通じてメインブランチ名を選択中

以下を使用して最後のコミット時間を抽出します:

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

ノードが付与されたら、Pythonディクショナリでできるのと同様に、そのHTML属性にアクセスできます。

このセクションのもう1つの重要な情報はコミット数です。

以前説明したアイコンパターンで収集してください。

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(',', '')

find_next_sibling()はトップレベルの兄弟要素にのみアクセスできることに注意してください。そのうち1つの子要素を選択するには、上記と同様に、まず兄弟要素を取得してからselect_one()を呼び出す必要があります。

GitHubでは1,000を超える数値にはカンマが含まれているため、replace()Pythonメソッドを使用してカンマを削除してください。

次に、右側の情報ボックスに注目してください。

次を使用して選択してください。

bordergrid_html_element = soup.select_one('.BorderGrid')

説明文要素を検証してください。

GitHub上の説明文要素の選択と検証を実行中

繰り返しますが、以下のように兄弟要素を通じて選択できます。

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、ウォッチ、フォークを取得します。

アイコンに注目してから、次にそのテキスト兄弟要素に注目してください。

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(',', '')

お疲れ様でした!GitHubリポジトリをスクレイピングできました。

ステップ7:READMEをスクレイピングする

取得すべきもう1つの重要な情報は、README.mdファイルです。これはGitHubリポジトリを説明し、コードの使用方法を解説するオプションのテキストファイルです。

README.mdファイルをクリックしてから[Raw]ボタンをクリックすると、以下のURLにリダイレクトされます。

https://raw.githubusercontent.com/luminati-io/luminati-proxy/master/README.md
Star、ウォッチャー、フォークの数の抽出と、GitHubのREADME.mdへのアクセスを実行中

したがって、GitHubリポジトリのREADMEファイルのURLは次の形式に従っていると推測できます。

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

main_branch変数に情報が格納されているので、Pythonのf-stringを使用して、このURLをプログラムで作成することができます。

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

次に、requestsを使用してREADMEの未処理のMarkdownコンテンツを取得します。

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

リポジトリにREADMEファイルがない場合にGitHubの404ページのコンテンツを保存しないよう、404チェックに注意してください。

ステップ8:スクレイピングしたデータを保存する

スクレイピングされたデータ変数をリポジトリディクショナリに追加することをお忘れなく。

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

print(repo)を使用して、データ抽出プロセスが想定通りに動作することを確認してください。Python GitHubスクレイパーを実行すると、次のようになります。

{'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...)'}

できました!GitHubのスクレイピング方法を習得しました!

ステップ9:スクレイピングしたデータをJSONにエクスポートする

最後のステップは、収集したデータの共有、読み取り、分析をしやすくすることです。これを実現する最善の方法は、JSONなどの人間が読める形式でデータをエクスポートすることです。

import json

# ...

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

Python標準ライブラリからjsonをインポートし、repo.jsonファイルをopen()で初期化し、最後にjson.ump()を使用してデータを入力します。PythonでJSONを解析する方法の詳細については、当社のガイドをご覧ください。

素晴らしい!ここで、GitHub Pythonスクレイパー全体をチェックしてみましょう。

ステップ10:仕上げ

完成したscraper.pyファイルは次のようになります。

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)

100行未満のコードで、リポジトリデータを収集するWebスパイダーを構築できます。

以下を使用してスクリプトを実行します。

python scraper.py

スクレイピングプロセスが完了するのを待つと、プロジェクトのルートフォルダにrepo.jsonファイルが見つかります。開くと、次の内容が表示されます。

{
    "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..."
}

おめでとうございます!Webページに含まれる未処理データから、最終的におおよその構造が付いたJSONデータを作ることができました。これで、Pythonを使ってGitHubリポジトリスクレイパーを構築する方法を習得しました。

まとめ

この手順ガイドでは、GitHubリポジトリスクレイパーを構築するべき理由を理解できました。具体的には、ガイド付きチュートリアルでGitHubのスクレイピング方法を学びました。ご覧いただいた通り、必要なのは数行のコードだけです。

しかし同時に、スクレイピング防止技術を採用するサイトが増えています。これらは、IP禁止や速度制限によりリクエストを識別してブロックし、スクレイパーによるサイトへのアクセスを阻止します。こうした技術を回避する最善の方法はプロキシです。Bright Dataが提供する一流のプロキシサービスGitHub専用プロキシをご確認ください。

Bright Dataは業界屈指のWebスクレイピング用プロキシを管理しており、フォーチュン500企業を含む20,000社以上のお客様にサービスを提供しています。世界中に広がるBright Dataのプロキシネットワークには以下が含まれます。

総合的に見ても、Bright Dataはプロキシ業界で最大かつ最も信頼性の高いスクレイピング向けプロキシネットワークの1つと言えます。当社の営業担当者にご相談の上、お客様のニーズに合ったBright Data製品をお選びください。

クレジットカードは必要ありません

注:このガイドは、執筆時点で当社チームによって徹底的にテストされていますが、Webサイトのコードや構造は頻繁に更新されるため、一部の手順が想定通りに機能しなくなる可能性があります。