Claude Media
Claudeで書籍の索引を作成する手順 — ゲラPDFからページ参照つきの案を出す

Claudeで書籍の索引を作成する手順 — ゲラPDFからページ参照つきの案を出す

校了前のゲラPDFをClaude APIに章ごとに渡し、索引語の候補と該当ページを出させ、見出し語の粒度をそろえて索引案にするまでの手順を、PDFの上限とページ番号の扱いに沿って示します。

Claudeに任せる範囲と、編集者が決める範囲

書籍の索引(さくいん)づくりは、ゲラPDFをClaude APIに渡し、章ごとに索引語の候補とページを出させ、最後に見出し語の粒度をそろえる流れで進められます。Claudeが出すのは候補と根拠の一覧です。どの語を拾うか、読みをどうするか、副項目をどこまで立てるかは、編集者と著者が決めます。

この役割分担を最初に固めないと、候補が数百語出てきたところで手が止まります。拾いすぎた語を削る作業は、一から拾う作業より重くなりがちです。

役割分担

工程ごとの担当

  • Claude

    章ごとに索引語の候補、該当するPDFのページ、本文からの短い抜粋を出します。章をまたいだ表記ゆれの洗い出しも担当します。

  • スクリプト

    PDFのページ番号を、本に印字されたページ番号(ノンブル)に換算します。抜粋が本当にそのページにあるかも機械的に確かめます。

  • 編集者・著者

    方針メモを作り、最終の見出し語と読み、副項目の立て方を決めます。

使うのは、PDFを document ブロックで渡せるMessages APIです。章ごとの繰り返しと換算を回すので、スクリプトから呼ぶ前提で書きます。

最初に作る索引の方針メモ

見出し語の粒度は、PDFを渡す前に文章で決めておきます。方針メモがないと、章ごとに拾い方が変わります。

次の項目を1ページにまとめ、すべての章で同じものをプロンプトに入れます。内容は編集上の取り決めの例で、書名やシリーズの方針に合わせて差し替えてください。

決めること方針メモの書き方の例
索引の種類方針メモの書き方の例事項索引と人名索引を分ける。書名・作品名は別立てにする
拾う基準方針メモの書き方の例定義や説明がある箇所を拾う。語が通りすがりに出るだけの箇所は拾わない
見出し語の形方針メモの書き方の例単数・基本形に統一し、略称は正式名称へ寄せる。略称からは「→正式名称」の見よ項目を作る
読み方針メモの書き方の例ひらがなで付ける。人名・外来語の読みは「要確認」の印を付けさせる
副項目方針メモの書き方の例同じ語で参照ページが5か所を超えたら、主題別に副項目へ分ける
ページの表し方方針メモの書き方の例連続するページは範囲で書く(例: 45-47)

「拾う基準」を一文でも書いておくと、効果がはっきり出ます。基準がなければ、本文に出てくる名詞がほぼ全部候補に上がります。

ゲラPDFをClaudeに渡す前の確認

PDFの扱いには上限があります。書籍1冊分は、そのままでは入らないことが多いので、先に数字を確かめます。

数字

PDFを渡すときの上限

  • リクエストの最大サイズ

    32MB

    PDF以外の内容も含む。プラットフォームで異なる

  • 1リクエストの最大ページ数

    600ページ

    コンテキストウィンドウが1M未満なら100ページ

  • 1ページあたりのテキストトークン

    1,500〜3,000

    内容の密度による

Claude APIの公式ドキュメントにある値

形式は、パスワードや暗号化のない標準的なPDFです。パスワード付きのゲラは、先に解除しておく必要があります。

ページ数の上限とサイズの上限は、どちらもリクエスト全体にかかります。ゲラのように図版が多いPDFは、ページ数の上限に届く前に失敗することがあります。公式は、その場合に文書を分割すること、画像を縮小することを勧めています。サイズが大きいときは、Files APIでアップロードして file_id で参照すると、リクエスト本体を小さく保てます。

つまり、数百ページの本は章単位に分けて渡すことになります。章ごとにPDFを切り出す作業は、ゲラの組版データからPDFを書き出し直すか、PDF分割ツールで行います。

PDFのページ番号とノンブルは別物

ここが索引づくりでいちばんの落とし穴です。

公式は、プロンプトでページを指すときは、PDFビューアに表示される論理ページ番号(先頭を1とするページ番号)を使うよう勧めています。ところが書籍の索引に載せるのは、紙面に印字されたノンブルです。前付がローマ数字で、本文の1ページ目がPDFの9ページ目、という本は珍しくありません。

前と後

ページ番号の扱いを分ける

BEFORE

混ぜた指示

「索引語と、本に印字されたページ番号を出してください」と頼む。Claudeがページ画像の隅の数字を読むのか、PDF上の通し番号を数えるのかが定まらず、章によって基準がずれる。

AFTER

分けた指示

ClaudeにはPDFビューアでの通し番号だけを出させる。ノンブルへの換算は、章ごとの起点を表にして、スクリプトで行う。

章ごとに切り出したPDFなら、各PDFの1ページ目が紙面の何ページ目に当たるかを表にします。たとえば第2章のPDFの1ページ目が紙面の23ページなら、PDFの通し番号に22を足せばノンブルです。前付がローマ数字のPDFは、区間ごとに別の表を持ちます。

章ごとに索引語の候補を出させる

方針メモを決め、章ごとのPDFを用意したら、1章ずつ呼び出します。次のスクリプトは、公式ドキュメントのbase64方式の document ブロックに沿った例です。モデル名は公式のサンプルと同じものを置いているので、使うモデルに合わせて変えてください。

import anthropic, base64, json, pathlib
 
client = anthropic.Anthropic()
POLICY = pathlib.Path("index_policy.md").read_text(encoding="utf-8")
 
PROMPT = f"""添付は書籍の1章分のゲラPDFです。
次の方針メモに従い、索引語の候補を拾ってください。
 
{POLICY}
 
出力はJSON配列のみ。要素は次の形にします。
{{"term": 見出し語, "yomi": 読み, "kind": "事項" か "人名" か "書名",
 "pdf_pages": [PDFビューアでの通し番号],
 "quote": 該当箇所の本文からの抜粋(30字以内、原文のまま),
 "note": 迷った点があれば一言(なければ空)}}
ページはPDFビューアの通し番号で答えること。"""
 
def extract(pdf_path: str) -> list[dict]:
    data = base64.standard_b64encode(
        pathlib.Path(pdf_path).read_bytes()
    ).decode("utf-8")
    msg = client.messages.create(
        model="claude-opus-5-5",
        max_tokens=8000,
        messages=[{
            "role": "user",
            "content": [
                {"type": "document", "source": {
                    "type": "base64",
                    "media_type": "application/pdf",
                    "data": data}},
                {"type": "text", "text": PROMPT},
            ],
        }],
    )
    return json.loads(msg.content[0].text)

PDFを先に置き、指示のテキストを後ろに置いている点は、公式の推奨(PDFをテキストより前に置く)に合わせたものです。

章を回すループは単純です。

chapters = {
    "ch01": ("ch01.pdf", 1),    # (ファイル, 1ページ目のノンブル)
    "ch02": ("ch02.pdf", 23),
}
rows = []
for ch, (path, first) in chapters.items():
    for r in extract(path):
        r["chapter"] = ch
        r["nombre"] = [p + first - 1 for p in r["pdf_pages"]]
        rows.append(r)

章ごとに切り出したPDFでは、ページ番号が各PDFの中で1から振り直されます。そのため first に、章の1ページ目のノンブルを入れて足し込みます。数字はここでは仮の値です。自分の本の構成で表を作り直してください。

費用の見積もり

テキストの分だけで、1ページあたり1,500〜3,000トークンがかかります。各ページは画像としても処理されるので、画像の分が上乗せされます。PDFの追加料金はなく、通常のAPI料金が適用されます。

300ページの本なら、テキストだけで45万〜90万トークンの入力です。ここに画像の分が加わります。確実な見積もりは、トークンカウントのAPIに実際のPDFを通して取ります。同じ章を方針メモの調整で何度も回すなら、プロンプトキャッシュの利用も検討します。

抜粋の検算でページのずれを拾う

Claudeが返したページは、そのまま信用しないほうが安全です。quote の抜粋が、そのPDFページの本文に本当にあるかを、手元のPDFから照合します。

from pypdf import PdfReader
 
def check(pdf_path: str, rows: list[dict]) -> list[dict]:
    reader = PdfReader(pdf_path)
    pages = [p.extract_text() or "" for p in reader.pages]
    bad = []
    for r in rows:
        q = r["quote"].replace(" ", "").replace("\n", "")
        hit = any(
            q in pages[p - 1].replace(" ", "").replace("\n", "")
            for p in r["pdf_pages"]
        )
        if not hit:
            bad.append(r)
    return bad

照合に通らなかった行は、ページがずれたか、抜粋が言い換えられたかのどちらかです。この一覧を編集者が目で見て、必要なら該当ページを再依頼します。抜粋が通った行でも、その語が拾う基準に当たるかどうかまでは機械では分かりません。ここは人の判断です。

ゲラのPDFに文字情報がなく、紙面をスキャンしたものだった場合は、extract_text() が空になります。その場合はこの検算が使えません。後述のCitationsも、スキャンPDFは引用の対象外です。

章をまたいで見出し語の粒度をそろえる

章ごとの候補が出そろったら、全章の行を1つにまとめます。ここからが「粒度をそろえる」作業です。

最初にスクリプトで、同じ term の行を機械的に束ねます。次に、表記ゆれの候補をClaudeに洗い出させます。この段階はPDFを渡す必要がなく、候補の一覧だけをテキストで渡せば足ります。

terms = sorted({r["term"] for r in rows})
prompt = f"""次は書籍の索引語の候補です。
{chr(10).join(terms)}
 
方針メモ:
{POLICY}
 
1. 同じ概念の表記ゆれ(略称・旧称・送り仮名違い)を組にしてください。
2. 組ごとに、採用すべき見出し語を1つ提案し、理由を一言添えてください。
3. 他の語の説明の中にしか出ない語は、副項目にする案を出してください。
JSON配列のみで答えること。"""

返ってきた組は、そのまま適用せず、編集者が採否を決めて対応表にします。対応表を決めたら、スクリプトで全行の term を書き換え、同じ見出し語のページを昇順に並べて、連続する数字を範囲にまとめます。

手順

索引案を仕上げる流れ

  1. 1

    章ごとに候補を出す

    方針メモを全章で共通にして、PDFを1章ずつ渡します。

  2. 2

    ノンブルに換算し、抜粋を検算する

    通し番号から換算し、照合に通らない行を洗い出します。

  3. 3

    表記ゆれの対応表を決める

    候補をまとめてClaudeに渡し、組と採用語の案を受け取ります。編集者が決めて確定します。

  4. 4

    五十音順に並べて版面へ回す

    読みで並べ、「要確認」の読みを著者に確かめて、組版担当へ渡します。

五十音順の並べ替えを、Claudeの出した読みに頼るかどうかは、本の性格で分けます。人名や専門用語の読みは間違いやすいので、「要確認」の印を付けさせ、著者に確認します。

Citationsを使うと何が変わるか

Claude APIにはCitationsという機能があります。document ブロックに citations: {"enabled": true} を指定すると、回答に根拠の位置が付きます。PDFの場合は、ページ番号の範囲が返ります。詳しい形式の違いは、ClaudeのCitationsは引用形式がPDF・テキスト・カスタムで変わるにまとめています。

索引づくりで関係するのは、次の3点です。

  • ページ番号は1始まりで、終了ページは含まない(排他的)。start_page_number: 5、end_page_number: 6 は5ページだけを指す
  • 引用の単位は文。PDF全体が文に分割され、その文を単位にページが返る
  • 構造化出力(output_config.format)とは併用できない。両方を指定すると400エラーになる

3つ目が実務では効きます。JSONの形を厳密に固定する構造化出力と、ページの根拠が付くCitationsは、同じリクエストで使えません。この記事のスクリプトが、プロンプトでJSONを頼み、あとから検算する形にしているのはそのためです。

くらべる

根拠の取り方を選ぶ

この記事の方法

プロンプトでJSONを頼み、検算する

出力の形を自分で決められる。ページが合っているかは、抜粋の照合で確かめる。

別の方法

Citationsで根拠を付ける

ページ番号が、APIの返す位置情報として付く。ただし出力は構造化出力で固定できず、文ごとの引用を後から索引の行に組み直す作業が増える。

どちらを選んでも、換算するのは「PDFの通し番号」です。ノンブルへの変換は変わりません。

スキャンしただけのPDFは、Citationsの対象外です。公式は、テキストを抽出できないスキャンPDFは引用できないと説明しています。経緯と回避の方法は、ClaudeのCitationsでスキャンPDFを引用できない理由と回避策で扱っています。

つまずきやすい箇所

落とし穴

100ページを超えたPDFが通らない

コンテキストウィンドウが1M未満のリクエストでは、1リクエストのページ上限は100ページです。章が100ページを超える本は、節の区切りで分けます。エラーの読み方は「Could not process PDF」400エラーの原因と対処法で確認できます。

もう1つ、章の切れ目をまたいで説明される概念も取りこぼしやすくなります。章ごとに別々のリクエストにするので、前の章の定義を知らないまま次の章を読むためです。章末と章頭の記述が連続する本では、前の章の末尾数ページを重ねて渡す手があります。重なった分の候補は、後で同じ語・同じページの行として束ねます。

読み取りの質はPDFの状態にも左右されます。公式は、標準的なフォントを使うこと、文字がはっきり読めること、ページが正しい向きで上を向いていることを条件に挙げています。ゲラの書き出し設定で、フォントを埋め込み、ページを回転させないように整えておくと安全です。

未刊行の原稿を送る前に

校了前のゲラは、未刊行の原稿です。APIに送る前に、出版社との契約、著者との取り決め、社内の情報管理ルールで外部サービスへの送信が認められているかを確かめてください。許可の範囲は組織ごとに違うので、この記事では判断しません。

まとめ

Claudeに頼めるのは、章ごとの候補と根拠の抜粋までです。見出し語の粒度は、方針メモという人の取り決めで決まります。効いてくるのは次の2点です。

ページは、PDFの通し番号で受け取り、ノンブルへの換算はスクリプトに任せること。抜粋の照合で、ずれた行を先に拾うこと。

この2つを回せば、編集者が見るのは「照合に通らなかった行」と「表記ゆれの対応表」に絞れます。本の構成を先に固める段階の話は、ClaudeでKindle出版の構成・目次を作る手順にあります。取材記事のゲラを事実の面から照合するなら、Claudeで取材記事のゲラを事実照合するが続きの作業です。

この記事を共有:XはてブLinkedIn