シナリオ② 手動運用

毎朝7分、
数値を残す習慣をつくる。

X投稿のエンゲージメント数値を、毎朝Notionに手動転記するための運用マニュアル。シナリオ①(Notion→Buffer→X)で投稿された前日分の数値を、Buffer Sent画面とX本体から拾って記録します。

🛠
対象
運用者本人

⏱
所要時間
1日 約2〜3分

🎯
作業時刻
毎朝 7:00

📱
主な使用機器
iPhone

━━ 毎朝のフロー ━━
⏰
7:00 通知

→
📝
Notion

→
📦
Buffer

→
𝕏
X(フォロワー数)

→
✅
完了

数値の取得元はBuffer Sent画面とX本体。Notionが記録の中心。

このマニュアルの位置づけ

シナリオ②(投稿の数値取得 → Notion記録)を手動転記運用で行うための手順書です。

i
シナリオ①は別運用。Make + Buffer + Notion での投稿自動化(シナリオ①)は別途稼働中。本マニュアルでは触りません。シナリオ②の自動化(Make + X API連携)は試行の末に断念し、手動運用に方針確定済みです。

PHASE 1 — 完了済み

初回セットアップ(再現用)

以下は構築時に完了済み。再現や確認が必要な場合のみ参照。

Notion DB の追加カラム

「X投稿管理データベース」に以下2つのカラムを追加済み。

カラム名 型 数値形式 取得元(Buffer表記)
インプレッション数 数値 数値(Number) Impressions
エンゲージメント率 数値 パーセント Eng. Rate
!
パーセント形式の入力ルール。Notionのパーセント形式は、入力値をそのまま%付きで表示します。Buffer表示が 1.23% なら、Notionには 1.23 と入力するだけでOK。0.0123 のように小数変換しないこと。

Notion ビュー「数値転記待ち」

「X投稿管理データベース」内に専用ビューを作成済み。未記録の投稿だけが表示される。

設定項目 値
ビュー種類 テーブル
フィルタ① 投稿ステータス: 投稿済み
フィルタ② いいね数: 未入力
並べ替え 投稿予定日時: 降順
表示プロパティ X投稿文 / 投稿予定日時 / 投稿URL / いいね数 / リプライ数 / リポスト数 / インプレッション数 / エンゲージメント率 / フォロワー数 / 気づき
非表示プロパティ 投稿ステータス / 投稿テーマ / タイプ / カテゴリ / 文字数 / 投稿ID / 他媒体展開済み

iOS リマインダー & Buffer アプリ

毎朝の作業をフックするリマインダーと、スマホで数値を確認するためのBufferアプリ。

A
iOS リマインダー
毎朝7:00に通知を発火

  • タイトル: X投稿の数値転記
  • 時刻: 毎朝 7:00
  • 繰り返し: 毎日
  • (任意)メモ欄に「数値転記待ち」ビューのURLを貼ると、通知タップから1ステップで開ける

B
Buffer iOS アプリ
Sent画面で5項目の数値を確認

  • App Storeで「Buffer」をインストール済み
  • 運用中のXアカウントでログイン済み
  • Sent画面で Reactions / Comments / Reposts / Impressions / Eng. Rate の5項目が表示できることを確認済み

PHASE 2 — 毎朝の運用

転記の7ステップ

7:00の通知から完了確認まで、所要時間は1〜2分。

1
リマインダー通知をタップ
7:00 にiPhoneの通知が届く

iOS リマインダーから通知が届く。タップで作業開始。

2
Notion「数値転記待ち」ビューを開く
未記録の投稿だけが表示される

Notion アプリを起動し、「X投稿管理データベース」 → 「数値転記待ち」タブをタップ。表示されている1件(=前日の投稿)の行をタップして詳細画面を開く。

i
必ず「数値転記待ち」タブで作業する。メインビューは過去全件が表示されて混乱の元。

3
Buffer Sent画面で数値を確認
5項目の数値を読む

Buffer iOS アプリを開き、Xアカウントを選択 → Sent タブ → 該当投稿をタップ。以下の対応関係で数値を確認する。

Buffer 表記 Notion カラム
Reactions いいね数
Comments リプライ数
Reposts リポスト数
Impressions インプレッション数
Eng. Rate エンゲージメント率

4
Notion に5項目を転記
行の詳細画面で全項目入力するのが楽

Notion の詳細画面に戻り、5つの数値を入力する。

✓
入力のコツ: 行を開いた詳細画面なら、各項目が縦に並ぶのでスマホでも入力しやすい。「いいね数」を最後に入力すると、確定した瞬間にビューから行が消える=処理済みのサインになる。

!
0 は明示的に入力する。いいね数・リプライ数・リポスト数が0だった場合、空欄ではなく 0 と入力。空欄のままだとビューから消えず、未記録扱いのまま残ってしまう。

5
X本体でフォロワー数を確認
その日のスナップショットを記録

X iOS アプリ または Safari で https://x.com/yukinostyle8 を開き、現在のフォロワー数を確認 → Notion の「フォロワー数」欄に転記。

i
フォロワー数は「投稿に紐付いた値」ではなく 「その日のスナップショット」。日次推移を追うための値なので、毎日同じタイミング(7:00)で取得することに意味がある。

6
気づきを記入(任意)
あれば書く、なければ空欄でOK

その投稿について何か気づきや振り返りがあれば「気づき」欄にメモ。なければ空欄のままで構わない。

7
完了確認
ビューから行が消えていればOK

詳細画面を閉じ、「数値転記待ち」ビューから対象の行が消えていることを確認。消えていれば作業完了。

PHASE 3 — ルール

運用ルール一覧

迷ったときの判断基準。

ルール 内容
作業時刻 毎朝 7:00
投稿時刻(現状) 毎日 6:00(変更時はマニュアル更新)
いいね数=0 の扱い 空欄ではなく 0 と明示入力
24時間未満の投稿 その日は処理せず、翌日のリマインダー時に処理
1日複数投稿 現状なし(発生時にルール再検討)
過去分のキャッチアップ 通常運用に組み込む(無理に遡らない)
作業を忘れた日 翌日まとめて処理してOK。ビューに残るので消えない

PHASE 4 — トラブル対応

うまくいかないときに見るところ

よくある詰まりポイントと対処法。

Q「数値転記待ち」ビューに行が表示されない

投稿ステータスが「投稿済み」になっていない可能性。メインビューで該当投稿のステータスを確認する。シナリオ①のMakeが「投稿済み」に更新するはずなので、これが効いていない場合はシナリオ①側の問題。
または、既に転記済み(=フィルタ条件を満たさない)の場合は表示されないのが正常な挙動。

Q数値を入力しても消えない

いいね数を 0 と入力する代わりに空欄のままになっている可能性。0 と明示入力すること。または、入力が確定していない可能性もある。セルの外側をタップしてフォーカスを外すか、行を開いた詳細画面で入力する。

Q入力した数値が確定せずに消える

これは多くの場合、入力した瞬間にビューのフィルタから外れて行が消える正常動作。メインの「テーブルビュー」タブに切り替えて、データが保存されているか確認する。本当に保存されていない場合は、行を開いた詳細画面で入力するのが確実。

QBuffer Sent画面で過去投稿が見つからない

Bufferの保持期間を超えている可能性。スマホ版で見つからない場合は、PCのブラウザ版 (https://publish.buffer.com/sent) でも確認。それでも無い場合はその投稿の数値はスキップ。

QNotion ビューの並びがおかしい

ソートが解除されている可能性。ビュー設定 → 並べ替え → 投稿予定日時 降順 を再設定する。

Q投稿URL欄に貼り付けても「無効」と出る

カラムの型が「数値型」になっている可能性。カラムヘッダーを右クリック → プロパティを編集 → 種類を URL に変更する。

PHASE 5 — 設計の前提

なぜこの運用にしたか

将来見直すときの判断材料。

Qなぜ24時間後に取得するか

X投稿のエンゲージメントは投稿後24時間でほぼ確定する。それ以降の伸びは緩やか。24時間ルールはシンプルで運用負荷が低く、毎日同じ時刻に作業すれば自然に「前日分」を拾える。

Qなぜフォロワー数を毎日記録するか

投稿ごとの数値ではなく「その日のスナップショット」として記録する。日次推移を追えるので、伸びている期間と落ちている期間を可視化できる。同じ投稿に紐付く値ではない点に注意し、時系列分析で使う。

Qなぜ Buffer の Eng. Rate を自前計算しないか

Bufferの計算定義は非公開で、分母にプロフィールクリック等を含む可能性がある。Notion で (いいね+リプライ+リポスト)÷インプレッション×100 の数式を組むと、Buffer表示値とズレて混乱する。Buffer表示値をそのまま転記するのが堅実。

Qなぜ手動運用を選んだか

シナリオ②の自動化(Make + X API)を試行したが複数の障害で断念。Make公式のX/Twitterモジュールは2025年5月廃止、HTTPモジュールはOAuth 1.0a非対応、OAuth 2.0に切り替えてもMake側で oauth/wait: Not found エラーで Connection 作成が完了しなかった。手動転記は1日2〜3分の負荷で済み、データ取得の確実性が高い(API障害の影響を受けない)。詳細は phase2_x_direct_attempt_log.md 参照。

✓毎朝のチェックリスト
  • 7:00 のリマインダー通知をタップした
  • Notion「数値転記待ち」ビューを開いた
  • 該当行をタップして詳細画面を開いた
  • Buffer Sent画面で5項目の数値を確認した
  • いいね数・リプライ数・リポスト数を転記した(0なら0を明示入力)
  • インプレッション数を転記した
  • エンゲージメント率を転記した(Buffer表示そのまま)
  • X本体でフォロワー数を確認して転記した
  • 気づきがあれば「気づき」欄にメモした
  • 詳細画面を閉じ、ビューから行が消えたことを確認した

関連ドキュメント

本マニュアルと関連する他のプロジェクトファイル。

ファイル名 内容
scenario2_handoff_prompt.md 本マニュアル設計時の引き継ぎプロンプト
make_notion_buffer_implementation_log.md シナリオ①(投稿自動化)の実装記録
phase2_x_direct_attempt_log.md Phase 2(X API自動化試行)の失敗ログ
system_settings_snapshot.md 全システムの設定値スナップショット

MyASP × Gmail × Obsidian × launchd

配信予約したら、
勝手にアーカイブ。

専用Gmailアドレスで受信→毎日22時+Mac起動時に自動でObsidianに保存。一度仕掛ければ、もう二度とコピペ作業はいりません。実装で実際にぶつかった問題の解決策も全部この中に。

📬
入力
専用Gmail受信箱

📝
出力
『5.4』件名.md

⏰
実行
22時 + 起動時

⏱
セットアップ
約60〜90分

なぜこの設計なのか

最初は「メインのGmailにフィルタを作って絞り込む」案でした。でも、メルマガ配信元から登録通知・コメント返信・Zoom申し込み等が同じFromアドレスで届いているので、「配信メルマガ本体だけを完璧に絞り込む」ことがほぼ不可能。だから方針転換。

★
解決策: メルマガ受信専用の新しいGmailアドレスを作って、そこで読者登録する。配信メルマガしか届かないので、フィルタが超シンプルに。これが最大のコツ。

仕組みの全体像

この仕掛けが裏で何をしているか。流れがわかると、どこかで止まったときに自分で原因を切り分けられます。

📤
MyASPで配信予約
メルマガ受信専用のGmailアドレスを読者リストに含めておく。

📥
配信時刻に専用Gmailへ届く
Gmailフィルタで自動的にラベル付け。

⏰
毎日22時 or Mac起動時に起動
launchdが自動でPythonスクリプトを実行。

🔍
imapclientでGmail接続
日本語ラベル名を正しく扱える専用ライブラリを使用。Message-IDで重複管理。

✏️
本文をmdファイル化
件名に既に『◯.◯』があれば重複付与しない。フロントマター付き。

🗂
年フォルダに自動振り分け
2026年/、2027年/…と自動で生成。iCloud同期でObsidianへ。

i
22時にMacがオフだったら? その回はスキップされますが、次にMacを起動した瞬間に自動的に追いつき処理されます(RunAtLoad: trueの効果)。Message-ID管理なので取りこぼし・二重保存は起きません。

10ステップで、仕掛け完了。

上から順に進めれば60〜90分で完成します。途中でつまずきやすいポイントは★印で警告しています。

1
メルマガ受信専用のGmailアドレスを用意
普段使いのGmailを汚さないために。

新規でGmailアドレスを作るか、既存の使っていないGmailアドレスを使う。

  1. そのアドレスでMyASPの読者登録を完了する
  2. メルマガが配信時刻にちゃんと届くことを確認(数分待って受信トレイをチェック)
★
これが最大のコツ: メインのGmailで頑張らない。「メルマガしか届かない箱」を作るのが圧倒的にラク。

2
2段階認証を有効化
アプリパスワード取得の前提条件。

専用Gmailにログインした状態で Googleアカウント → セキュリティ を開く。

  1. 「2段階認証プロセス」の状態を確認
  2. オフなら「オン」にする(SMS or 認証アプリ)
  3. 「オン」になっていれば次のステップへ

3
アプリパスワードを取得
Pythonスクリプトがログインするための鍵。

専用Gmailにログイン中の状態で アプリパスワード作成ページ を開く。

  1. 「アプリ名」に obsidian-archive と入力
  2. 「作成」をクリック
  3. 表示された16桁の文字列をすぐにメモアプリに保存
!
絶対に守ること:

・この16桁は他人に見せたらGmailを乗っ取られます

・チャット・SNS・メール・GitHubなどに絶対に貼らない

・もし漏洩したら、すぐにこのページで削除して再発行

・閉じると二度と表示されないので、必ずメモしてから閉じる

4
Gmailフィルタとラベルを作る
配信メルマガだけにラベルを付ける。

専用Gmailを開いて、右上の歯車⚙️ → すべての設定を表示 → フィルタとブロック中のアドレス → 新しいフィルタを作成。

  1. From欄: MyASPの配信元アドレスを入力(例: info@example.jp)
  2. 件名欄: 『 (全角左二重カギ括弧)
  3. 「フィルタを作成」をクリック
  4. 「ラベルを付ける」にチェック → 「新しいラベル」 → 任意の名前(例: Obsidianへのメルマガアーカイブ)
  5. 「○件の一致するスレッドにもフィルタを適用する」にチェック
  6. 「フィルタを作成」で完了
!
必ず控えておく: ラベル名は大文字小文字も含めて完全一致でconfig.jsonに書く必要があります。サイドバーで実際の表示名をしっかり確認してください。

5
作業フォルダを作って仮想環境を構築
Macのターミナル作業の出発点。

ターミナルを開く(⌘+Spaceで「ターミナル」検索)。

Terminal

mkdir -p ~/myasp-archive
cd ~/myasp-archive
python3 -m venv venv
source venv/bin/activate
pip install imapclient

5行を順に実行(まとめて貼り付けてもOK)。

★
なぜvenv? HomebrewでPythonを入れていると、システム直下にライブラリを入れようとすると externally-managed-environment エラーが出ます。仮想環境なら安全に入れられます。

✓
プロンプトの先頭に (venv) が付き、Successfully installed imapclient が表示されればOK。

6
Obsidianの保存先パスを取得
FinderからD&Dで正確に。

Finderでメルマガを保存したい親フォルダ(年フォルダの1つ上)を1回クリックして選択。

そのフォルダをターミナルウィンドウへドラッグ&ドロップ。長いパスが自動入力される。

例

/Users/[ユーザー名]/Library/Mobile Documents/iCloud~md~obsidian/Documents/[Vault名]/[途中のパス]/メルマガ

そのパスをマウスでドラッグしてコピー(⌘+C)→ メモアプリに保存。

ターミナルでは Control+U で入力した文字を全部消す。

!
D&Dで入力したパスには\\(バックスラッシュ)でスペースがエスケープされていますが、JSON設定では不要なので外す。例: Mobile\ Documents → Mobile Documents

7
設定ファイル(config.json)を作る
アドレス・パスワード・ラベル・保存先をまとめる。

Terminal

nano config.json

下を貼り付けて、4か所を自分の値に書き換える。

config.json

{
  "gmail_address": "専用アドレス@gmail.com",
  "app_password": "取得した16桁スペースなし",
  "label": "Obsidianへのメルマガアーカイブ",
  "vault_base_path": "/Users/[ユーザー名]/Library/Mobile Documents/iCloud~md~obsidian/Documents/[Vault名]/[途中]/メルマガ"
}

  1. Control+O → Enter → Control+X で保存

動作確認(中身は表示されません):

確認コマンド

python3 -c "import json; c=json.load(open('config.json')); print('gmail_address:', c['gmail_address']); print('app_password length:', len(c['app_password'])); print('label:', c['label']); print('vault_base_path:', c['vault_base_path'])"

✓
app_password length: 16 が表示されればOK。違う数字ならスペースが混ざっている可能性。再度nano config.jsonでスペースを削除。

8
アーカイブスクリプト(archive.py)を作る
日本語ラベル対応・重複防止・年フォルダ自動振り分け。

Terminal

nano archive.py

下のコードをそのまま全部貼り付け(書き換え不要)。

archive.py

import email, json, re
from email.header import decode_header
from email.utils import parsedate_to_datetime
from datetime import datetime
from pathlib import Path
from imapclient import IMAPClient

BASE = Path(__file__).resolve().parent
CONFIG = json.loads((BASE / "config.json").read_text("utf-8"))
SEEN_FILE = BASE / "seen.txt"
LOG_FILE = BASE / "archive.log"


def log(msg):
    line = f"[{datetime.now().isoformat(timespec='seconds')}] {msg}\n"
    LOG_FILE.open("a", encoding="utf-8").write(line)


def load_seen():
    if not SEEN_FILE.exists():
        return set()
    return set(SEEN_FILE.read_text("utf-8").splitlines())


def add_seen(uid):
    SEEN_FILE.open("a", encoding="utf-8").write(uid + "\n")


def decode(s):
    if s is None:
        return ""
    parts = decode_header(s)
    return "".join(
        (b.decode(enc or "utf-8", errors="replace") if isinstance(b, bytes) else b)
        for b, enc in parts
    )


def extract_body(msg):
    if msg.is_multipart():
        for part in msg.walk():
            if part.get_content_type() == "text/plain":
                payload = part.get_payload(decode=True) or b""
                charset = part.get_content_charset() or "utf-8"
                return payload.decode(charset, errors="replace")
        return ""
    payload = msg.get_payload(decode=True) or b""
    charset = msg.get_content_charset() or "utf-8"
    return payload.decode(charset, errors="replace")


def safe_filename(name, maxlen=80):
    name = re.sub(r'[\\/:*?"<>|]', "", name)
    name = name.strip()
    return name[:maxlen] if len(name) > maxlen else name


def main():
    seen = load_seen()
    new_count = 0

    with IMAPClient("imap.gmail.com", ssl=True) as M:
        M.login(CONFIG["gmail_address"], CONFIG["app_password"])
        M.select_folder(CONFIG["label"])

        ids = M.search(["ALL"])
        if not ids:
            log("no messages found")
            return

        msgs = M.fetch(ids, ["RFC822"])

        for uid, data in msgs.items():
            raw = data[b"RFC822"]
            msg = email.message_from_bytes(raw)
            msg_id = msg.get("Message-ID", "").strip()
            if not msg_id or msg_id in seen:
                continue

            subject = decode(msg.get("Subject", ""))
            date_hdr = msg.get("Date", "")
            try:
                dt = parsedate_to_datetime(date_hdr)
            except Exception:
                dt = datetime.now()

            year_folder = Path(CONFIG["vault_base_path"]) / f"{dt.year}年"
            year_folder.mkdir(parents=True, exist_ok=True)

            # 件名にすでに『◯.◯』形式の日付があれば追加付与しない
            date_label = f"『{dt.month}.{dt.day}』"
            if re.search(r"『\d+\.\d+』", subject):
                base_name = subject
            else:
                base_name = f"{date_label}{subject}"
            filename = safe_filename(base_name) + ".md"
            out_path = year_folder / filename

            if out_path.exists():
                stem = out_path.stem
                n = 2
                while (year_folder / f"{stem} ({n}).md").exists():
                    n += 1
                out_path = year_folder / f"{stem} ({n}).md"

            body = extract_body(msg)
            front = (
                f"---\n"
                f"date: {dt.isoformat(timespec='minutes')}\n"
                f"subject: {subject}\n"
                f"from: {decode(msg.get('From',''))}\n"
                f"---\n\n"
            )
            out_path.write_text(front + body, encoding="utf-8")
            add_seen(msg_id)
            seen.add(msg_id)
            new_count += 1
            log(f"saved: {dt.year}年/{out_path.name}")

    log(f"done: {new_count} new")


if __name__ == "__main__":
    main()

  1. Control+O → Enter → Control+X で保存
★
imapclient を使う理由: Pythonの標準imaplibは日本語ラベル名でエラーを吐きます(UnicodeEncodeErrorまたはCould not parse command)。imapclientはGmailの日本語ラベルをUTF-7に正しく変換してくれます。

9
手動で動作確認
launchd登録の前に必ずここをクリア。

Terminal

python3 archive.py
cat archive.log

✓
ログに saved: 2026年/『5.6』〜.md や done: N new が出ればOK。Obsidianを開いて、年フォルダにファイルが追加されているか確認。

!
エラーが出たら: 後述の「つまずいたら」セクションを参照。多くの場合、ラベル名の大文字小文字違い、アプリパスワードのスペース混入、JSONの構文エラーが原因。

10
launchdで自動化
22時 + Mac起動時の2タイミングで実行。

Terminal

nano ~/Library/LaunchAgents/com.local.myasp-archive.plist

下を貼り付けて、[ユーザー名]を3か所書き換える(whoamiコマンドで確認可)。

com.local.myasp-archive.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.local.myasp-archive</string>
  <key>ProgramArguments</key>
  <array>
    <string>/Users/[ユーザー名]/myasp-archive/venv/bin/python3</string>
    <string>/Users/[ユーザー名]/myasp-archive/archive.py</string>
  </array>
  <key>StartCalendarInterval</key>
  <dict>
    <key>Hour</key>
    <integer>22</integer>
    <key>Minute</key>
    <integer>0</integer>
  </dict>
  <key>RunAtLoad</key>
  <true/>
  <key>StandardOutPath</key>
  <string>/Users/[ユーザー名]/myasp-archive/stdout.log</string>
  <key>StandardErrorPath</key>
  <string>/Users/[ユーザー名]/myasp-archive/stderr.log</string>
</dict>
</plist>

★
2つの重要ポイント:

1. /usr/bin/python3 ではなくvenvのpython3を指定(imapclientが使える環境)

2. RunAtLoad: true でMac起動時にも自動実行(22時を逃しても取りこぼし回収)

  1. Control+O → Enter → Control+X で保存

登録して動作確認:

Terminal

launchctl load ~/Library/LaunchAgents/com.local.myasp-archive.plist
launchctl list | grep myasp

✓
– 0 com.local.myasp-archive が表示されればOK。load直後にも1回スクリプトが実行される(RunAtLoad: trueの効果)ので、cat archive.logで確認可能。

つまずいたら、ここを。

実装時に実際に発生したエラーと、その解決策。

Err 01

UnicodeEncodeError: ‘ascii’ codec…

原因

標準imaplibは日本語ラベル名を扱えない。

対処

imapclientを使う(本マニュアル通りに作っていれば発生しない)。

Err 02

SELECT command error: BAD

原因

ラベル名が見つからない、または書式不一致。

対処

下記コマンドで全ラベル名を確認し、config.jsonに完全一致(大文字小文字含む)で書く:

確認

python3 -c "from imapclient import IMAPClient; import json; c=json.load(open('config.json')); M=IMAPClient('imap.gmail.com', ssl=True); M.login(c['gmail_address'], c['app_password']); [print(repr(f[2])) for f in M.list_folders()]; M.logout()"

Err 03

externally-managed-environment

原因

HomebrewのPythonにシステム直下でpip installしようとした。

対処

必ずvenvを作って、その中でpip install。Step5の手順通りに。

Err 04

app_password length が16以外

原因

16桁にスペースが含まれている、または余計な文字が入っている。

対処

nano config.jsonで開き、app_password行のスペースをすべて削除。16文字きっかりに。

Err 05

JSONDecodeError

原因

config.jsonの構文ミス(カンマ抜け、ダブルクオート抜け、{}重複等)。

対処

cat config.jsonで中身を見て、Step7のテンプレと比較。心配ならファイルを削除して作り直し。

Err 06

launchdが22時に動かない

原因

22時にMacがスリープ・電源オフだった。

対処

RunAtLoad: trueを入れていれば、次のMac起動時に自動実行されて取りこぼし回収。手動なら cd ~/myasp-archive && source venv/bin/activate && python3 archive.py。

Err 07

登録通知などが混じる

原因

Gmailフィルタの絞り込みが甘い。

対処

件名の条件を 『 に絞ると配信メルマガのみに。それでも混じる場合はObsidianで手動削除。

Err 08

『5.6』が件名に二重に付く

原因

登録通知などは件名にすでに『◯.◯』が含まれている。

対処

本マニュアルのarchive.pyは件名に『◯.◯』があれば追加付与しない仕様。古いコードを使っているなら最新版に書き換え。

もしアプリパスワードが漏れたら

チャットや投稿に誤って貼ってしまった場合の緊急対応。

!
すぐに無効化:
アプリパスワード管理ページ で該当エントリを削除。これで漏れた16桁は即時無効。その後、新規発行してconfig.jsonを更新。

停止・再開・手動実行

必要なときに見るコマンド集。

一時停止

launchctl unload ~/Library/LaunchAgents/com.local.myasp-archive.plist

再開

launchctl load ~/Library/LaunchAgents/com.local.myasp-archive.plist

手動で1回だけ実行

cd ~/myasp-archive
source venv/bin/activate
python3 archive.py

動作ログを見る

tail -n 30 ~/myasp-archive/archive.log

エラーログを見る

cat ~/myasp-archive/stderr.log

時刻を変える(例:21時に変更)

nano ~/Library/LaunchAgents/com.local.myasp-archive.plist
# <integer>22</integer> を <integer>21</integer> に書き換え、保存
launchctl unload ~/Library/LaunchAgents/com.local.myasp-archive.plist
launchctl load ~/Library/LaunchAgents/com.local.myasp-archive.plist

Make × Notion × Buffer

Notionで管理、
Bufferで安全にX投稿。

Makeを初めて使う人向けの、Notion投稿管理データベースとX投稿運用の構築マニュアル。手順通りに進めれば、初期設定から運用開始まで到達できます。

🛠
対象
Make初心者

⏱
所要時間
約60〜90分

🎯
最終ゴール
Notion→Buffer連携

🔒
運用方針
自動投稿・人が確認

━━ 全体フロー ━━
📝
Notion

→
⚙️
Make

→
📦
Buffer

→
𝕏
X(投稿)

投稿管理の中心はNotion。Makeは中継役、XへはBuffer経由で安全に。

なぜMakeから直接Xに投稿しないのか。

XのAPI制限や仕様変更の影響を避けるため、Bufferを中継します。Buffer経由にすることでX API直連携の不安定さを回避し、投稿後の状態(Queue/Sent/Error)も一覧で追えます。

★
Buffer経由のメリット
・投稿予約がしやすい
・投稿後の状態をBufferのSent/Errorで確認できる
・X以外のSNSにも展開しやすい
・Make側の設定が複雑になりにくい
・誤投稿リスクを下げやすい

!
最初から完全自動化を目指さない
初期は Now: No(下書き保存)で動作確認し、安定したら Now: Yes / Post immediately(即投稿)に切り替えるのがおすすめ。本マニュアルの推奨は Lv.2(半自動)で安定させてから Lv.3(自動投稿)に移行 です。



Phase 1

Makeアカウントを作って、初期設定を済ませる

登録時の質問6つに答えればセットアップ完了。各画面で何を選ぶか、なぜそれを選ぶかを順番に説明します。

1
Makeのアカウントを作る
公式サイトでサインアップ。メールアドレスとパスワードがあればOK。

  1. https://www.make.com/ を開く
  2. 右上の 「Get started free」 または 「Sign up」 をクリック
  3. メールアドレス・パスワードを入力して登録
  4. 確認メールが届くのでリンクをクリックして本人確認
★
登録直後にリージョン(サーバーの地域)を聞かれることがあります。特に指定がなければ EU のままで進めて構いません。

2
「Discovery」画面で答える
Makeをどこで知ったか。

How did you hear about Make?
Makeをどこで知りましたか?
✓Recommended by AI

ChatGPTなどAIに相談しながらMakeを使い始める場合、この選択が自然です。

3
「Experience」画面で答える
自動化ワークフローの経験。

How would you describe your experience with automating workflows?
自動化ワークフローの経験をどう説明しますか?
✓I have no experience with automation

Makeを初めて使う場合は、初心者向けの選択で問題ありません。

4
「Skillset」画面で答える
技術スキルレベル。

Which of the following best describes your technical skill level?
あなたの技術スキルに一番近いものはどれですか?
✓I use everyday business tools or AI assistants, but not advanced features.

意味:普段の業務ツールやAIアシスタントは使うが、高度な機能までは使っていない。
ChatGPT・Notion・Googleスプレッドシートを使っていても、APIやWebhookを自分で扱う段階でなければこの選択が安全です。

5
「Building for」画面で答える
誰のためにMakeを使うか。

Who do you plan to use Make for?
Makeを誰のために使う予定ですか?
✓For my company

Notion管理、投稿管理、メルマガ管理、WordPress更新など、自分の事業の効率化に使うため。

6
「AI Intent」画面で答える
自動化の中でAIをどう使うか。

What is your primary goal for using AI in your automations?
自動化の中でAIを使う主な目的は何ですか?
✓AI for specific tasks

投稿ネタの整理、要約、分類、文章生成、分析など、自動化の一部にAIを使う想定だから。

7
「Tech Stack」画面でツールを選ぶ
よく使うツールにチェックを入れる。

最低限チェックする項目:

  • Google Sheets
  • Gmail
  • Notion
  • WordPress
  • OpenAI(ChatGPT, Whisper..)

SNS運用も視野に入れる場合は追加:

  • Instagram for Business
  • Facebook Pages
  • YouTube
  • Anthropic Claude
!
Xはこの画面に出ていない場合があります。X投稿はMakeから直連携ではなく、Buffer経由で行うので、ここに出ていなくて問題ありません。

8
選択内容のまとめ表で確認
最後に答えた内容が正しいかチェック。

画面 選ぶ項目
Discovery Recommended by AI
Experience I have no experience with automation
Skillset I use everyday business tools or AI assistants, but not advanced features.
Building for For my company
AI Intent AI for specific tasks
Tech Stack Google Sheets / Gmail / Notion / WordPress / OpenAI
✓
この内容で進めればMakeのダッシュボードに到達できます。これで Phase 1 完了。



Phase 2

Notionに投稿管理データベースを作る

投稿の中心はNotion。Makeに渡す前提のデータベース設計を、項目とステータスで固めます。

9
Notionで新規データベースを作る
空白ページに「Database – Full page」を追加。

  1. Notionのサイドバーで 「+ Add a page」 を押す
  2. ページタイトルに 投稿管理 と入力
  3. 本文エリアで 「/database」 と入力
  4. 表示された 「Database – Full page」 を選択

10
プロパティ(項目)を作る
下の表の通りに、項目を10個追加します。

項目名 種類 用途
ネタタイトル Title(タイトル) 投稿ネタの管理名
投稿本文 Text(テキスト) 実際に投稿する文章
切り口 Text 投稿の視点・訴求
カテゴリ Select お金を守る/AI実践記/法人化 など
ステータス Select 下書き/投稿待ち/投稿済み
投稿先 Select X/note/ブログ/メルマガ など
投稿予定日 Date 投稿予定日
生成月 Date ネタを生成した月
メモ Text 補足メモ
!
項目名を後から変えない。Makeで連携設定をした後にNotion側の項目名を変えると連携が崩れます。特に ネタタイトル投稿本文ステータス投稿予定日投稿先 は注意。

11
「ステータス」のセレクト項目を設計する
3段階。Makeが自動で拾うのは「投稿待ち」のみ。

「ステータス」プロパティに以下の3つのセレクト値を追加します。シンプルに3段階で運用します。

ステータス 意味
下書き 投稿文を作成中/確認前
投稿待ち Makeで拾ってよい状態
投稿済み 実際に投稿済み
★
重要:Makeが自動で拾う対象は、原則として 投稿待ち のみ。下書きや未確認の投稿までBufferに送られると誤投稿リスクがあるため、人が「投稿待ち」に変えた瞬間が連携トリガーになる設計にします。

12
テスト用の投稿データを1件入れる
Phase 4のテスト実行で使うサンプル。

  1. データベースで 「+ New」 を押す
  2. ネタタイトル に「テスト投稿」と入力
  3. 投稿本文 に100〜180文字程度の短い文章を入れる
  4. 投稿先 を X に
  5. ステータス は 下書き のまま(Phase 4でテストする時に「投稿待ち」へ変える)
!
テスト時の本文は 長文ではなく、100〜180文字程度 に。スレッド投稿や長文の検証は、基本フローが安定してから。



Phase 3

MakeでScenarioを作る(Notion → Buffer)

最初のScenarioは1本だけ。Notionで「投稿待ち」になった投稿をBufferに送り、Notionのステータスを更新する流れを作ります。

13
新しいScenarioを作成する
Makeダッシュボードから空のScenarioを開始。

  1. Makeにログインしてダッシュボードを開く
  2. 左メニューから 「Scenarios」 を選択
  3. 右上の 「+ Create a new scenario」 をクリック
  4. 空のキャンバスが表示される
  5. 画面上部の名前欄に Notion to Buffer と入力して保存

14
最初のモジュール:Notion「Search Objects(Data Source Items)」
投稿待ちかつ投稿予定日時が過ぎた行を、毎回検索して取得する。

  1. キャンバス中央の 「+」 円をクリック
  2. 検索欄に Notion と入力
  3. 「Search Objects」 を選択(説明:Searches for objects of a page, database, or database item.)
  4. 初回は 「Add」 ボタンからNotionと接続(OAuth認証)
  5. 連携先のWorkspace・ページを承認
  6. Search Objects ドロップダウンで 「Data Source Items」 を選択(重要)
  7. Data source ID 欄で「投稿管理データベース」を選択(または「Search」ボタンから検索)
  8. Filter を以下のように2条件設定:
    • 条件①:プロパティ 投稿ステータス (Select) → 演算子 Select: Equals → 値 投稿待ち
    • 「Add AND rule」 をクリックして条件②を追加
    • 条件②:プロパティ 投稿予定日時 (Date) → 演算子 Date: On or before → 値 {{now}}(Date and time 関数パネルから「now」を選択して挿入)
  9. Limit は 10 に設定
  10. 「Save」 で保存
!
「Search Database Items」ではなく「Search Objects」を選ぶ
Make の Notion 統合は2026年5月頃にアップデートされ、旧「Search Database Items」「Search Data Source Items」は 「Search Objects」 に統合されました。Object Type のドロップダウンで Data Source Items を選ぶことで、旧「Search Database Items」相当の動作になります。

!
フィルター条件で「Name」だけを直接選ばない
Make の演算子選択で String: Equals を選んで Name = 投稿待ち と書くと、ページ名(タイトル)側の Name を見ている可能性があります。
必ず Select: Equals(Selectカテゴリの演算子)を選び、プロパティは 投稿ステータス (Select) を選んでください。

i
なぜ Watch型ではなく Search型なのか
Watch Data Source Items は「新規作成・更新された行」を拾う仕組みです。「投稿予定日時が来た行」を自動で探す機能ではありません。
実際にWatch型で運用すると、以下の問題が起きます:

  • 投稿予定日時が来ても、その行が更新されていなければ拾われない
  • 別の投稿済み行を更新すると、その投稿済み行が拾われてしまう
  • 意図した投稿がBufferに送られない

そのため、本マニュアルでは Schedule起動 + Notion Search Objects (Data Source Items) を採用しています。

★
Notionと接続するとき、Notion側で 「Connections」→「投稿管理ページにMakeを追加」 する操作が必要な場合があります。Notion画面右上の「・・・」→「Connections」から確認できます。

15
Limit 10 運用の注意点を理解する
複数件まとめて送られないよう、運用ルールを守る。

Limit 10 で運用する場合、条件に合う「投稿待ち」が複数残っていると、まとめてBufferへ送られます。これを防ぐために、以下の運用ルールを守ってください。

  • 過去日時の「投稿待ち」を残さない
  • 同じ時間帯に「投稿待ち」を複数残さない
  • 投稿予定日時を正確に入力する
  • 投稿後はNotion側のステータスを「投稿済み」に変更する(Step 17でMakeが自動更新するので通常は不要)
  • 投稿後はBufferの Queue / Sent / Error を確認する
!
危険な状態の例
以下のように、過去日時の「投稿待ち」が複数残っていると、次回 Make 実行時に複数件まとめてBufferへ送られる可能性があります:

  • 2026/5/4 19:00 投稿待ち
  • 2026/5/5 07:00 投稿待ち
  • 2026/5/5 12:00 投稿待ち

投稿が完了したらすぐに「投稿済み」へステータスを変えるのが鉄則です。

i
Limit を 10 にする理由
実践時に Limit 1 で運用したところ、Notion API の返却順序の都合で意図した投稿文が検索結果に含まれないことがありました。Limit 10 にすることで、意図した投稿文が拾われ、Bufferに送信できました。

16
2つ目のモジュール:Buffer「Create a status update」
取得した投稿をBufferに送信する(初期は下書き、安定後は即投稿)。

  1. 右側の 「+」 をクリックして次のモジュールを追加
  2. 検索欄に Buffer と入力
  3. 「Create a status update」 を選択
  4. 初回は Buffer と接続(OAuth認証)
  5. Profile 欄で連携対象のXアカウントを選択
  6. Text 欄に、Notionから取得した 投稿本文 をマップする
  7. Now オプション(または Publication):
    ・初期(Lv.2 半自動運用):No(下書き保存)にして、Buffer上で確認してから手動でShare Now → 動作と投稿文の検証ができる
    ・安定後(Lv.3 自動投稿運用):Yes(または Publication: Post immediately)に切り替えると、Bufferに送信された瞬間にXへ自動投稿される
  8. 「OK」で保存
★
事前にBufferで無料アカウントを作り、Xアカウントを接続しておくこと。Buffer側でXが連携済みでないと、Make側で選択肢に出てきません。

17
3つ目のモジュール:Notion「Update a Data Source Item」
Bufferに送ったらNotionのステータスを更新する。

  1. 右側の 「+」 をクリック
  2. 検索欄に Notion と入力
  3. 「Update a Data Source Item」 を選択
  4. Update By:Data Source を選択
  5. Data Source ID:投稿管理データベースを選択
  6. Page ID:1つ目のNotionモジュール(Search Objects)から Page ID をマップ
  7. Fields に以下の項目を追加:
    • Key:投稿ステータス
    • Value Type:Select
    • Value:投稿済み
  8. 「Save」で保存
★
3ステータス運用では、BufferにデータをStep 16で送った時点で、Notion側を「投稿済み」に変えるシンプルな流れにします。Notionは「Makeが取り扱いを終えた」マーカーとして「投稿済み」を使います。Buffer→Xへの実投稿状況はBufferのSent/Errorタブで別途確認します。

✓
これで Notion → Make → Buffer → Notion更新 の3モジュール構成が完成。次のPhase 4でテスト実行します。



Phase 4

「Run once」で1件だけテストする

本番運用の前に、必ず1件だけ流して全モジュールが正常に動くか確認します。エラーが出たら次のモジュールに進ませません。

18
テスト投稿のステータスを「投稿待ち」に変える
Notionで状態を変えて、Makeに拾わせる準備。

  1. Notionの「投稿管理」DBで、Phase 2で作ったテスト投稿を開く
  2. ステータス を 下書き から 投稿待ち に変更
  3. 保存(自動保存)

19
Makeで「Run once」を実行
Scenario画面の左下にあるボタン。

  1. Make Scenario画面の左下にある 「Run once」 をクリック
  2. 各モジュールに緑のチェックが付くのを確認
  3. クリックすると各モジュールの実行結果(Input/Output)が確認できる
✓
確認するポイント:

  • Notion「Search Objects」が「投稿待ち」かつ「投稿予定日時 ≤ now」の投稿を取得しているか
  • 投稿本文がBufferに正しく送られたか
  • Notionのステータスが 投稿済み に変わったか
  • Buffer のダッシュボードで投稿が登録されているか(Lv.2なら下書き、Lv.3なら Sent または X 上に投稿済み)

20
Bufferで投稿状態を確認する
Lv.2なら下書き確認→手動投稿、Lv.3なら投稿後の状態確認。

Lv.2(半自動 / Now: No 運用)の場合:

  1. Bufferにログインしてダッシュボードを開く
  2. 下書きフォルダ(Drafts)を確認
  3. Makeから送られた投稿が入っているか確認
  4. 文字化けや改行ミスがないか目視チェック
  5. 問題なければ 「Share Now」 または日時を指定して投稿

Lv.3(自動 / Now: Yes・Post immediately 運用)の場合:

  1. Bufferの Sent タブで投稿が成功しているか確認
  2. Error タブにエラーがないか確認(あれば Retry Now で再試行)
  3. X本体で実際に投稿が公開されているか念のため確認
  4. Notion ステータスが「投稿済み」になっているか確認
★
Notion側のステータスは、Bufferに送信した時点でMakeが自動で 投稿済み に変えてくれます。
本マニュアルの推奨は Lv.2 で1〜2週間運用 → 動作が安定したら Lv.3 に切り替え。シンプルな3段階ステータスで完結します。



Phase 4.5

本番運用に切り替える(Scheduleを起動)

Run onceでの動作確認が完了したら、Scheduleを起動して自動運用に切り替えます。

21
Schedule(実行間隔)を設定する
何分ごとに「投稿待ち」をチェックするかを決める。

  1. Scenarioのキャンバス画面を開く
  2. 最初のNotion Search Objects モジュールの左上の 時計アイコン をクリック
  3. Run scenario:At regular intervals を選択
  4. Minutes:15 程度に設定(15分に1回チェック)
  5. Advanced scheduling を確認(特に注意):
    • Time from / Time to:投稿時刻をカバーする時間帯を指定(例:投稿が6:00なら 06:00 〜 06:30)。何も指定しなければ24時間動く
    • Days:曜日を指定。土日も含めるか必ず確認(デフォルトで月〜金しか入っていないことがある)
    • Months:月を指定。通常は全月選択
  6. 「OK」 で保存
  7. 画面下部の ON/OFF トグル を ON にする
!
Schedule の Days と Time 制限は要確認
過去の設定が残っていて、土日が外れていたり、特定時間帯にしか動かない設定になっていることがあります。本番運用に入る前に必ず Schedule settings の中身を全項目確認してください。
気付かずに ON にすると、土日の投稿が漏れたり、設定時刻外の投稿予定が処理されないトラブルになります。

✓
これで本番運用が始まります。指定時間帯・曜日に15分間隔で動き、Notionをチェックして「投稿待ち かつ 投稿予定日時 ≤ now」の投稿があればBufferへ送ります。



本番運用前のチェックリスト

本番運用を始める前に、3カテゴリ(Notion/Make/Buffer)すべてに目を通してください。

✓Notion側
  • 投稿管理データベースがある
  • 投稿本文の項目がある
  • 投稿予定日時の項目がある(Date型)
  • ステータス項目がある(下書き/投稿待ち/投稿済みの3段階)
  • 投稿先項目がある
  • テスト用の投稿データが1件ある(投稿予定日時は過去〜現在に設定してテスト)
  • テスト投稿のステータスが「投稿待ち」になっている
  • 過去日時の「投稿待ち」が他に残っていないか確認した
✓Make側
  • Makeアカウントにログインできる
  • Notionと接続できている
  • Bufferと接続できている
  • Scenario「Notion to Buffer」を作成している
  • 最初のNotionモジュールが Search Objects(Data Source Items) である
  • Filter条件①「投稿ステータス Select: Equals 投稿待ち」が設定されている
  • Filter条件②「投稿予定日時 Date: On or before {{now}}」が設定されている
  • Limit が 10 になっている
  • Buffer モジュールに NotionのX投稿文がマップされている
  • Notion Update モジュールの Page ID が Search Objects から取れている
  • Notion Update で投稿ステータス=投稿済みに更新する設定になっている
  • Schedule 設定の Days で土日も含む全曜日にチェックが入っている
  • Schedule 設定の Time from / Time to が投稿時刻をカバーしている
  • Run onceで1件テストした(または0件取得=正常を確認した)
  • エラーが出ていない
  • Schedule の ON/OFF トグルが ON になっている
✓Buffer側
  • Bufferにログインできる
  • XアカウントがBufferに接続されている
  • Makeから送られた投稿がBufferに入っている
  • 投稿文が文字化けしていない
  • 投稿日時が意図通りになっている
  • Lv.2運用なら投稿前に人が確認できる状態になっている / Lv.3運用なら投稿後にSent/Errorを確認する運用になっている
  • Queue / Sent / Error タブを定期的に確認する運用になっている



自動化レベルのロードマップ

いきなりレベル4を目指さない。レベル2で動作確認 → 安定したらレベル3に移行するのが現実的なステップ。本マニュアルの構築完了時点はLv.3(自動投稿)を想定。

レベル 内容 向いている段階
Lv.1
手動中心
Notionで投稿管理 → 手動でBufferに貼り付け → Bufferから投稿 Makeに慣れる前/投稿数が少ない
Lv.2
半自動
初期セットアップ
Notion → Make → Buffer下書き登録 → 人が確認して投稿 安全性と効率のバランスを取りたい
Lv.3
自動投稿
★現在の運用
Notion → Make → Buffer (Post immediately) → 即座にX投稿(Notionの投稿予定日時で起動タイミング制御) 運用に慣れている/チェック体制がある
Lv.4
AI生成込み
Notionのネタ → OpenAIで投稿文生成 → Makeで整形 → Buffer → 投稿 後回しでOK。AI生成文を確認せず投稿するリスクあり



外注さんへの作業指示文

そのままコピーして外注さんに共有できます。

Makeを使って、Notionの投稿管理データベースとBufferを連携する初期設定をお願いします。

目的は、Notionで管理しているX投稿案を、Make経由でBufferに送ることです。

XへはMakeから直接投稿しません。
XのAPI制限や仕様変更の影響を避けるため、Bufferを中継して運用します。

最初から完全自動投稿にはせず、まずは以下の状態を目指してください。

Notionでステータスが「投稿待ち」の投稿だけを取得
↓
MakeでBufferへ送信
↓
Notionのステータスを「投稿済み」に変更

初期はBuffer上で人が最終確認してから手動投稿する運用(Lv.2)で動作確認し、安定したら自動投稿(Lv.3)に切り替えます。

作業時は、必ずRun onceで1件だけテストしてください。
いきなり本番自動稼働はしないでください。

Notion側の項目名は勝手に変更しないでください。
変更が必要な場合は、事前に確認してください。



Claudeに引き継ぐ場合のプロンプト

作業を別のチャットや別の人に引き継ぐ時、このプロンプトをClaudeに渡せば前提を一発で共有できます。

あなたは、Make・Notion・Buffer連携に詳しい業務自動化アドバイザーです。

以下の前提で、初心者でも作業できる手順書を作成してください。

【前提】

Makeは初めて使う
投稿管理はNotionで行う
X投稿はMakeから直接投稿しない
X投稿はBufferを経由する
理由は、MakeとXの直連携が不安定、または使えなくなっているため
初期(Lv.2)は完全自動投稿ではなく、Bufferに下書きとして送って人が確認(Now: No)
安定後(Lv.3)は Now: Yes(または Publication: Post immediately) に切り替えて自動投稿
最終確認方法はLv.2ではBuffer下書き、Lv.3ではBufferのSent/Errorタブとなる
Notionのステータスが「投稿待ち」の投稿だけをMakeで取得する
Bufferに送信後、Notionのステータスを「投稿済み」に変更する
Notionのステータスは「下書き/投稿待ち/投稿済み」の3段階のみで運用する

【作成してほしい内容】

全体構成
Notionデータベースの項目設計
Make Scenarioの作成手順
Notion接続手順
Buffer接続手順
テスト実行手順
よくある失敗と対処法
初心者向けの注意点
本番運用前チェックリスト

専門用語はなるべくかみ砕いて説明してください。
ただし、Make上のボタン名やモジュール名は英語表記を残してください。



参照させていただいた動画

本マニュアルの作成にあたり、以下の動画を参考にさせていただきました。素晴らしい解説に感謝します。

▶
ノーコードツールmake × Notionの使い方を初心者向けに解説
チャンネル:管理の山田 | Notion公式アンバサダー

まとめ。

✓
Makeを「全部自動で投稿するツール」ではなく、まずは NotionとBufferをつなぐ中継役 として使います。

Notionで投稿を管理 → Makeで投稿待ちデータだけ取得 → Bufferへ送信 → 自動的にXに投稿 → Notionのステータスを更新。

初期は半自動(Lv.2)で動作確認し、安定したら自動投稿(Lv.3)に移行するのが現実的です。本マニュアルの構築完了形はLv.3(自動投稿)を想定しています。

OpenAI API × Python × Mac

英語の音声を、
きれいに文字起こし。

Macのターミナル初心者向け、OpenAI APIを使った文字起こしマニュアル。コピペでそのまま動きます。

🎧
対象
Macユーザー

⏱
所要時間
約20〜30分

📦
最終ファイル
transcript.txt

🌐
音声言語
English → 日本語

12ステップで、最後まで。

上から順に進めれば、英語音声から自然な日本語訳まで作成できます。各ステップは独立しているので、途中で止めて再開してもOK。

1
ターミナルを開く
作業の起点。Macに最初から入っているアプリです。

  1. キーボードで ⌘ + Space を押す
  2. 検索欄に ターミナル と入力
  3. 表示された「ターミナル」を開く

2
作業用フォルダを作る
ホームフォルダの中に専用の場所を用意します。

下のコマンドを1行ずつターミナルに貼り付けてください。

Terminal

mkdir -p ~/openai-transcribe
cd ~/openai-transcribe

3
APIキーを設定する
OpenAIに認証してもらうための鍵をセット。

ここにあなたのAPIキー の部分を、自分のキーに置き換えてから貼ります。

Terminal

export OPENAI_API_KEY="ここにあなたのAPIキー"

正しく設定されたか確認:

Terminal

echo ${#OPENAI_API_KEY}

✓
数字(キーの長さ)が表示されればOK。0が出たら設定が反映されていません。

4
仮想環境を作る
他のPython設定を汚さないように、独立した環境を準備。

Terminal

python3 -m venv .venv
source .venv/bin/activate

✓
行の先頭に (.venv) が表示されたら成功です。

5
OpenAIライブラリを入れる
PythonからOpenAI APIを呼び出すための部品。

Terminal

python3 -m pip install --upgrade pip
python3 -m pip install openai

6
音声ファイルを入れる
文字起こししたい音声を作業フォルダへ。

今のフォルダをFinderで開きます。

Terminal

open .

開いたフォルダに、音声ファイルを入れてください。ファイル名は必ず output.wav に変更します。

確認:

Terminal

ls

★
output.wav が一覧に表示されたら次へ進めます。

7
長いwavを分割する
APIに送れるサイズに合わせて、自動でカットします。

7-1. 分割用ファイルを作成:

Terminal

nano split_wav.py

開いた画面に下のコードを貼り付けます。

split_wav.py

import wave

INPUT_FILE = "output.wav"
CHUNK_SECONDS = 700

with wave.open(INPUT_FILE, "rb") as wav:
    n_channels = wav.getnchannels()
    sampwidth = wav.getsampwidth()
    framerate = wav.getframerate()
    total_frames = wav.getnframes()

    frames_per_chunk = CHUNK_SECONDS * framerate
    total_chunks = (total_frames + frames_per_chunk - 1) // frames_per_chunk

    for i in range(total_chunks):
        out_name = f"part_{i+1}.wav"
        start_frame = i * frames_per_chunk
        wav.setpos(start_frame)
        frames = wav.readframes(frames_per_chunk)

        with wave.open(out_name, "wb") as out:
            out.setnchannels(n_channels)
            out.setsampwidth(sampwidth)
            out.setframerate(framerate)
            out.writeframes(frames)

        print(f"作成: {out_name}")

7-2. 保存と終了:

  1. Control + O を押す
  2. Enter で確定
  3. Control + X で閉じる

7-3. 実行:

Terminal

python3 split_wav.py
ls

✓
part_1.wav, part_2.wav … が並んでいれば成功です。

8
文字起こし用ファイルを作る
分割したwavを順番にAPIに送信するスクリプト。

Terminal

nano transcribe_parts.py

開いた画面に下を貼り付けます。

transcribe_parts.py

from openai import OpenAI
import glob

client = OpenAI()

wav_files = sorted(glob.glob("part_*.wav"))
all_text = []

for wav_file in wav_files:
    print(f"文字起こし中: {wav_file}")
    with open(wav_file, "rb") as audio_file:
        result = client.audio.transcriptions.create(
            model="gpt-4o-transcribe",
            file=audio_file,
            language="en"
        )
    all_text.append(f"\n--- {wav_file} ---\n")
    all_text.append(result.text)

full_text = "\n".join(all_text)

with open("transcript.txt", "w", encoding="utf-8") as f:
    f.write(full_text)

print("完了: transcript.txt を保存しました")

保存と終了の操作はステップ7と同じです:

  1. Control + O → Enter → Control + X

9
文字起こしを実行する
あとは待つだけ。音声の長さに応じて時間がかかります。

Terminal

python3 transcribe_parts.py

✓
最後に 「完了: transcript.txt を保存しました」 と表示されたら成功!

10
結果を見る
英語の文字起こし結果が出来上がっています。

Terminal

open transcript.txt

11
日本語に翻訳する
ChatGPTに貼って、自然な日本語にしてもらいます。

transcript.txt の中身をすべてコピーして、ChatGPTに貼り付け。下のプロンプトを一緒に送ってください。

Prompt

以下の英語文字起こしを、意味が通る自然な日本語に翻訳してください。
話し言葉の温度感は残しつつ、読みやすく整えてください。

12
次回また使うとき
2回目以降は最初からやり直す必要なし。

ターミナルを開いたら、まず再開コマンドを実行します。

Terminal — 再開コマンド

cd ~/openai-transcribe
source .venv/bin/activate
export OPENAI_API_KEY="ここにあなたのAPIキー"

そのあと、新しい音声で実行:

Terminal

python3 split_wav.py
python3 transcribe_parts.py
open transcript.txt

進み具合を、可視化。

各項目をクリックすると完了マークがつきます。ページを閉じるとリセットされます。

事前準備 3

  • OpenAIのAPIキーを用意した
  • 英語音声ファイルを用意した
  • ファイル名を output.wav にした

ターミナル準備 3

  • ターミナルを開いた
  • ~/openai-transcribe フォルダを作った
  • cd ~/openai-transcribe で移動した

APIキー設定 2

  • export OPENAI_API_KEY=… を実行
  • echo ${#OPENAI_API_KEY} で数字が出た

Python環境 4

  • python3 -m venv .venv を実行
  • source .venv/bin/activate を実行
  • 行頭に (.venv) が出た
  • pip install openai を実行

音声配置 3

  • open . でフォルダを開いた
  • output.wav をフォルダに入れた
  • ls で output.wav が見えた

分割 5

  • nano split_wav.py を開いた
  • 分割コードを貼った
  • 保存した
  • python3 split_wav.py を実行
  • part_1.wav などができた

文字起こし 5

  • nano transcribe_parts.py を開いた
  • 文字起こしコードを貼った
  • 保存した
  • python3 transcribe_parts.py を実行
  • 「完了」メッセージが出た

翻訳 4

  • transcript.txt をコピーした
  • ChatGPTに貼った
  • 翻訳プロンプトを送った
  • 日本語版を保存した

つまずいたら、ここを。

よく出るエラー4つと、その対処法をまとめました。落ち着いて1つずつ確認すればだいたい解決します。

Error 01

externally-managed-environment

原因

仮想環境を作らずに pip install を実行している。

対処
fix

python3 -m venv .venv
source .venv/bin/activate

Error 02

FileNotFoundError

原因

音声ファイル名が違う、または同じフォルダに置かれていない。

対処
fix

ls
# output.wav があるか確認

Error 03

zsh: command not found: from

原因

Pythonコードをターミナルに直接貼り付けてしまっている。

対処

コードは nano ファイル名.py で開いた画面の中に貼り付け、ターミナルでは python3 ファイル名.py だけを実行します。

Error 04

長い音声で失敗する

原因

長いwavファイルをそのままAPIに送っている(サイズ制限超過)。

対処

ステップ7の split_wav.py を必ず先に実行して、分割してから送信します。