← ブログに戻る

Kindleが売れない原因を65冊で測り3回外した

Kindleで65冊出しているのですが、直近38日で1冊でも売れたのは19冊しかなく、そのうち上位5冊が売上の72%を占めていました。首位の1冊だけで39%です。

この差はどこから来ているのか。ひと晩かけて3つの仮説を立てて、3つとも実測で潰しました。当たった仮説はゼロです。自分で立てて自分で潰したので、誰も責められません。ただ、外れ方が3回とも同じでした。その共通点のほうが、個々の仮説より役に立ちました。

私が原因だと思って測った指標は、どれも売上の結果でした。

何を比べたのか

売上首位は『ナレッジグラフ活用大全』です。2026年8月1日から9月8日までの実績で18冊。同じ期間のカタログ全体が46冊なので、この1冊で39%を持っています。まず、これがまぐれかどうかを見ました。

期間注文1日あたり全体に占める割合
4/21〜7/18200.2223.5%
7/1〜7/31150.4830.6%
8/1〜8/1480.5753.3%
8/23〜8/3050.71
8/30〜9/830.3339.1%

5か月近く途切れていません。新刊直後の初速でも、どこかで紹介された単発の山でもなく、誰かが継続的に見つけている形で、しかもカタログが増えていく中でこの1冊の割合のほうが上がっています。ベースラインです。

では、この1冊だけを支えているものは何か。

仮説1: 棚が空いているから売れている

最初に疑ったのは競合の不在。Amazonのサジェストで「ナレッジグラフ」を種にして71通り総当たりしたところ、返ってきたクエリは3語だけでした。

ナレッジグラフ
ナレッジグラフ活用大全                      ← 自著
ナレッジグラフ活用大全 構造化すれば aiは賢くなる   ← 自著

「ナレッジグラフ 本」も「ナレッジグラフ 入門」も空でした。本を探すときの一般的なクエリがひとつも存在しないのに、この語を打った人に出る候補のほうは、ほぼ自分の本で埋まっていたわけです。英語版と比べると、もっとはっきりします。中身は同じ本です。

日本市場米国市場
クエリの深さ3語(うち2つが自著)9語
「〜 本」型全部空graphrag book graphrag in action あり
サジェスト上の競合書ゼロManning系ほか複数
実売(8/1〜9/8)18冊0冊

需要が3倍あって競合がいる市場で0冊。需要がほぼ無くて競合がいない市場で18冊。中身は同じです。ここまでは仮説どおりでした。そして既刊を並べた瞬間に崩れます。

中核語競合書語がタイトル先頭売上
エンジニアの心理トリック大全心理トリックなしあり0
テクニカルサポートの教科書テクニカルサポートなしあり0
AIエンジニアの無在庫スモビジ設計図無在庫なしあり0
ビデオ通話アプリの自動テストPlaywrightなしサブのみ0
AI時代のD2C販売導線D2Cなしサブ寄り0

空いた棚に立っていて売れていない本が、6冊以上ありました。うち2冊はその語をタイトルの先頭に置いています。英語版との対照が示していたのは「競合がいると売れない」だけで、私はそれを「いなければ売れる」と読んでいました。必要条件と十分条件の取り違えです。

仮説2: カテゴリが誤配置になっている

次に疑ったのはカテゴリでした。KDPのカテゴリ選択は「それらしいものを3つ」で済ませがちで、私もずっと適当に埋めていたので、ここが壊れているなら話が早いと思ったわけです。雑にやったところが原因であってほしい、という願望も混ざっていました。商品ページから既刊の配置を読むツールを書いて、日本語16冊を一括で監査しました。

冊数
カテゴリ順位が1つも出ない5
配置に問題の疑い0
問題なし11

順位が出ない5冊が見つかったので、これが原因かと思いました。ところがパンくずを開くと、5冊すべてでカテゴリは正しく設定されていました。過去の自分は、ここだけは真面目にやっていたようです。

設定カテゴリ
LLMO実践ガイドBusiness & Money › Marketing & Sales › Marketing › Direct
エンジニアリング100の言葉Computers & Technology › Computer Science
エンジニアの心理トリック大全Computers & Technology › Programming
なぜAI生成UIは全部青いのかArts & Photography › Design
★ナレッジグラフ活用大全(18冊)Computers & Technology › Applications

心理トリック大全は、首位の本が70位を取っているのと同じProgramming系に入っていました。誤配置ではありません。順位が付かないのは売上が無いからです。 カテゴリ順位を原因として扱った時点で、順序が逆でした。

仮説3: 順位を見れば実力が分かる

3つ目は、そもそも指標が何を数えているかを確認していなかった、という話です。

Kindleストア総合順位実売
ナレッジグラフ活用大全#25,12518冊
AIくさい文章から脱出する技術#25,3120冊

ほぼ同じ順位で、売上が正反対でした。後者はKindle Unlimitedで352ページ読まれていて、順位を作っていたのはKU読者です。Kindleストアの順位は貸出を含みます。売上の代理に順位を使うと、この2冊は同じ実力に見えます。

3回とも、同じ壊れ方でした

並べると分かります。

使った指標実際は
サジェストの占有売れている本が候補に載る
カテゴリ順位売れないと順位が付かない
Kindleストア順位KU貸出を含む

3つとも売上の下流にありました。原因を探しているつもりで結果のほうを測っていたので、どれも最後は「売れている本が良い数字を持っている」という同語反復に着地します。

上流と下流で分けていませんでした。

上流(自分が設定する)下流(売上の結果)
書名・サブタイトル / カテゴリ選択 / 価格 / 表紙 / 説明文 / キーワード7枠 / 本の中身ランキング / カテゴリ順位 / サジェスト占有 / レビュー数

打ち手になり得るのは左側だけです。右側は答え合わせにしか使えません。

残っているもの

上流のうち、首位の本と他の本の差を説明できたものは、いまのところひとつもありません。比較した2冊はページ数が197対194、発売が5月1日と8月1日、カテゴリはどちらもComputers & Technology系。条件がほぼ揃っていて、18冊と0冊に割れています。未検証の上流は、表紙、説明文、キーワード7枠、価格。次はそこを測ります。ただし今度は測る前に、その指標が上流にあるのかどうかを先に決めておくつもりです。

取得スクリプト

カテゴリと順位は商品ページから読めます。私が回しているのは常駐のヘッドレスブラウザ経由ですが、取り出している場所は同じなので、Playwright に置き換えた形を置いておきます。

# pip install playwright && playwright install chromium
import re, sys
from playwright.sync_api import sync_playwright

DETAIL = "#detailBullets_feature_div"          # 発売日・ページ数・言語・順位
CRUMB  = "#wayfinding-breadcrumbs_feature_div"  # 設定されているカテゴリ

def grab(d, pattern):
    m = re.search(pattern, d)
    return m.group(1) if m else None

def book(page, asin):
    page.goto(f"https://www.amazon.co.jp/dp/{asin}", wait_until="domcontentloaded")
    page.wait_for_timeout(4000)
    d = re.sub(r"\s+", " ", page.locator(DETAIL).inner_text())

    # 「#70 in Computer Programming」形式を拾う。Kindle Store は総合順位なので分ける
    cats = [(int(n.replace(",", "")), c.strip())
            for n, c in re.findall(r"#([\d,]+)\s+in\s+([A-Za-z][^#(]{2,44})", d)
            if "Kindle Store" not in c]

    crumb = page.locator(CRUMB)
    return {
        "lang":  grab(d, r"Language\s*[^A-Za-z]*([A-Za-z]+)"),
        "pages": grab(d, r"Print length\s*[^0-9]*([\d,]+)\s*pages"),
        "rank":  grab(d, r"#([\d,]+)\s+in\s+Kindle Store"),
        "cats":  cats[:3],
        "crumb": re.sub(r"\s+", " ", crumb.inner_text()).strip() if crumb.count() else "",
    }

with sync_playwright() as pw:
    browser = pw.chromium.launch()
    page = browser.new_page()
    for asin in sys.argv[1:]:
        print(asin, book(page, asin))
    browser.close()

参入閾値のほうは、カテゴリのベストセラーページから最下位の本を取って、その本の総合順位を引きます。

def threshold(page, node):
    page.goto(f"https://www.amazon.co.jp/gp/bestsellers/digital-text/{node}/?pg=2",
              wait_until="domcontentloaded")
    page.wait_for_timeout(5000)

    items = []
    for card in page.locator("[id^=gridItemRoot]").all():
        badge = card.locator(".zg-bdg-text")
        link  = card.locator("a[href*='/dp/']").first
        if not badge.count() or not link.count():
            continue
        m = re.search(r"/dp/([A-Z0-9]{10})", link.get_attribute("href") or "")
        if m:
            items.append((int(badge.inner_text().lstrip("#").replace(",", "")), m.group(1)))

    pos, asin = max(items)              # 表示されている中で一番下の本
    return pos, book(page, asin)["rank"]  # (その順位, その本の総合順位)

カテゴリのノードIDは、ベストセラーページのリンクから辿れます。a[href*="bestsellers/digital-text/"] を拾って階層を降りるだけです。Kindle eBooks 直下から深さ2で329ノード取れました。

書いてみて分かった詰まりどころを3つ置いておきます。

  • 検索結果ページは403が返ります。 商品ページとベストセラーページは通るので、キーワードの競合数を数える用途だけ別の手段が要ります
  • UIが英語で返ってきます。 日本語のラベルで正規表現を書くと 発売日ページ数 も1つも取れません。Publication datePrint length で書きます
  • 英語で返る条件までは詰めていません。 私の環境では常に英語だったので英語ラベルで書きましたが、localeAccept-Language で日本語に寄せられるかは試していません。日本語ラベル前提で書くなら、まず1冊で実際に何が返るか見てください

ついでに分かった実務的なこと

同じ調査で出た副産物です。KDPで本を出している方には使えるかもしれません。

  • カテゴリの参入閾値は測れます。 あるカテゴリのベストセラー最下位の本の総合順位が、そのカテゴリに入るための水準です。実測では Computers & Technology › Programming は80位の本が総合#24,027、Applications は総合#23,482でした。総合順位すら付いていない本は、このカテゴリには入れません
  • Amazonは著者の全書籍を返す口を持っていません。 著者ページは16件で頭打ちで、スクロールしても増えませんでした。ASIN台帳は自前で持つ必要があります
  • リポジトリをgrepしてASINを集めると他人の本が混ざります。 記事中で言及した書籍のASINを拾うためです。私の台帳42件のうち4件がこれでした
  • Amazonの商品ページはUIを英語で返してくることがあります。 日本語のラベルで正規表現を書くと、Publication datePrint length を全部取りこぼします

一番効いたのは、介入実験を先にやらなかったことでした。最初は「売れていない3冊を改題して4週間待つ」という計画を立てていて、既刊65冊がすでに65回分の自然実験になっていることに気づいてそちらを先に見たので、4週間待たずにその晩のうちに仮説が否定されています。手を動かす前に、手元にあるデータで否定できないかを先に見る。それだけの話です。

今回はそれで3週間が浮きました。

ナレッジグラフ活用大全 関連書籍 ナレッジグラフ活用大全 ナレッジグラフ 活用大全 | GraphRAG・Neo4j・RDF・Property Graph・Emotion AI の実践書 書籍ページを見る →