【トラブルシューティング】ブラウザ自動操作でファイルを集めるときの落とし穴集(確定申告自動化 第5回)

この記事でわかること
- ブラウザが連続ダウンロードを無言でブロックする条件と、その見分け方
- 「最新のファイルを拾う」実装が無関係なファイルを壊した事故と、安全な書き方
- Shadow DOM のサイトでクリックが効かないときの回避策
- 取得手段を「直URL / DLボタン / HTMLのみ」で機械的に選ぶ判断基準
ブラウザ自動操作は Claude Code の Chrome 連携を使用(他の自動化ツールでも考え方は共通)/ 自分のアカウントの領収書を自分で取得する用途 / macOS 環境(パス表記は適宜読み替え)
概要: 領収書収集のうち、メールや API で取れないものはブラウザ自動操作で拾うことになります。ここには「動いているように見えて実は失敗している」パターンがいくつもあります。連続ダウンロードの無言ブロック、ファイル検出の事故、Shadow DOM でクリックが効かないサイト — 実際に踏んだものを並べます。
はじめに
第 4 回で「可能なら HTTP クライアントで直接取得する」と書きました。その理由がこの記事です。
ブラウザ自動操作は強力ですが、失敗の仕方が静かです。例外も出ない、エラーログも出ない、スクリーンショット上は成功しているように見える。でもファイルがない。
この手の失敗は、自動化しているときに一番怖いです。間違ったデータが黙って下流に流れていくからです。
本記事では、実運用で踏んだ落とし穴と、その対策を並べます。
1. 連続ダウンロードは無言でブロックされる
一番最初にぶつかるのがこれです。
ブラウザは、JavaScript 起点のダウンロードをサイト単位で制限します。1 件目は普通に落ちるのに、2 件目以降が黙って失敗します。エラーは出ません。
自動化スクリプトから見ると、クリックは成功し、ページも遷移し、何も問題がないように見えます。でもファイルがない。
対策は 2 つあります。
サイトごとに自動ダウンロードを許可してもらう。 ブラウザの設定に「自動ダウンロード」のサイト別許可があります。これはユーザー自身にやってもらう必要があります。
そもそもブラウザの DL 機能を使わない。 これが本命です。PDF の直 URL が得られるなら、HTTP クライアントで取った方が確実です。第 4 回の手法が価値を持つのはここです。
いずれにしても、ダウンロード後は必ずファイルの存在を確認する。クリックしたことを成功と見なさないことです。
2. 「最新のファイルを拾う」実装が事故を起こした
これは実際にやらかした失敗です。
ダウンロードしたファイルをリネームして保管庫に移すために、こういうコードを書きました。
# 危険: ダウンロードフォルダの最新ファイルを掴む
latest=$(ls -t ~/Downloads | head -1)
mv "$HOME/Downloads/$latest" "receipts/2026/07/….pdf"一見問題なさそうです。しかしダウンロードが失敗していた場合、ls -t の先頭は「前回のファイル」や「ユーザーが自分で落とした無関係のファイル」になります。
それを mv するとどうなるか。ユーザーの無関係なファイルが、領収書の名前にリネームされて別の場所に消えます。実際にこの事故を起こしました。
対策は 2 つ。
時刻で絞る。 直近に作成されたファイルだけを対象にします。直近 1 分以内などとしておけば、古いファイルを掴むことはありません。
# 安全: 直近作成のものだけを対象にする
find ~/Downloads -newermt "1 minute ago" -name "*.pdf"前後の差分を取る。 ダウンロード前にファイル一覧をスナップショットし、後との差分を取る。確実ですが一手間増えます。
ここから得られる教訓は一般化できます。失敗時に「別の何か」を掴む実装は書かない。失敗したら何も掴まないのが正しい振る舞いです。
3. Shadow DOM でクリックが効かない
一部のエンタープライズ向けサイト(ポータル系に多い)は、Shadow DOM で UI を構築しています。こういうサイトでは通常の要素検索や JavaScript 経由のクリックが効きません。
確立した回避策はこの組み合わせでした。
- 自然言語の要素検索で参照を取る(「項目を選択 N」のようなラベルで探す)
- その参照をクリックして行を選択する
- 参照を使ってスクロールし、画面位置を安定させる
- 固定座標をクリックして次画面へ遷移させる
ポイントは 2 つあります。
参照クリックだけでは遷移しないことがある。選択はできても、次のアクションは実際のマウスイベントを要求する場合があります。だから座標クリックにフォールバックします。
座標クリックの前に必ずスクロールを固定する。スクロール位置が違うと座標がすべてずれます。参照を使って目的の要素を画面内に入れてから、座標を叩きます。
この手のサイトでは、一度確立したクリック座標を手順書に書き残すのが現実的です。毎回探索するのは時間の無駄です。
4. ブラウザから PDF が取れないとき
サービスによっては、領収書が HTML ページとしてしか存在しないことがあります。「印刷」を想定していて、PDF のダウンロードボタンがないパターンです。
このときは HTML を保存して、ローカルのヘッドレスブラウザで PDF 化します。
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--headless --disable-gpu --no-pdf-header-footer \
--print-to-pdf=out.pdf \
file:///path/to/saved.html--no-pdf-header-footer を入れるのがコツです。これがないと、印刷日時や URL がヘッダーに入ってしまい、証憑として見苦しいものになります。
また、ページが JavaScript で描画されている場合は、HTTP クライアントで取っても中身が空です。ブラウザで開いてから DOM のテキストを抜く必要があります。この判別を間違えると、空のファイルを量産します。
5. 自動化ツール固有の制約もある
使っている自動化ツール側の制約で、実際に困ったものを 2 つ挙げます。
ページ内 JavaScript の戻り値にクエリ付き URL を含めると、保護機能で読めなくなる。セッション情報の漏洩を防ぐ仕組みですが、URL をログに出してデバッグしようとするとぶつかります。対策は単純で、成否を表す真偽値や件数だけを返すこと。
スクリーンショットの保存先パスが取得できないことがある。これを証憑の保存手段に使うのは避けます。スクリーンショットは「人が目で確認するため」のものと割り切るのが安全です。
6. いつブラウザを使わないと判断するか
ここまでの落とし穴を見ると、判断基準はしぜんと見えてきます。
| 状況 | 選ぶべき手段 |
|---|---|
| PDF の直 URL が得られる | HTTP クライアントで直接取得(最優先) |
| ログインが必要だが URL は安定 | ブラウザでリンクだけ取得 → 取得は HTTP クライアント |
| DL ボタンしかない | ブラウザで DL(サイト許可と存在確認が必須) |
| HTML しかない | HTML 保存 → ローカルで PDF 化 |
| 再認証が頻繁に入る | 自動化を諦めて人に任せる |
最後の行が重要です。すべてを自動化しないという判断も設計の一部です。
パスワードの再認証を求めてくるサービスは、自動化しようとすると毎回詰まります。ここは「ユーザーに入力してもらう」と手順に明記して、潔く人に渡す方が速いです。とくに認証情報の入力は自動化側で背負わないのが原則です。
Tips
- クリックしたことを成功と見なさない。ダウンロード後は必ずファイルの存在と型を確認する
- 失敗時に「別の何か」を掴む実装を書かない。時刻で絞るか、前後の差分を取る
- 座標クリックの前に必ずスクロール位置を固定する。参照で要素を画面内に入れてから叩く
- 確立した操作手順は座標ごと手順書に残す。Shadow DOM サイトの探索を毎回やり直さない
- JS 描画のページは HTTP クライアントでは空になる。ブラウザで開いて DOM から抜く
- 再認証が入るところは自動化しない。認証情報の入力は人に渡す
まとめ
- ブラウザは JS 起点の連続ダウンロードをサイト単位で無言ブロックする。1 件目だけ成功して気づかない
ls -tの先頭を拾う実装は、DL 失敗時に無関係なファイルを壊す。時刻で絞るか差分を取る- Shadow DOM のサイトは「参照で選択 → 参照でスクロール → 固定座標でクリック」の組み合わせで進む
- PDF がないサービスは HTML を保存してローカルのヘッドレスブラウザで PDF 化する
- 取得手段は「直 URL があるか」「DL ボタンがあるか」「HTML しかないか」で機械的に選べる
- 再認証が頻繁に入るところは自動化を諦める。これも設計判断の一部
参考リンク
更新履歴
- 誤字を修正(掘む→3箇所を掴むに)
- 記事内リンクの修正
確定申告自動化
全6回個人事業主の経理を、UIを作らず Claude Code の skill だけで回した記録。領収書の自動取得から電子帳簿保存法の要件、freee への添付、月次締めまで。


