Python 2026.02.20

画面を自動でキャプチャして
PDFにまとめるツールを
Pythonで作った話

はじめに

複数ページにわたる資料を手作業でスクリーンショットしてまとめる作業が、面倒だと感じていた。1ページずつキャプチャして、ファイルを整理して、PDFに変換して——単純な繰り返しなのに時間がかかる。

「これは自動化できるはずだ」と思い、Pythonで作ることにした。技術的な学習も兼ねて、pyautogui・OpenCV・Pillow を組み合わせたツールを開発した。

最終的には、キーを1回押すだけで自動的にページを送りながらキャプチャし続け、最終ページを検出したら自動でPDFを生成して終了する、という仕組みが完成した。


ツールの概要

コマンドラインからページ送りの方向(左 or 右)を指定して起動する。3秒以内に対象ウィンドウをクリックすると、あとは自動でキャプチャが進み、最終ページに達したらPDFが生成される。

① コマンドで起動(left / right を指定)
     ↓
② 3秒以内に対象ウィンドウをクリック
     ↓
③ スクリーンショットを撮影・保存
     ↓
④ 前ページとの差分を比較
     ↓
⑤ 変化なしが一定回数続いたら最終ページと判断
     ↓
⑥ 保存した画像をまとめてPDF生成 → 完了
# 使い方(右向きページ送りの場合)
python auto_capture.py right

# 左向きページ送りの場合
python auto_capture.py left

一番の工夫:ページ終端の自動検出

このツールで一番考えたのが、「最終ページをどう判断するか」だった。単純にページ数を指定する方法もあるが、それでは毎回設定が必要になる。

採用したのは、前後ページの画像差分を比較する方法だ。最終ページに達するとページ送りをしても画面が変わらなくなる。この「変化なし」が一定回数続いたら終端と判断して処理を止める。

def diff_ratio(a, b):
    diff = cv2.absdiff(a, b)
    return np.sum(diff) / (diff.size * 255)

# 差分が閾値未満なら「変化なし」としてカウント
if diff < CHANGE_THRESHOLD:
    no_change_count += 1
if no_change_count >= NO_CHANGE_LIMIT:
    print("最終ページと判断 → 終了")
    break
ノイズ対策:画面には微妙な描画差異(アンチエイリアスなど)があり、全く同じ画面でも差分がゼロにならないことがある。そのためガウスブラーとリサイズを前処理として入れ、細かいノイズを平均化してから比較している。
gray = cv2.GaussianBlur(gray, (7, 7), 0)
gray = cv2.resize(gray, (800, 450))

ハマったポイント:prev_gray = gray.copy()

実装の途中、「毎回同じページと判定されて止まらない」というバグが発生した。

原因は、Pythonの参照渡しの挙動だった。prev_gray = gray と書くと、変数が同じオブジェクトを指してしまう。その後 gray が更新されると prev_gray も同時に書き変わってしまうため、常に「前回と今回が同じ」という判定になっていた。

# NG:参照渡しになってしまう
prev_gray = gray

# OK:値をコピーして独立させる
prev_gray = gray.copy()  # ← これが超重要

.copy() を付けることで独立したコピーが作られ、前のページの情報が正しく保持されるようになった。NumPy配列を扱う際に覚えておくべき基本的な挙動だが、実際にバグとして踏んで初めて腹落ちした。


使用技術

技術 用途
Pythonメイン言語
pyautoguiスクリーンショット・キー操作の自動化
OpenCV (cv2)画像の差分比較・前処理(ブラー・リサイズ)
Pillow (PIL)画像のPDF変換・保存
NumPy画像データの数値演算

おわりに

手作業で繰り返していた単純な作業が、コードを書くことで完全に自動化できた。実際に動かして使えるものが出来上がる体験は、学習のモチベーションになる。

prev_gray.copy() のような「実際にバグを踏んで学んだ知識」は、ドキュメントを読むだけでは得られないものだと感じている。作って、動かして、壊して、直す——この繰り返しが一番の近道だと思っている。

← ブログ一覧へ戻る