はじめに
現役の高校教員(数学)です。エンジニアではありません。
年度末に、Wordで作られた資料を大量にPDF化して1本にまとめる作業がありました。手作業だと「開く→PDFで保存→閉じる」の繰り返しで、数十ファイルあります。
Pythonで自動化しようとして、定番ライブラリが動かず、最終的にAppleScriptでWordを直接操作する形に落ち着きました。
同じところで詰まる人がいそうなので、動いた手順を残します。
環境
- macOS
- Microsoft Word(デスクトップ版)
- Python 3
- Claude Code
1. まず docx2pdf を試して、失敗した
macOSでdocx→PDFといえば docx2pdf です。内部でWordを操作してくれます。
入れて実行したら、こうなりました。
メッセージを理解できません。
WordのAppleScript連携でのエラーです。ライブラリが送っているコマンドを、こちらのWordが解釈できていない状態でした。
docx2pdf はWordのバージョンによって動いたり動かなかったりします。動かないときに中で何が起きているかが見えないのが厄介でした。
LibreOfficeを入れて soffice --convert-to pdf を使う手もありますが、Wordの体裁が崩れる可能性があります。配布する資料だったので、見た目が変わるのは避けたい。
そこで、Wordを直接操作することにしました。docx2pdf がやっていることを自分で書く、ということです。
2. 動いたAppleScript
最終的にこうなりました。
tell application "Microsoft Word"
open docPath read only true
delay 5
set theDoc to active document
save as theDoc file name pdfPath file format format PDF
close theDoc saving no
end tell
短いですが、3か所にハマりどころがあります。
read only true が重要
open docPath read only true
読み取り専用で開きます。これが最大のポイントでした。
通常どおり開くと、ファイルによっては
- 「編集を有効にしますか」
- 「他のユーザーが使用中です」
- 更新のダイアログ
といったモーダルが出て、スクリプトがそこで止まります。しかも画面を見ていないと止まっていることに気づきません。
読み取り専用で開くと、この手のダイアログがほぼ出なくなります。今回はPDFにするだけで編集はしないので、読み取り専用で困りません。
delay を入れる
delay 5
open の直後に save as すると、まだ開き終わっていないことがあります。Wordの起動やファイルサイズによって所要時間が変わるため、待ちを入れます。
4〜6秒あたりで安定しました。短くすると、たまに失敗します。「たまに」なのが一番たちが悪いので、余裕を持たせています。
行儀のいい書き方ではありませんが、GUIアプリを外から叩く以上、ここは割り切りました。
close theDoc saving no で閉じる
close theDoc saving no
saving no を付けないと、「変更を保存しますか?」で止まります。読み取り専用で開いていても、付けておく方が安全です。
3. タイムアウトとリトライ
Pythonから osascript を呼ぶとき、タイムアウトを長めに設定しておかないと、大きなファイルで落ちます。
体感では、標準的な120秒だと足りないことがありました。150秒程度に延ばすと安定しました。
そして、失敗したときのリカバリが必要です。処理が途中で落ちると、Wordがファイルを開いたまま残ります。この状態で次のファイルを処理すると、また止まります。
なので、リトライの前にWordごと終了させます。
tell application "Microsoft Word" to quit saving no
流れとしてはこうです。
1ファイル変換
↓ 失敗したら
Wordを終了
↓
少し待ってからリトライ
「失敗したらアプリごと再起動する」という、原始的ですが確実な方法です。GUIアプリを自動操作するときは、状態をきれいにしてやり直す方が早いという結論になりました。
4. PDFの結合は pypdf
変換できたら、まとめて1本にします。
from pypdf import PdfWriter
writer = PdfWriter()
for pdf in pdf_list:
writer.append(pdf)
writer.write("結合済み.pdf")
実行すると、こういう警告が出ることがあります。
Ignoring wrong pointing object ...
**これは無害です。**PDF内部の参照が少し変でも、pypdfが読み飛ばしているだけで、出力は正常でした。最初は何か壊れているのかと思って調べましたが、気にしなくて大丈夫です。
5. ファイル名の特殊文字は平気だった
心配していたのがこれです。教育系の資料は、ファイル名に
‗(全角アンダーバー) Ⅰ Ⅱ Ⅲ(ローマ数字)
といった文字が普通に入っています。
結論から言うと、そのまま動きました。AppleScript経由でも pypdf でも、特に問題は起きませんでした。事前にリネームする処理を書こうとしていたので、これは助かりました。
「たぶん壊れるだろう」と思って先に対策を書くより、一度そのまま流してみる方が早い場合がある、という例でした。
6. ハマったポイントまとめ
| ハマり | 対処 |
|---|---|
docx2pdf が「メッセージを理解できません」 |
AppleScriptで直接Wordを操作する |
| ダイアログでスクリプトが止まる |
open ... read only true で開く |
| 開き終わる前に保存して失敗する |
delay 4〜6 を入れる |
| 「変更を保存しますか」で止まる | close theDoc saving no |
| 大きいファイルでタイムアウト | タイムアウトを150秒程度に延ばす |
| 失敗後、次のファイルも連鎖して失敗 | Wordごと終了させてからリトライ |
| pypdfの警告が怖い |
Ignoring wrong pointing object は無害 |
| ファイル名の特殊文字 | 今回は対策不要だった |
7. 非エンジニアとしての感想
この作業で一番助かったのは、エラーメッセージをそのまま貼れることでした。
「メッセージを理解できません。」という日本語のエラーは、検索してもなかなか的確な情報にたどり着けません。Claude Codeに貼ったら、「WordのAppleScript連携で、開くときのダイアログが原因の可能性がある」という当たりを付けてくれて、read only true にたどり着きました。
もう1つ実感したのは、ライブラリが動かないときに、その下のレイヤーに降りられることです。
docx2pdf は結局AppleScriptでWordを操作しているだけです。それが分かれば、自分で同じことを書けばいい。以前の私なら「ライブラリが動かない=無理」で終わっていました。中で何をやっているかを聞けば、代わりが書けるというのは、非エンジニアにとってかなり大きな変化でした。
8. GUIアプリを外部から自動操作するとき、最初に疑う3つ
Word以外のGUIアプリをAppleScript等で自動操作するときも、詰まったらまずこの3つを疑うようにしています。
- ダイアログで止まっていないか。 画面を見ていないと気づけない。読み取り専用で開けないか、まず確認する
- 直前の操作が終わる前に、次を実行していないか。 起動やファイルサイズで所要時間が変わる操作の直後は、待ちを入れる
- 失敗後、アプリが変な状態のまま残っていないか。 前の失敗を引きずって次も失敗する連鎖は、アプリごと再起動すれば大抵切れる
3つとも「行儀よく直す」より「状態をリセットしてやり直す」方向の対処です。GUIアプリを外から叩く以上、これくらいの割り切りが早いと思っています。
おわりに
やったことは泥臭い自動化です。delay 5 で待って、失敗したらアプリを再起動する。きれいではありません。
ただ、手作業で数十回繰り返すより確実です。人間は途中で1ファイル飛ばしますが、スクリプトは飛ばしません。
非エンジニアの自動化は、たぶんこのくらいの雑さでいいのだと思います。美しく書くことより、もう二度と手でやらなくていい状態にする方が価値があります。
次は趣味の話を書く予定です。ブログの入荷情報をClaude Codeに巡回させて、一覧をiCloud経由でスマホから見られるようにした話です。