📚 古本Arbitrage システム全体図

仕入元サイト × Amazonの価格差を見つけるシステムの仕組み・用語集 (= ユーザー向け説明書)

最終更新: 2026-05-23

📋 目次

① そもそも何のシステム?

古本Arbitrage (= 価格差仕入販売) を自動化するシステム です。

仕入元サイトブックオフ等 安い
価格差を見つける自動でリスト化
Amazon FBA高く売る

仕入元サイト (= 9プラットフォーム)

国内 (= 紙の古本)
ブックオフ / もったいない / 楽天 / バリューブックス / ネットオフ / 紀伊國屋 / Yahoo!ショッピング
海外
AbeBooks / ThriftBooks

システムの役割

役割説明
① 仕入元サイトを巡回各サイトの在庫を毎日チェック (= 「スクレイパー」)
② 価格情報をDBに蓄積商品マスタ + 在庫情報を PostgreSQL に保存
③ Amazon価格を取得Keepa API で各商品の Amazon価格を取得
④ 利益の出る商品を抽出仕入価格 vs Amazon売価 で利益計算
⑤ ポータルに表示/books 画面で仕入候補を一覧表示

② 全体の流れ (= パイプライン図)

仕入元サイト
scraper
= 巡回プログラム
listings
= 在庫テーブル
Phase 2 ISBN補完
Phase 3 ASIN紐付け
Keepa API
book_products
= Amazon商品マスタ
book_summary_mv
= 集計済キャッシュ
portal /books
= 仕入候補画面

各段階の説明

段階やること担当
Phase 1仕入元サイトの一覧を巡回 → 商品情報を取得各サイトの scraper
Phase 2各商品の詳細ページから ISBN (= 本のID) を補完各サイトの scraper
Phase 3ISBN → ASIN (= AmazonのID) を紐付けauto_pipeline.sh
Keepa取得ASINに対応する Amazon情報を Keepa API から取得continuous_bulk
MV更新集計テーブル book_summary_mv を 30分ごと再構築auto_pipeline.sh
portal表示ユーザーが /books で利益商品を一覧Next.js (= web画面)

③ 主要なテーブル (= データの貯蔵庫)

テーブル book_products = Amazon商品マスタ

Amazonに登録されている商品のマスター情報。 約 190万件 (= 紙の本中心)。

カラム名意味
asinAmazonの商品ID (= 10文字英数字)
isbn_13本の国際番号 (= 13桁)
title商品タイトル
amazon_price_jpyAmazon最安購入価格 (= 新品/中古/BuyBoxの最安)
amazon_new_price_jpy新品最安
amazon_used_price_jpy中古最安
amazon_buybox_price_jpyBuy Box価格 (= 客が実際に買う価格)
amazon_sales_rank売れ筋ランキング (= 数字が小さいほど売れる)
amazon_offer_count出品者の数
has_amazon_or_fba_offer_24h24時間以内に Amazon直販/FBA出品があるか
offers_last_fetched_atKeepa /offers API で最後に取得した日時
keepa_last_fetched_atKeepa /product API で最後に取得した日時

テーブル listings = 仕入元サイトの在庫リスト

各サイトで見つかった商品の在庫情報。 約 1,500万件

カラム名意味
platform仕入元 (= bookoff / rakuten / mottainai / 等)
isbn本の国際番号 (= 13桁、 Phase 2で補完)
asinAmazonのID (= Phase 3で紐付け)
price_jpy仕入元での販売価格
in_stock在庫あり / なし
fetched_atスクレイピングした日時
url商品ページURL

テーブル book_summary_mv = 集計済 高速キャッシュ

portal表示を高速化するための事前集計テーブル (= Materialized View)。 30分ごとに再構築。 約 46万行

カラム名意味
asinAmazonのID
platform仕入元プラットフォーム
min_active_price_jpyそのASIN×platformの最安在庫価格
active_count在庫アクティブな件数
fetched_at最新取得日時

④ 主要なスクリプト (= 自動で動くプログラム)

スクレイパー系 仕入元サイトを巡回するプログラム

スクリプト役割頻度
bookoff scraperブックオフ巡回毎日
mottainai scraperもったいない巡回毎日
rakuten scraper楽天市場・楽天ブックス巡回毎日
valuebooks scraperバリューブックス巡回毎日
netoff scraperネットオフ巡回毎日
kinokuniya scraper紀伊國屋巡回毎日
abebooks / thriftbooks海外サイト巡回毎日

Keepa取得系 Amazon情報を取得するプログラム

スクリプト役割頻度
keepa_reverse_lookup_incremental.py新規ASIN登録 (= ISBN→ASIN紐付け + 基本情報取得)常時
continuous_bulk ★主役30日経過したASINの Amazon情報を再取得常時稼働
keepa_offers_refresh_oos.py在庫切れASINの詳細チェック毎日
keepa_amazon_price_refresh.py異常値の補正必要時
💡 continuous_bulk が一番重要
このスクリプトが裏で24時間動き続けて、 古いAmazon情報を新しい情報で上書きし続けます。 これがないとDBが半年で使い物にならなくなります。

紐付け系 ISBN ↔ ASIN を結びつけるプログラム

スクリプト役割頻度
auto_pipeline.shPhase 3 (= ISBN→ASIN紐付け) + MV更新10分ごと
各サイトの Phase 2商品詳細ページからISBN抽出scraper後

⑤ Keepa API 契約・運用

Keepa とは

Amazon商品の価格履歴・在庫状況・出品者情報を提供する第三者APIサービス。 月額制で tokens (= API呼び出し回数の上限) を購入する。

現在の契約 (= 2026-05-23 時点)

プラン月額tpm (= tokens/分)契約日
Keepa 60 tpm€129 (= 約2万円)602026-05-16
Keepa 250 tpm€459 (= 約7万円)2502026-05-17
Keepa 2000 tpm€2,499 (= 約40万円)2,0002026-05-19
合計€3,087 (= 約50万円)2,310 tpm-
🔴 6月18日 上位プラン解約予定
契約期限 6/19 の前日に 250 tpm + 2000 tpm を解約。 60 tpm 単独 (€129/月) に縮小。
削減効果: 月48万円、 年600万円。 カレンダーに登録済。

運用サイクル

期間契約サイクル用途
5/23-5/31 (今月)2,310 tpm2.6日で完走初回フルロード (= 170万件全部取得)
6/1以降 (来月以降)60 tpm60日サイクル常にデータベース更新 (= 2ヶ月で全件1周)
✅ なぜ60 tpm単独で60日サイクル可能か
60日サイクルに必要な処理量 = 1日 31,700件。
60 tpm で実測 1日 約 62,400件処理可能 → 136%の余裕 で60日サイクル達成。

⑥ 用語集 (= 専門用語の意味)

ASIN (= Amazon Standard Identification Number)
Amazon上で各商品に付与される10文字の英数字ID。 例: 4320006232 (= 麦とTwitter)。 本の場合は ISBNと一致するケースも多い。
ISBN (= 国際標準図書番号)
本に付与される13桁の国際ID。 例: 9784320006232。 同じ本なら世界中どこでも同じISBN。
Keepa
Amazon商品の価格履歴・在庫情報を提供する有料APIサービス (https://keepa.com)。 古本転売業界では事実上の標準ツール。 月額制で tokens (= 呼び出し回数枠) を購入。
Buy Box
Amazon商品ページの「カートに入れる」 ボタン直結の出品者枠。 客が普通に買うとここから買うことになる = 「実際に売れる価格」。 複数出品者で争奪戦。
FBA (= Fulfillment by Amazon)
Amazon倉庫に在庫を預けて、 注文・配送・カスタマーサポートをAmazonに代行してもらうサービス。 Prime対応 + Buy Box取りやすい。 手数料が高い。
FBM (= Fulfillment by Merchant)
自社で在庫管理・配送を行う方式。 FBAの逆。 手数料安いがBuy Box取りにくい。
Sales Rank (= 売れ筋ランキング)
Amazonでの売れ行き順位。 1位が一番売れる。 本カテゴリで100万位以下は「ほぼ売れない」。 仕入判断の主要指標。
condition (= Keepa の商品状態コード)
Keepa offers配列内の出品状態。 1=New (新品), 2=Used-LikeNew (未使用に近い), 3=Used-VeryGood, 4=Used-Good, 5=Used-Acceptable, 6=Refurbished, 7-10=Collectible11は存在しない
tpm (= tokens per minute)
Keepaの呼び出し速度上限。 1分あたり消費可能なtoken数。 60 tpm = 1分60トークン消費可能 (= 1リクエスト約2 tokens なら 30リクエスト/分)。
MV (= Materialized View)
PostgreSQLの「事前計算済テーブル」。 通常のViewは都度計算するが、 MVは結果を物理的に保存して高速化。 30分ごとに REFRESH MATERIALIZED VIEW で再構築。
launchd
macOSの自動起動・スケジュール実行システム (Linux でいう cron 相当)。 ~/Library/LaunchAgents/*.plist でジョブ定義。 scraperやauto_pipelineを定刻に動かす。
scraper (= スクレイパー)
Webサイトを自動巡回して商品情報を取得するプログラム。 当システムでは httpx (= Python HTTPクライアント) を使用。 サイトのWAF対策とのいたちごっこ。
Phase 1 / 2 / 3
スクレイピングの段階分け (= 当プロジェクト独自用語)。 Phase 1=一覧巡回Phase 2=詳細ISBN補完Phase 3=ISBN→ASIN紐付けSQL
バックフィル (= backfill)
過去に取得した壊れたデータを修正版コードで再取得して上書きする作業。 例: 「cond==11 バグで NULL化された44,454件をNULLマークに戻して continuous_bulkに再取得させる」。
独占チャンス案件
Amazon側で 24時間以内のFBA出品がない + listings在庫ありの商品。 自分がFBA出品すればBuy Box独占可能 = 高利益チャンス。 portal でバッジ表示。
特選チャンス (= v3で追加)
利益額 ≥ ¥2,000 AND 利益率 ≥ 50% AND BSR ≤ 10万 AND 出品数 ≤ 5 を満たす最優先案件。 portalで💎バッジ表示。
FBA代行
仕入れた商品をAmazon倉庫に納品する作業を代行してくれる業者。 当社は amazon-fbadaikou.jp プレミアム会員契約 (¥4,900/月)。 発送先: 三郷市番匠免1丁目30 薩摩賢幸宛。
プライスター
Amazon出品自動化ツール。 当システムでは CSVでASIN/価格/原価を出力 → プライスター取込 → Amazon出品 の流れ。

⑦ よくある質問

Q. continuous_bulk いる?

A. 必須。 削除するとDBが半年で使い物にならなくなる。 「DB蓄積=データ永続」 と思いがちだが、 蓄積したデータは時間が経つと古くなる。 continuous_bulkは「古い情報を新しい情報で上書き」 する役割。 これがないとAmazon価格が永遠に古いまま。

Q. なぜAmazon価格は都度叩かない?

A. portal応答が遅くなる + 同一ASINの重複呼び出しで token浪費。 業界標準は「事前にDB化 + 定期更新」。

Q. Keepaのデータが古くなる時間は?

A. 最大60日。 6月以降の運用で常に「60日以内のデータ」 を維持。 業界標準より1か月遅れだが、 月50万円→月2万円のコスト削減で許容。

Q. 仕入元サイトに新規商品が出たらどうなる?

A. ① scraper が拾う → ② listings に登録 → ③ Phase 2 でISBN補完 → ④ Phase 3 でASIN紐付け → ⑤ Keepaから Amazon情報取得 → ⑥ portal表示。 全自動。

Q. ポータルが重い時の対処は?

A. v3で LIMIT 100,000 → 2,000 + ORDER BY 利益額DESC に変更済。 応答 34秒→5秒に改善。 もし重くなったら book_summary_mv の REFRESH中の可能性。

Q. cond==11 って何?

A. 存在しないKeepaのコード。 過去の指示書が「cond==11=NEW」 と誤認した結果、 4.4万件のデータが壊れた事故あり。 v3で cond==1=NEW に修正済。

Q. システムが完全停止したらどうなる?

A. ① scraper停止 = 新規仕入候補が見つからない (= 既存の portal表示は継続)。 ② continuous_bulk停止 = Amazon価格が古いまま (= 表示は継続)。 ③ portal停止 = ユーザー画面表示不可。 復旧は launchd 再起動で。