GitHubのファイルをapiでダウンロードする方法

03_IT
GitHubのファイルをapiでダウンロードする方法。

公式のAPIリファレンスの通りにやると、
api呼出を2回実行する必要があるのですが、
api呼出を1回実行するだけでファイルをダウンロードできる、
別のやり方もあったので忘れないようにメモします。

公式リファレンスの方法(apiを2回実行)

概要


GitHubのファイル(リポジトリコンテンツ)の操作に関する
公式のAPIリファレンスは下記の通り。
ファイルをダウンロードするためには
リポジトリのコンテンツ取得でファイルの情報を取得できるので、
その中にあるダウンロード先のURLを利用してファイルをダウンロードします。


publicリポジトリだと特に認証トークンが必要ないのですが、
privateリポジトリだと認証トークンが必要となるためその点は注意が必要。

サンプルソース

実際にpythonで取得できるようにしたソースが下記の通り。
処理の流れとしては下記の通りです。
処理順序
  1. リポジトリのcontents取得APIを実行し対象ファイルの情報を取得
  2. ファイルの情報(json形式)の中にdownload_urlというURLが格納されているので、そこからファイルのデータを取得のAPIを実行し、ファイルのバイナリデータを取得
  3. 取得したファイルのバイナリデータをファイルに出力
from pathlib import Path

import requests
from requests.adapters import HTTPAdapter
from urllib3 import Retry


def api_get(header: dict, url: str, parameter: dict):
    session = requests.Session()

    retries = Retry(total=5,  # リトライ回数
                    backoff_factor=1,  # sleep時間
                    status_forcelist=[500, 502, 503, 504])  # timeout以外でリトライするステータスコード

    session.mount("https://", HTTPAdapter(max_retries=retries))

    print(url)
    # connect timeoutを10秒, read timeoutを30秒に設定
    response = session.get(url=url,
                           headers=header,
                           params=parameter,
                           stream=True,
                           timeout=(10.0, 30.0))
    
    assert response.status_code < 300

    return response


def main():
    # ファイル
    token = "githubのsettingで作った認証トークン"
    # privateリポジトリの場合はヘッダの設定は不要
    header = {"Accept": "application/vnd.github.v3+json", "Authorization": f"token {token}"}
    owner = "uji"
    repository = "work"
    brunch = "main"
    file = Path("uji.zip") # カレントディレクトリに同名のファイルをダウンロード
    url = f"https://api.github.com/repos/{owner}/{repository}/contents/{file.name}"
    # ブランチが必要な場合設定。省略するとデフォルトリポジトリの情報を取得
    parameter = {"ref": "develop"}

    response = api_get(header, url, parameter)
    download_url = response.json()["download_url"]

    response = api_get(header, download_url, {})

    with file.open('wb') as saveFile:
        saveFile.write(response.content)


if __name__ == '__main__':
    main()


公式リファレンス通りなので、
普通はこちらを使用するのがせいだと思いますが、
2回API実行するのが少々めんどくさい・・・。

別の方法(apiを1回実行)

概要

1回のAPIでファイルをダウンロードするためには、
https://raw.githubusercontent.com
という連携先を使用するとファイルをそのまま持ってくることができます。
このドメインが何か調べてみるとstackoverflowに下記のように記述がありました。
raw.githubusercontent.com ドメインは、GitHub リポジトリに保存されているファイルの未処理バージョンを提供するために使用されます。
GitHub のファイルを参照して Raw リンクをクリックすると、そこに移動します。

ソース

get_apiは上記のソースで記述したので、省略し
main処理だけ記述。

publicリポジトリだとソースファイルを認証設定なしで
URLだけたたいてもファイル取得できます。
def main():
    # ファイル
    token = "githubのsettingで作った認証トークン"
    # privateリポジトリの場合はヘッダの設定は不要
    header = {"Accept": "application/vnd.github.v3+json", "Authorization": f"token {token}"}
    owner = "uji"
    repository = "work"
    brunch = "main"
    file = Path("uji.zip")
    url = f"https://raw.githubusercontent.com/{owner}/{repository}/{brunch}/{file.name}"
    parameter = {"ref": "develop"}

    response = api_get(header, url, parameter)

    with file.open('wb') as saveFile:
        saveFile.write(response.content)

こっちの方がURLだけでリポジトリ、ブランチ、ファイルが完結しており
わかりやすいまである・・・。

私がこのやり方を知るきっかけになったソースは
ソースのバージョン確認で使用されていました。
shellからのcurl呼び出しとかで中身解析が面倒な場合、こっちのやり方が使えそう・・・。

公式リファレンスで説明しているやり方を利用するのが、
将来的にも使えなくなるといったことも少ないため、通常はそちらが推奨です。

ただ、pythonなどの高級言語のアプリで扱うときはそこまで気にしないけど、
shellでローカルにjq(json解析ツール)をインストールできない場合のような
限定された場面では、
1回APIを実行して、そのレスポンスを解析するのが面倒なため、
こちらの方法も使えるかもしれません。

コメント

タイトルとURLをコピーしました