1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[macOS] 次々にWebサイトを表示して PDF出力するスクリプト [AppleScript(JXA)]

Posted at

姉妹記事

✅技術要素

  • JavaScript for Automation (JXA)を使って、Safariを操作します
  • PDF出力するサイトのURLは、あらかじめテキストファイルに記述しておきます
  • ファイルオープンダイアログを表示して、指定されたURLリストを読み込み処理します

macOS 26.1 (25B78)
Safari 26.1 (21622.2.11.11.9)
AppleScript 2.8


✅URLリスト(テキストファイル)

  • 単純に、1行:1URLで記述します

    URL_list.txt
    https://qiita.com/nak435/items/800dbda782f4fe9c7df1
    https://qiita.com/nak435/items/67aca33ca49b6f328faa
    https://qiita.com/nak435/items/d6d9782d43c5e948447b
    https://qiita.com/nak435/items/02fb97a666f0596c0316
    

✅AppleScrip(JXA)スクリプト

・ Safariの開発タブ

「Apple Event からの JavaScript を許可」にチェック✅

dev_menu.png

これを忘れると、次のエラーとなります。
You must enable 'Allow JavaScript from Apple Events' in the Developer section of Safari Settings to use 'do JavaScript'.

・ スクリプトエディタで JavaScript を選択

change_javascript.png

・ コード

url2pdf_jxa.scrt
// JavaScript for Automation (JXA)
(function() {
    const app = Application.currentApplication();
    app.includeStandardAdditions = true;

    const safari = Application("Safari");
    const systemEvents = Application("System Events");
    const safariProcess = systemEvents.processes.byName("Safari");

    // URLリストのファイル選択
    const filePath = app.chooseFile({ withPrompt: "URLリストのテキストファイルを選択してください。" });
    const content = app.read(filePath);
    const urlList = content.split(/[\r\n]+/);

    // Safariの準備
    if (safari.windows.length === 0) {
        safari.documents.push(safari.Document());
        delay(1);
    }

    let urlCount = 1;
    for (let aUrl of urlList) {
        if (aUrl.trim() === "") continue;

        safari.activate();
        delay(0.1);
        
        safari.windows[0].currentTab.url = aUrl;
        console.log(`URL #${urlCount}  ${aUrl}`);

        if (wait4page(10)) {
            const filename = `${urlCount}_`;
            if (write2pdf(filename)) {
                console.log(` #${urlCount} Write to PDF succeeded.`);
            } else {
                console.log(` #${urlCount} Write to PDF failed.`);
            }
            urlCount++;
        }
    }

    // --- 関数定義 ---
    function wait4page(repeatCount) {
        while (repeatCount > 0) {
            delay(0.1);
            // Safariのドキュメントステータスを確認
            const state = safari.doJavaScript('document.readyState', { in: safari.windows[0].currentTab });
            if (state === "complete") return true;
            repeatCount--;
        }
        return false;
    }

    function write2pdf(filename) {
        app.setTheClipboardTo(filename);
        safari.activate();
        delay(0.5);

        const fileMenu = safariProcess.menuBars[0].menuBarItems.byName("ファイル").menus[0];
        const pdfMenuItem = fileMenu.menuItems.byName("PDFとして書き出す…");

        // メニュー項目の確認
		var retryCount = 0
        while (!pdfMenuItem.exists() || !pdfMenuItem.enabled()) {
			//console.log("wait for menu enabled")
			if (retryCount > 1 && retryCount % 12 == 0) {
				safariProcess.menuBars[0].menuBarItems.byName("表示").menus[0].menuItems.byName("ページを再読み込み").click();
				console.log("reloaded!")
			}
			retryCount++;
			delay(0.2) 
		}

        pdfMenuItem.click();
        delay(1); // シートが出るのを待つ

        // 保存ボタンとシートの特定
        let windowNo = -1;
        let targetSheet = null;

        for (let n = 0; n < safariProcess.windows.length; n++) {
            const win = safariProcess.windows[n];
            try {
                const sheet = win.sheets[0];
                if (sheet.exists() && sheet.splitterGroups[0].buttons.byName("保存").exists()) {
                    windowNo = n;
                    targetSheet = sheet;
                    break;
                }
            } catch (e) { }
        }

        if (windowNo === -1) {
            const res = app.displayDialog("Button '保存' not found.", {
                buttons: ["OK"],
                defaultButton: "OK"
            });
			return false;
        }

        // ファイル名の入力と保存
        safari.activate();
        systemEvents.keyCode(123); // 左矢印キー
        delay(0.5);
        systemEvents.keystroke("v", { using: "command down" }); // 貼り付け
        delay(0.5);

        const saveButton = targetSheet.splitterGroups[0].buttons.byName("保存");
        saveButton.click();
        delay(0.5);

        // 上書き確認ダイアログの処理
        try {
            const replaceSheet = targetSheet.sheets[0];
            if (replaceSheet.exists()) {
                replaceSheet.buttons.byName("置き換え").click();
            }
        } catch (e) {}

        //閉じるのを待つ
        while (targetSheet.exists()) {
            delay(0.1);
			console.log("wait for close sheet")
        }
        return true;
    }

    console.log("Completed.");
	app.displayDialog("Mission Completed.", {buttons: ["OK"], defaultButton: "OK"});
	return true;
})();

スクリプトエディタで繰り返し実行できるように、
グローバル変数を使わず、すべてを関数(ローカル変数)で記述しました。
これをしないと、applescript Error: SyntaxError: Can't create duplicate "variable-name"エラーになります。

✅実行例

スクリプトエディタで開いて実行するか、次のコマンドで実行します。

osascript url2pdf_jxa.scpt
#1.gif
  • PDFファイル名は、ウインドウタイトルの先頭に 1〜 番号_を付与したファイル名になります
  • すでに同名のファイルが存在する場合は、無条件に上書きします
  • 注)スクリプトが終了するまで、UI操作をしないでください

✅出力されるPDF

メニュー ファイル "PDFとして書き出す…" で出力される PDFは、次のように縦に長い PDFとなります。
「印刷」でPDF化したものとは まったく異なります。

nak435-Qiita.png

もし、印刷目的のPDFであれば、「印刷」でPDF化することをお勧めします。




以上

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?