API自動化 Python

マガジンの作成・記事追加をPythonで一括処理する

整理の自動化

記事が100本を超えると、手作業の分類は破綻する。ルールを決めれば、あとは機械が振り分ける。

マガジンの作成から記事の一括追加までを実装します

📋 この記事でわかること

  • マガジンAPIのエンドポイント一覧
  • マガジンの作成・更新・削除の実装
  • 記事をマガジンに追加・削除する処理
  • タグやタイトルから自動分類するスクリプト
  • 更新がフルペイロード方式である点への対処
  • 有料マガジンの制約と注意点

記事が増えると、読者は目的の記事にたどり着けなくなります。マガジンは、記事をテーマごとにまとめて回遊を作る機能です。しかし記事が100本を超えると、1本ずつ手作業でマガジンに追加するのは現実的ではありません。APIを使えば、タグやタイトルのルールに従って自動で振り分けられます。この記事では、マガジンの作成から記事の一括追加までを実装します。マガジン機能そのものの使い方はマガジンの記事を参照してください。

使うエンドポイント

  1. GET/POST /api/v1/my/magazines——一覧取得・作成
  2. PUT /api/v1/our/magazines/{key}——更新(フルペイロード)
  3. DELETE /api/v1/my/magazines/{key}——削除
  4. POST /api/v1/our/magazines/{key}/notes——記事の追加
  5. 認証が必要です。Cookie認証を済ませてください

マガジンの種類と制約

実装の前に、マガジンの種類ごとの違いを整理します。ここを理解していないと、想定外の403に遭遇します。

種類 価格 主な制約
無料マガジン 0円 制約が少ない。自由に作れる
有料マガジン 買い切り 無料に戻せない。他人の記事は入れられない
定期購読マガジン 月額 申請と審査が必要。カテゴリ2つの指定が必須
共同運営マガジン 無料 複数人で記事を追加できる

有料マガジンは後戻りできない

一度有料に設定したマガジンを無料に戻すことはできません。また、有料マガジンに追加した記事は、その後に下書きへ戻すことができなくなります。自動化のスクリプトで有料マガジンを扱う際は、特に慎重に——この記事のコードが確認モードを既定にしているのはそのためです。

マガジンを操作するクラス

一覧取得から作成・削除・記事の追加まで、必要な操作をまとめたクラスを作ります。

magazine_client.py
"""
noteのマガジン操作をまとめたクライアント。
更新はフルペイロード方式なので、現在値を取得してからマージする。
"""
import logging

from note_auth_client import NoteAuthClient

logger = logging.getLogger(__name__)

# PUTで送るべきフィールド
MAGAZINE_FIELDS = [
    "name", "description", "status", "price",
    "subscribe", "categories", "is_public",
]


def pick(d: dict, *keys, default=None):
    for k in keys:
        if k in d and d[k] is not None:
            return d[k]
    return default


class MagazineClient:

    def __init__(self, client: NoteAuthClient):
        self.client = client

    # ---- 取得 ----

    def list_magazines(self) -> list:
        """自分のマガジン一覧を取得する"""
        data = self.client.get_json("/api/v1/my/magazines")

        if isinstance(data, list):
            return data

        for key in ("magazines", "contents"):
            value = data.get(key)
            if isinstance(value, list):
                return value

        return []

    def find_by_name(self, name: str) -> dict:
        """名前でマガジンを探す"""
        for m in self.list_magazines():
            if (m.get("name") or "").strip() == name.strip():
                return m
        return {}

    def get_notes(self, magazine_key: str,
                  max_pages: int = 20) -> list:
        """マガジンに入っている記事の一覧"""
        notes = []

        for page in range(1, max_pages + 1):
            data = self.client.get_json(
                f"/api/v2/magazines/{magazine_key}/notes",
                params={"page": page},
            )
            items = data.get("notes", data.get("contents", []))
            if not items:
                break

            notes.extend(items)

            if data.get("isLastPage") or data.get("is_last_page"):
                break

        return notes

    # ---- 作成・更新・削除 ----

    def create(self, name: str, description: str = "",
               price: int = 0, status: str = "public") -> dict:
        """
        マガジンを作成する。
        price=0 なら無料マガジン。
        """
        payload = {
            "name": name,
            "description": description,
            "status": status,
            "price": price,
            "subscribe": False,
        }

        data = self.client.post_json("/api/v1/my/magazines",
                                     payload=payload)

        key = pick(data, "key", "id")
        if not key:
            raise RuntimeError(f"マガジンの作成に失敗しました: {data}")

        logger.info("マガジンを作成しました: %s(key=%s)", name, key)
        return data

    def update(self, magazine_key: str, **changes) -> bool:
        """
        マガジンを更新する。
        フルペイロード方式なので、現在値を取得してマージする。
        """
        current = self.find_by_key(magazine_key)
        if not current:
            logger.error("マガジンが見つかりません: %s", magazine_key)
            return False

        payload = {}
        for field in MAGAZINE_FIELDS:
            if field in current and current[field] is not None:
                payload[field] = current[field]

        payload.setdefault("price", 0)
        payload.setdefault("description", "")
        payload.update(changes)

        res = self.client.request(
            "PUT",
            f"/api/v1/our/magazines/{magazine_key}",
            json=payload,
        )

        if res.status_code not in (200, 201):
            logger.error("更新失敗 HTTP %s: %s",
                         res.status_code, res.text[:200])
            return False

        logger.info("マガジンを更新しました: %s", payload.get("name"))
        return True

    def find_by_key(self, magazine_key: str) -> dict:
        for m in self.list_magazines():
            if str(pick(m, "key", "id")) == str(magazine_key):
                return m
        return {}

    def delete(self, magazine_key: str) -> bool:
        """マガジンを削除する(記事自体は消えない)"""
        res = self.client.request(
            "DELETE", f"/api/v1/my/magazines/{magazine_key}"
        )

        if res.status_code not in (200, 204):
            logger.error("削除失敗 HTTP %s", res.status_code)
            return False

        logger.info("マガジンを削除しました: %s", magazine_key)
        return True

    # ---- 記事の追加・削除 ----

    def add_note(self, magazine_key: str, note_id: int) -> bool:
        """マガジンに記事を追加する(note_idは数値)"""
        res = self.client.request(
            "POST",
            f"/api/v1/our/magazines/{magazine_key}/notes",
            json={"note_id": note_id},
        )

        if res.status_code not in (200, 201):
            logger.warning("追加失敗 HTTP %s(note_id=%s)",
                           res.status_code, note_id)
            return False

        return True

    def remove_note(self, magazine_key: str, note_key: str) -> bool:
        """マガジンから記事を外す(note_keyはn形式)"""
        res = self.client.request(
            "DELETE",
            f"/api/v1/our/magazines/{magazine_key}/notes/{note_key}",
        )

        if res.status_code not in (200, 204):
            logger.warning("削除失敗 HTTP %s(%s)",
                           res.status_code, note_key)
            return False

        return True


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)

    client = NoteAuthClient()
    client.verify()

    mag = MagazineClient(client)

    print("=== マガジン一覧 ===")
    for m in mag.list_magazines():
        name = m.get("name", "")
        count = pick(m, "notesCount", "notes_count", default=0)
        price = m.get("price", 0)
        label = f"{price}円" if price else "無料"
        print(f"  [{label}] {name}({count}本)")

追加はid、削除はkey

記事の追加には数値IDを、削除には記事キー(n形式)を使います。統一されていないため、混同すると404になります。この記事のクラスでは引数名を note_idnote_key に分けて、間違いにくくしています。

記事を自動で分類する

ここからが本題です。記事のタグやタイトルを見て、対応するマガジンに自動で振り分ける処理を作ります。

auto_classify.py
"""
記事をルールに従ってマガジンへ自動分類する。

    python auto_classify.py          # 分類結果の確認のみ
    python auto_classify.py --run    # 実際に追加する
"""
import sys
import time

from note_auth_client import NoteAuthClient, CookieExpiredError
from magazine_client import MagazineClient, pick

URLNAME = "your_note_id"
SLEEP_SEC = 1.5

# 分類ルール: マガジン名 → 判定キーワード
RULES = {
    "Python入門": ["Python", "プログラミング", "コード"],
    "副業と収益化": ["副業", "収益", "稼ぐ", "マネタイズ"],
    "日々の記録": ["日記", "雑記", "振り返り"],
    "読書メモ": ["読書", "書評", "本を読んだ"],
}


def fetch_my_notes(client: NoteAuthClient, urlname: str,
                   max_pages: int = 50) -> list:
    """自分の公開記事を全件取得する"""
    items = []

    for page in range(1, max_pages + 1):
        data = client.get_json(
            f"/api/v2/creators/{urlname}/contents",
            params={"kind": "note", "page": page},
        )
        contents = data.get("contents", [])
        if not contents:
            break

        items.extend(contents)

        if data.get("isLastPage") or data.get("is_last_page"):
            break

        time.sleep(1.2)

    return items


def extract_tags(item: dict) -> list:
    """記事からタグ名の配列を取り出す"""
    raw = item.get("hashtags") or item.get("hashtag_notes") or []
    names = []

    for h in raw:
        if isinstance(h, str):
            names.append(h.lstrip("#"))
        elif isinstance(h, dict):
            inner = h.get("hashtag", h)
            name = inner.get("name")
            if name:
                names.append(name.lstrip("#"))

    return names


def classify(item: dict) -> list:
    """記事がどのマガジンに該当するかを判定する"""
    title = (item.get("name") or "").lower()
    tags = [t.lower() for t in extract_tags(item)]
    haystack = title + " " + " ".join(tags)

    matched = []
    for magazine_name, keywords in RULES.items():
        if any(k.lower() in haystack for k in keywords):
            matched.append(magazine_name)

    return matched


def main(dry_run: bool = True):
    try:
        client = NoteAuthClient()
        client.verify()
    except CookieExpiredError as e:
        print("認証エラー:", e)
        return

    mag = MagazineClient(client)

    print("=== 記事を取得します ===")
    notes = fetch_my_notes(client, URLNAME)
    print(f"  {len(notes)} 本\n")

    # 分類結果を集計する
    plan = {name: [] for name in RULES}
    unclassified = []

    for note in notes:
        matched = classify(note)
        if not matched:
            unclassified.append(note)
            continue
        for name in matched:
            plan[name].append(note)

    print("=== 分類結果 ===")
    for name, items in plan.items():
        print(f"  {name}: {len(items)} 本")
    print(f"  分類なし: {len(unclassified)} 本")

    if dry_run:
        print("\n--- 分類の詳細(各3件まで)---")
        for name, items in plan.items():
            if not items:
                continue
            print(f"\n[{name}]")
            for item in items[:3]:
                print(f"  - {(item.get('name') or '')[:48]}")

        print("\n[確認モード] --run を付けると実際に追加します")
        return

    # 実際にマガジンへ追加する
    for magazine_name, items in plan.items():
        if not items:
            continue

        magazine = mag.find_by_name(magazine_name)

        if not magazine:
            print(f"\nマガジンを作成します: {magazine_name}")
            magazine = mag.create(magazine_name)
            time.sleep(2.0)

        magazine_key = pick(magazine, "key", "id")

        # 既に入っている記事を確認して重複を防ぐ
        existing = {
            n.get("key") for n in mag.get_notes(magazine_key)
        }

        print(f"\n[{magazine_name}] 既存 {len(existing)} 本")

        added = 0
        for item in items:
            if item.get("key") in existing:
                continue

            note_id = item.get("id")
            if not note_id:
                continue

            if mag.add_note(magazine_key, note_id):
                print(f"  追加: {(item.get('name') or '')[:44]}")
                added += 1

            time.sleep(SLEEP_SEC)

        print(f"  → {added} 本を追加しました")

    print("\n完了しました")


if __name__ == "__main__":
    main(dry_run="--run" not in sys.argv)

既存の記事を確認してから追加している点が重要です。重複チェックが無いと、実行のたびに同じ記事を追加しようとして無駄なリクエストが発生します

▶ 記事一覧の取得方法は、別記事で詳しく解説しています。
ページング処理とCSV出力の実装です。

全記事の取得を見る

マガジンの整理と棚卸し

マガジンが増えすぎると、それ自体が分かりにくくなります。状態を確認して整理するスクリプトです。

magazine_report.py
"""
マガジンの状態を報告する。
- 空のマガジン
- 記事が少なすぎるマガジン
- 説明文が未設定のマガジン
"""
import time

from note_auth_client import NoteAuthClient, CookieExpiredError
from magazine_client import MagazineClient, pick

MIN_NOTES = 3


def report(mag: MagazineClient):
    magazines = mag.list_magazines()

    if not magazines:
        print("マガジンがありません")
        return

    print(f"=== マガジン {len(magazines)} 件 ===\n")

    empty = []
    few = []
    no_description = []

    for m in magazines:
        name = m.get("name", "(無題)")
        key = pick(m, "key", "id")
        count = pick(m, "notesCount", "notes_count", default=0)
        price = m.get("price", 0) or 0
        description = (m.get("description") or "").strip()

        label = f"{price:,}円" if price else "無料"
        print(f"  [{label}] {name[:36]}")
        print(f"          記事 {count} 本 / key={key}")

        if count == 0:
            empty.append(name)
        elif count < MIN_NOTES:
            few.append((name, count))

        if not description:
            no_description.append(name)

        time.sleep(0.3)

    print("\n=== 気になるもの ===")
    print(f"  空のマガジン       : {len(empty)} 件")
    print(f"  記事{MIN_NOTES}本未満    : {len(few)} 件")
    print(f"  説明文なし         : {len(no_description)} 件")

    if empty:
        print("\n  空のマガジン:")
        for name in empty:
            print(f"    - {name}")
        print("  → 記事を追加するか、削除を検討してください")

    if no_description:
        print("\n  説明文が未設定:")
        for name in no_description[:8]:
            print(f"    - {name}")
        print("  → 説明文があると、読者が中身を判断しやすくなります")


if __name__ == "__main__":
    try:
        client = NoteAuthClient()
        client.verify()
        report(MagazineClient(client))
    except CookieExpiredError as e:
        print("認証エラー:", e)

マガジンは少なく、中身を濃く

マガジンを10個作って各2本しか入っていない状態より、3個作って各10本入っている状態のほうが読者にとって価値があります。記事が3本未満のマガジンは、統合を検討するという基準を持っておくと整理しやすくなります。

説明文を一括で設定する

マガジンの説明文は、読者が中身を判断する材料になります。未設定のものにまとめて設定する処理です。

set_descriptions.py
"""
マガジンの説明文をまとめて設定する。
更新はフルペイロード方式なので、他の設定を壊さないよう注意する。
"""
import sys
import time

from note_auth_client import NoteAuthClient, CookieExpiredError
from magazine_client import MagazineClient, pick

# マガジン名 → 説明文
DESCRIPTIONS = {
    "Python入門": (
        "プログラミング未経験から始めるPythonの記事をまとめています。"
        "環境構築から実際に動かすところまで、順番に読める構成です。"
    ),
    "副業と収益化": (
        "副業を始める前に知っておきたいことと、実際にやってみた記録。"
        "うまくいかなかった話も含めて書いています。"
    ),
    "読書メモ": (
        "読んだ本の内容と、そこから考えたことの記録です。"
    ),
}


def main(dry_run: bool = True):
    try:
        client = NoteAuthClient()
        client.verify()
    except CookieExpiredError as e:
        print("認証エラー:", e)
        return

    mag = MagazineClient(client)

    for name, description in DESCRIPTIONS.items():
        magazine = mag.find_by_name(name)

        if not magazine:
            print(f"見つかりません: {name}")
            continue

        current = (magazine.get("description") or "").strip()
        key = pick(magazine, "key", "id")

        print(f"\n[{name}]")
        print(f"  現在: {current[:50] or '(未設定)'}")
        print(f"  変更: {description[:50]}")

        if current == description:
            print("  → 変更なし")
            continue

        if dry_run:
            continue

        if mag.update(key, description=description):
            print("  → 更新しました")

        time.sleep(2.0)

    if dry_run:
        print("\n[確認モード] --run を付けると実際に更新します")


if __name__ == "__main__":
    main(dry_run="--run" not in sys.argv)

▼ RECOMMENDATION ▼

整理して読みやすくしても、来る人が増えなければ意味は薄い

マガジンで回遊を作れば、1人あたりの読む記事数は増えます。ただし、そもそも訪問者が少なければ効果は限定的です。アメブロであれば、アメプレスProがいいね・フォロー・アクセス獲得を自動で回し、訪問者そのものを増やせます。noteと違ってASPアフィリエイトも使えるため、回遊した読者を収益につなげる設計もできます。月額2,980円、365日LINEサポート付きです。

✦ 自動いいね・フォロー ✦ ASPアフィリOK ✦ 複数ブログ対応 ✦ 365日LINEサポート
アメプレスPro 公式ページを確認する →

※ 当サイトはアフィリエイト広告を含みます。

連載を順番通りに並べる

連載記事をマガジンにまとめる場合、追加する順序が表示順に影響します。古い記事から順に追加する処理です。

build_series.py
"""
タイトルに連番を含む記事を、順番通りにマガジンへ追加する。

    「第1回」「その2」「(3)」などの表記に対応する。
"""
import re
import sys
import time

from note_auth_client import NoteAuthClient, CookieExpiredError
from magazine_client import MagazineClient, pick
from auto_classify import fetch_my_notes

URLNAME = "your_note_id"
MAGAZINE_NAME = "連載シリーズ"
SERIES_KEYWORD = "連載"      # タイトルに含まれるキーワード

NUMBER_PATTERNS = [
    r"第(\d+)回",
    r"その(\d+)",
    r"\((\d+)\)",
    r"((\d+))",
    r"#(\d+)",
    r"vol\.?\s*(\d+)",
]


def extract_number(title: str) -> int:
    """タイトルから連番を取り出す。見つからなければ大きな値を返す"""
    for pattern in NUMBER_PATTERNS:
        m = re.search(pattern, title, re.IGNORECASE)
        if m:
            return int(m.group(1))
    return 9999


def main(dry_run: bool = True):
    try:
        client = NoteAuthClient()
        client.verify()
    except CookieExpiredError as e:
        print("認証エラー:", e)
        return

    notes = fetch_my_notes(client, URLNAME)

    series = [
        n for n in notes
        if SERIES_KEYWORD in (n.get("name") or "")
    ]

    if not series:
        print(f"「{SERIES_KEYWORD}」を含む記事が見つかりません")
        return

    # 連番の順に並べる
    series.sort(key=lambda n: extract_number(n.get("name") or ""))

    print(f"=== 対象 {len(series)} 本(追加する順序)===")
    for n in series:
        num = extract_number(n.get("name") or "")
        label = f"#{num}" if num < 9999 else "(番号なし)"
        print(f"  {label:>8}  {(n.get('name') or '')[:44]}")

    if dry_run:
        print("\n[確認モード] --run を付けると実際に追加します")
        return

    mag = MagazineClient(client)
    magazine = mag.find_by_name(MAGAZINE_NAME)

    if not magazine:
        print(f"\nマガジンを作成します: {MAGAZINE_NAME}")
        magazine = mag.create(MAGAZINE_NAME)
        time.sleep(2.0)

    magazine_key = pick(magazine, "key", "id")
    existing = {n.get("key") for n in mag.get_notes(magazine_key)}

    added = 0
    for n in series:
        if n.get("key") in existing:
            continue

        if mag.add_note(magazine_key, n.get("id")):
            print(f"  追加: {(n.get('name') or '')[:44]}")
            added += 1

        time.sleep(1.5)

    print(f"\n{added} 本を追加しました")


if __name__ == "__main__":
    main(dry_run="--run" not in sys.argv)

つまずきやすい点

症状 原因 対処
記事の追加で404 keyを渡している 追加には数値idを使う
記事の削除で404 idを渡している 削除にはn形式のkeyを使う
更新で設定が消えた フルペイロードを送っていない 現在値を取得してマージする
有料マガジンで403 操作が許可されない条件 制約を確認する
定期購読の作成に失敗 カテゴリ未指定 categoriesに2つ指定する
同じ記事が何度も追加される 重複チェックがない 既存記事を先に取得する
他人の記事を追加できない 有料マガジンの制約 無料マガジンを使う

マガジンの中身を一覧に書き出す

どのマガジンに何が入っているかを俯瞰したいとき、Markdownで書き出しておくと便利です。記事の重複や偏りも見つけやすくなります。

export_magazines.py
"""
全マガジンの中身をMarkdownで書き出す。
重複して入っている記事も検出する。
"""
import time
from collections import Counter
from datetime import datetime

from note_auth_client import NoteAuthClient, CookieExpiredError
from magazine_client import MagazineClient, pick

OUTPUT = "magazines.md"
SLEEP_SEC = 1.2


def collect(mag: MagazineClient) -> list:
    """マガジンごとに記事一覧を集める"""
    result = []

    for m in mag.list_magazines():
        key = pick(m, "key", "id")
        name = m.get("name", "(無題)")
        price = m.get("price", 0) or 0

        print(f"取得中: {name[:36]}")
        notes = mag.get_notes(key)

        result.append({
            "name": name,
            "key": key,
            "price": price,
            "description": (m.get("description") or "").strip(),
            "notes": notes,
        })

        time.sleep(SLEEP_SEC)

    return result


def find_duplicates(magazines: list) -> list:
    """複数のマガジンに入っている記事を洗い出す"""
    counter = Counter()
    titles = {}

    for mg in magazines:
        for n in mg["notes"]:
            key = n.get("key")
            if not key:
                continue
            counter[key] += 1
            titles[key] = n.get("name", "")

    return [
        (titles[key], count)
        for key, count in counter.most_common()
        if count >= 2
    ]


def build_markdown(magazines: list) -> str:
    lines = [
        "# マガジン一覧",
        "",
        f"作成日時: {datetime.now().strftime('%Y-%m-%d %H:%M')}",
        f"マガジン数: {len(magazines)}",
        "",
    ]

    total_notes = sum(len(mg["notes"]) for mg in magazines)
    lines.append(f"収録記事の合計: {total_notes} 本(重複含む)")
    lines += ["", "---", ""]

    for mg in magazines:
        label = f"{mg['price']:,}円" if mg["price"] else "無料"

        lines.append(f"## {mg['name']}({label})")
        lines.append("")

        if mg["description"]:
            lines.append(f"> {mg['description']}")
            lines.append("")

        lines.append(f"記事数: {len(mg['notes'])} 本")
        lines.append("")

        if not mg["notes"]:
            lines.append("(記事が入っていません)")
            lines.append("")
            continue

        for n in mg["notes"]:
            title = n.get("name", "(無題)")
            key = n.get("key", "")
            user = (n.get("user") or {}).get("urlname", "")
            url = f"https://note.com/{user}/n/{key}" if user else ""

            if url:
                lines.append(f"- [{title}]({url})")
            else:
                lines.append(f"- {title}")

        lines.append("")

    duplicates = find_duplicates(magazines)

    if duplicates:
        lines += ["---", "", "## 複数のマガジンに入っている記事", ""]
        for title, count in duplicates[:30]:
            lines.append(f"- {title}({count}個のマガジン)")
        lines += ["", "※ 3個以上に入っている場合は整理を検討してください", ""]

    return "\n".join(lines)


def main():
    try:
        client = NoteAuthClient()
        client.verify()
    except CookieExpiredError as e:
        print("認証エラー:", e)
        return

    mag = MagazineClient(client)
    magazines = collect(mag)

    if not magazines:
        print("マガジンがありません")
        return

    markdown = build_markdown(magazines)

    with open(OUTPUT, "w", encoding="utf-8") as f:
        f.write(markdown)

    print(f"\n{OUTPUT} に出力しました")

    duplicates = find_duplicates(magazines)
    if duplicates:
        print(f"\n複数マガジンに入っている記事: {len(duplicates)} 本")
        for title, count in duplicates[:10]:
            print(f"  {count}個  {title[:44]}")


if __name__ == "__main__":
    main()

3個以上のマガジンに入っている記事が多い場合、マガジンの切り分けが曖昧になっているサインです。読者から見ても、どのマガジンを見ればいいのか分からない状態になっている可能性があります。

まとめ:ルールを決めれば分類は自動化できる

マガジンの管理は、記事数が増えるほど手作業では回らなくなります。タグやタイトルにルールを持たせておけば、分類は機械的に処理できます。

この記事の要点

  1. マガジン一覧は GET /api/v1/my/magazines
  2. 更新はフルペイロード方式。現在値をマージしてから送る
  3. 記事の追加は数値id、削除はn形式のkey
  4. 追加前に既存の記事を確認して重複を防ぐ
  5. 有料マガジンは無料に戻せない。慎重に扱う
  6. 定期購読マガジンはカテゴリを2つ指定する必要がある
  7. 記事3本未満のマガジンは統合を検討する
  8. 連載は連番順に追加すると読者が追いやすい

まずは確認モードで分類結果を見て、ルールが妥当かを判断してください。分類のルールさえ固まれば、以降は新しい記事を書くたびに実行するだけで整理が保たれます。

FAQ|マガジンAPIについてよくある質問

Q. マガジンをいくつまで作れますか?

無料会員とプレミアム会員で上限が異なり、プレミアムのほうが多く作れます。ただし実運用では、上限に達する前に「多すぎて分かりにくい」という問題が起きます。読者の視点では、マガジンは3〜7個程度が把握しやすい数です。上限を気にするより、中身の濃さを優先してください。

Q. マガジンを削除すると記事も消えますか?

消えません。マガジンは記事をまとめる箱にすぎず、削除しても記事本体は残ります。整理のためにマガジンを統合する場合も、記事が失われる心配はありません。ただし、有料マガジンの場合は購読者との関係があるため、削除の判断は慎重に行ってください。

Q. 1つの記事を複数のマガジンに入れられますか?

入れられます。この記事の分類スクリプトも、複数のルールに該当する記事は複数のマガジンに追加する設計になっています。ただし、あまりに多くのマガジンに同じ記事が入っていると、マガジンごとの特色が薄れます。1記事あたり1〜2個のマガジンに留めるのが自然です。

Q. マガジン内の記事の並び順を変えられますか?

note側の画面から並べ替えができます。APIでの並べ替えは確認されていないため、追加する順序で調整するのが現実的です。この記事の連載向けスクリプトが、連番順にソートしてから追加しているのはこの理由です。順序が重要な連載では、追加前に並び順を確定させてください。

Q. 有料マガジンをAPIで作成できますか?

作成時に price を指定すれば技術的には可能です。ただし、有料マガジンは無料に戻せず、追加した記事も下書きに戻せなくなります。自動化のスクリプトで有料マガジンを作るのは、事故のリスクが高すぎます。有料マガジンは画面から手作業で作成し、記事の追加だけAPIで行うという分担をおすすめします。

Q. 定期購読マガジンの作成でエラーになります

定期購読マガジンには、カテゴリを2つ指定する必要があります。また申請と審査が必要な機能でもあるため、APIから作成するのは現実的ではありません。定期購読マガジンについては、noteの画面から申請してください。仕組みそのものは定期購読とメンバーシップの記事で解説しています。

Q. 他人の記事をマガジンに入れられますか?

無料マガジンであれば可能です。有料マガジンには他人の記事を入れられません。これは、他人の記事で収益を得ることを防ぐための制約です。共同運営マガジンという機能を使えば、複数人で記事を持ち寄る運用もできます。用途に応じて種類を選んでください。

Q. 分類ルールをもっと賢くできますか?

この記事の実装はキーワードの単純な部分一致です。より高度にするなら、本文の内容を見る、複数キーワードの重み付けをする、といった改良が考えられます。ただし、複雑にするほど誤分類の原因を追いにくくなります。まずは単純なルールで動かし、誤分類が目立つようになってから調整するのが現実的です。

Q. マガジンから記事を外すとどうなりますか?

マガジンの一覧から消えるだけで、記事自体は公開されたままです。ただし、有料マガジンに追加した記事を外しても、購読者が既に閲覧できる状態になっている場合があります。有料マガジンからの除外は、購読者との関係に影響するため慎重に判断してください。

Q. マガジンを作る意味はどれくらいありますか?

読者の回遊を作るという点で効果があります。1本の記事を読んで気に入った読者が、次に何を読めばいいか分かる状態にできるからです。特に連載やシリーズものでは効果が大きくなります。一方、記事数が20本未満の段階では、マガジンを作るより記事を増やすほうが優先度は高いでしょう。

アメプレスラボ編集部

AMEPRESS LAB EDITORIAL TEAM

アメブロ・note・WordPressを実際に運営し、それぞれの収益構造を数字で検証しているチーム。プラットフォームごとの得意・不得意を踏まえた、宣伝色に偏らない実践情報の発信を続けています。