Githubのプルリク本文に貼られた画像を一括ダウンロードする

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334798/8f0b58bc-6d77-59e3-8865-3040819be80e.png" width="320px">

スクリーンショットは[DroidKaigi/conference-app-2020](https://github.com/DroidKaigi/conference-app-2020)のものをお借りしています。

# なぜするか

自社サービスのアプリ開発をしています。アプリアップデータリリース時の速報として、新しく作られたり変更があった画面のスクリーンショット一覧を、企画、運用部門と共有することを一部自動化したいです。また、あとで文章にまとめるときにも、すでに収集されたスクリーンショット集があるので効率的に作れます。
私が所属する開発チームではプルリクのレビュー依頼時にプルリクの本文にスクリーンショットやGifアニメーションを貼る習慣あるので、その画像を企画、運用部門と共有します。

# Pythonを使って一括ダウンロードする

事前準備

```sh:sh
mkdir Downloads
pip install PyGithub BeautifulSoup4 requests
```

実行Pythonソースコード

```python:main.py
import os
from datetime import datetime, timedelta
# マークダウンからHTMLに変換
import markdown
# Github API クライアント
from github import Github
# HTMLパーサー
from bs4 import BeautifulSoup
# HTTPクライアント
import requests
# MIMEタイプから拡張子を取得
import mimetypes

# 保存先ディレクトリ
save_dir = "Downloads"
# マークダウンをHTMLに変換する担当オブジェクト作成
md = markdown.Markdown()
# アクセストークンはこちらで作る
# https://github.com/settings/tokens
access_token = os.environ['GITHUB_API_ACCESS_TOKEN']
# アクセストークンを持ってGithubオブジェクトを作成
g = Github(access_token)
# リポジトリを取得
gr = g.get_organization('DroidKaigi').get_repo('conference-app-2020')
# 個人のリポジトリの場合は
# gr = g.get_user().get_repo('repository_name')
# ダウンロードする範囲(プルリク作成日基準)
start_datetime = datetime(2020, 1, 1)
end_datetime = datetime(2020, 1, 13)
# 閉じられたプルリクエスト一覧を取得
prs = gr.get_pulls(state='close', base='master')
for pr in prs:
    # 順番は作成日降順
    if pr.created_at < start_datetime:
        # これ以上は遡らなくて良い
        break
    if pr.created_at < end_datetime:
        # ダウンロードする範囲に入った
        # マージされたPRに限定する
        if pr.merged != True:
            continue
        print("#%d %s" % (pr.number, pr.title))
        # プルリク本文のマークダウンをHTMLに変換する
        html = md.convert(pr.body)
        # HTMLをパースする
        soup = BeautifulSoup(html, features='html.parser')
        # すべてのimgタグに対して
        for img_index, img in enumerate(soup.find_all('img')):
            # src属性の値を画像urlとして取得して
            url = img.get('src')
            # src属性が空のケース対応
            if len(url) == 0:
                continue
            # URLをダウンロードする
            r = requests.get(url)
            if r.ok:
                # ダウンロード成功
                # ファイルのmimeタイプを取得する
                content_type = r.headers['Content-Type']
                # mimeタイプから拡張子を取得する
                ext = mimetypes.guess_extension(content_type)
                # ファイル名にプルリクタイトルを含めたいが
                # スラッシュがファイル名に含まれているとファイルを作れないので
                # 全角に変換する
                title = pr.title.replace('/', '／')
                # 保存先パスを作成
                path = "%s/%04d_%s_%02d.%s" % (save_dir,
                                               pr.number, title, img_index, ext)
                # 画像を保存する
                with open(path, 'wb') as f:
                    f.write(r.content)
            else:
                # ダウンロード失敗
                message = "Image download error\n%s\n%d %s" % (
                    url, r.status_code, r.reason)
                raise RuntimeError(message)

```

## 状況に応じてカスタマイズ

上記ソースコードはとりあえずローカルPCで動かしてローカルフォルダに一括ダウンロードしています。組織やプロジェクトの状況に応じて例えばこのようなカスタマイズを行うと良いと思います。

- 継続的デリバリーのワークフローで実行する
- SlackBotにする
- 画像はGoogle Driveにアップロードする

## バージョンアップの差分だけ画像をダウンロードする

[自作アプリ](https://github.com/tfandkusu/quickecho)のリリースワークフローでは前バージョンリリース以後にマージされたPR一覧を取得して[リリースノート](https://github.com/tfandkusu/quickecho/releases)を自動で作るPythonスクリプトを動かしています。そのPR一覧から本文を取得して上記と同じような処理を行えばバージョンアップの差分だけ画像をダウンロードできます。

[該当ソースコードへのリンク](https://github.com/tfandkusu/quickecho/blob/159666474897689f4ce7da231a58ab630d27ea04/release_note.py#L11)

# 注意点

他部門と共有するスクリーンショットにAWSアクセスキーにような秘密の情報が含まれていないかご注意ください。