自動購入botの作り方|Pythonの実装コードと、購入まで自動化できない3つの理由

転売

このブログは5年ほど、「自動購入botは自分で作れる」と書いてきました。今回、その前提を1つずつ確認し直しました。

結論から書きます。作れます。ただし、作れるのは購入の手前までです。

理由は精神論ではありません。日本の主要ECには「購入」を実行できる公式APIが1つも存在せず、Amazonは自社のrobots.txtでカートとサインインと購入ボタンの処理を名指しで拒否しており、Seleniumが検知されるのはW3Cの標準仕様がそう定めているからで、そしてYahoo! JAPAN IDはパスワードだけのログインを順次終了します。

この4つは、どれも回避テクニックの話ではありません。土台がそうなっているという話です。

そこでこの記事は、在庫監視と通知までを作り、購入ボタンは人間が押すという設計で書き直しました。実際に動くPythonコードを全文載せます。前の版にはコードが1行もありませんでした。そこがこの改稿でいちばん変わった点です。

1. 結論:どこまで自動化できて、どこからできないのか

購入という行為を処理ステップに分解すると、自動化の可否がきれいに分かれます。

ステップ 自動化 理由
商品ページの取得 条件つきで可 robots.txtが許可している範囲であれば可能。間隔の管理が前提
在庫の有無の判定 取得したHTMLを解析するだけ。サイト側に追加の負荷はかからない
通知(Slack・LINE・メール) 自分の環境で完結する
ログイン 困難 二要素認証・パスキーへの移行。SMSは別デバイスに届くため、サーバー上のスクリプトからは原理的に取得できない
カート投入 不可に近い Amazonはrobots.txtで /gp/cart を名指しで拒否。bot検知の主戦場でもある
決済の確定 不可 公式APIが存在しない。reCAPTCHA v3は警告を出さずスコアだけ下げる

線が引かれるのは「ログイン」の手前です。ログインしない範囲の情報取得と判定と通知は、いまも問題なく組めます。ログインの先は、技術的にも規約的にも別の世界になります。

この記事が作るのは、線の手前側です。具体的には次のものです。

  • 指定した商品ページを、robots.txtで許可された間隔で見に行く
  • 在庫が復活したらSlackに通知する
  • 失敗が続いたら自分で止まる
  • 購入ボタンは、通知を見たあなたが押す

「それでは意味がない」と感じるかもしれません。ですが、先着販売で勝負が決まるのは通知から数十秒の世界です。24時間画面を見張らなくてよくなる、という一点だけで、この構成の価値はあります。そして、この構成なら壊れません。壊れる理由を全部避けてあるからです。

2. 前提の確認:購入できる公式APIは存在しない

ここが記事の背骨です。市販ツールにせよ自作botにせよ、なぜ全部が不安定なのかは、この一点で説明がつきます。

2-1. 主要5サービスのAPI提供状況

各社の公式開発者ドキュメントを1つずつ確認しました。

サービス 公開されているAPI 購入
Amazon SP-API。冒頭に「出品パートナー向けのアプリケーションを作る開発者のためのサイト」と明記。Orders APIは自分の店に入った注文を処理するもの 不可
楽天 楽天ウェブサービス。市場5・ブックス9・トラベル7・レシピ2・Kobo2・GORA3の計28API。すべて検索と情報取得 不可
Yahoo!ショッピング Yahoo!デベロッパーネットワーク。「注文API」は存在するがストア(出店者)向けで、受注処理用。一般向けは検索系のみ 不可
ヤフオク! オークションWeb APIは2018年2月22日に提供終了。残っていた評価APIも2020年1月に終了 APIなし
メルカリ メルカリShops APIのみ。商品CRUD・在庫管理・注文処理・Webhook。対象は中〜大規模ショップ 不可

5サービスすべてで、公開APIは「検索・取得」か「出品者側の受注処理」のどちらかでした。買い手として注文を出す機能は、どこにもありません。

2-2. Amazonの商品データAPIは、実質的に閉じた

もう一段、厳しい変化があります。Amazonの商品データを取得する唯一の公式手段だったPA-API 5.0(Product Advertising API)は非推奨(deprecated)となりました。旧ドキュメントのURLは告知ページへリダイレクトされ、旧エンドポイントを叩くとHTTP 403が返り、「Creators APIへ移行してください」というメッセージが返ります。サービス終了日そのものは告示されていませんが、既存の呼び出しが拒否される以上、使える手段ではありません。

後継のCreators APIで使える操作は4つだけです。SearchItems / GetItems / GetVariations / GetBrowseNodes。購入系の操作は当然ありません。

そして利用資格に、こう書かれています。

直近30日間で10件以上の適格売上があること

これから始める人は、Amazonの商品データを公式に取得する手段を持ちません。「まず価格監視ツールを自作して、慣れてきたら…」という定番の入り口が、制度として閉じています。PA-APIを前提に書かれたまま更新されていない解説記事が多いので、ここは注意してください。

2-3. これが意味すること

自作botが「公式に用意されていない経路」を通るのは、開発者が横着をしているからではありません。選択肢が存在しないからです。

この構造は、そのまま結論に効いてきます。公式の入口が無いということは、いつ塞がれても文句が言えないということです。市販ツールが次々に開発を終了しているのも、同じ理由の別の現れ方です。

市販ツールの側で何が起きたかは、こちらにまとめました。

自動購入ツール・bot比較6選【2026年8月検証】今も動くのはどれか

[LP]

3. Amazonのrobots.txtは、購入導線を名指しで拒否している

規約の文言は解釈が割れます。robots.txtは割れません。機械が読む形式で書かれた、明示的な意思表示だからです。

3-1. 拒否されているパス

https://www.amazon.co.jp/robots.txt は492行あります(末尾に改行が無いため wc -l は491を返します)。その中に、自動購入botが必ず通る経路が並んでいます。

Disallow: /gp/cart
Disallow: /gp/sign-in
Disallow: /ap/signin
Disallow: /gp/item-dispatch
Disallow: /exec/obidos/handle-buy-box
Disallow: /gp/legacy-handle-buy-box.html

カート、サインイン、購入ボタンの処理(buy-box handler)。自動購入に必要な全ステップが、Amazon自身の手でDisallow指定されています。

これは、その場で確かめられます。Pythonの標準ライブラリだけで書けます。

import urllib.robotparser
from urllib.parse import urlparse

def probe(url, ua="my-stock-watcher/1.0 (contact: you@example.com)"):
    p = urlparse(url)
    rp = urllib.robotparser.RobotFileParser()
    rp.set_url(p.scheme + "://" + p.netloc + "/robots.txt")
    rp.read()
    print(url, "=>", rp.can_fetch(ua, url))

probe("https://www.amazon.co.jp/gp/cart/view.html")
probe("https://www.amazon.co.jp/dp/B08N5WRWNW")

実行結果はこうなります。

https://www.amazon.co.jp/gp/cart/view.html => False

503:サービスが利用できませんService Unavailable Error
=> True

商品ページは取得してよい。カートは駄目。境界線が、機械が読める形で引かれています。この記事が「監視と通知まで」で止める設計を選んでいるのは、精神論ではなく、この False が根拠です。

3-2. UAブロックが101個。Scrapyも名指しされている

同じファイルには User-agent ブロックが101個あり、そのうち100個が Disallow: /(サイト全面禁止)です。名前が挙がっているものを一部引くと、こうなります。

  • Scrapy(Pythonの代表的なスクレイピングフレームワーク)
  • Crawl4AI、Devin、Manus-User
  • GPTBot、ChatGPT-User、ClaudeBot、Claude-User、PerplexityBot
  • Bytespider
  • GoogleAgent-Shopping

Pythonのスクレイパーと、AIエージェントが、名指しで、サイト単位で締め出されています。最後の GoogleAgent-Shopping が象徴的です。「AIに買い物をさせる」という発想そのものが、プラットフォーム側で先回りして拒否されているということになります。

3-3. robots.txtに法的な拘束力はあるのか

ありません。robots.txtは技術的な取り決めであって、法令でも契約でもありません。無視しても、それだけで違法になるわけではない。

ただし、実務上は次の意味を持ちます。

  • サイト側の意思が明文化されているため、「知らなかった」が通らなくなる
  • 後に紛争になったとき、拒否の意思表示が存在したことの証拠になる
  • 利用規約の禁止条項と組み合わさると、規約違反の認定が容易になる

「法的拘束力がないから守らなくていい」ではなく、「法的拘束力がないのにここまで明示されている」という事実の重さを見たほうがいい。この記事のコードは、robots.txtを読んでから動く設計にしてあります。

4. 作るもの:在庫監視・通知botの設計

4-1. 処理フロー

  1. 起動時にrobots.txtを取得し、対象URLの取得が許可されているか確認する
  2. robots.txtが読めなかった場合も、許可されていなかった場合も、何もせず終了する(ここは標準ライブラリの既定動作に任せてはいけません。理由は後述します)
  3. 許可されていれば、robots.txtが指定する間隔(Crawl-delay)を尊重して取得する
  4. HTMLから在庫の有無を判定する
  5. 在庫が復活していればSlackに通知する
  6. 取得に失敗し続けたら、自分で停止して通知する

2番と6番が、他の解説記事にはあまり書かれていない部分です。やらないことと、止まることを先に決めておくのが、この種のプログラムでいちばん大事です。

4-2. なぜ「監視と通知」で止めるのが合理的か

3つあります。

壊れないから。ログインしない、カートに入れない、決済しない。この3つを避けると、壊れる原因のほとんどが消えます。二要素認証もCAPTCHAもbot検知も、そのほとんどは購入導線に集中して配置されているからです。

間に合うから。先着販売で勝敗を分けるのは、在庫が復活した瞬間を知るまでの時間です。そこから先の操作を人間がやっても、体感で数十秒しか違いません。一方、決済まで自動化しようとすると、その数十秒を得るために壊れやすさを全部引き受けることになります。

説明がつくから。アカウントが止まったとき、「公開ページをrobots.txtの範囲で見に行って通知していただけです」と言える状態と、そうでない状態には差があります。

4-3. 必要なもの

要素 役割
Python 3.11以降 本体
requests HTTPでページを取得する
beautifulsoup4 HTMLから文字列を取り出す
urllib.robotparser robots.txtを読む。標準ライブラリなので追加インストール不要
Slack Incoming Webhook 通知の宛先。LINEやDiscordでも同じ構造で書けます

Seleniumは使いません。ブラウザを立ち上げる必要が無いからです。在庫表示がJavaScriptで後から描画されるサイトの場合だけ、後述の方法を検討してください。

5. 実装:コードと、その読み方

5-1. 環境構築

python3 -m venv .venv
source .venv/bin/activate        # Windowsなら .venv\Scripts\activate
pip install requests beautifulsoup4

依存はこの2つだけです。webdriver-manager も selenium も要りません。

5-2. 監視スクリプト(全文)

stock_watch.py として保存してください。

import os
import sys
import time
import random
import logging
import urllib.robotparser
from urllib.parse import urlparse

import requests
from bs4 import BeautifulSoup

# 自分の連絡先を書いたUAを名乗る。匿名で叩かないこと。
UA = "my-stock-watcher/1.0 (contact: you@example.com)"

TARGET_URL = os.environ["TARGET_URL"]
SLACK_WEBHOOK = os.environ["SLACK_WEBHOOK"]

# 「在庫あり」を示す文字列。ここに一致したときだけ在庫ありと判定する。
# 「在庫切れ語が無い=在庫あり」という否定形で書くと、CAPTCHA画面や
# エラーページを全部「在庫あり」と誤判定する。必ず肯定形で書くこと。
IN_STOCK_WORDS = ("カートに入れる", "今すぐ買う", "在庫あり")

# robots.txt に Crawl-delay の指定が無かった場合の既定値(秒)
DEFAULT_INTERVAL = 300

# 連続失敗の上限
MAX_ERRORS = 5

logging.basicConfig(level=logging.INFO,
                    format="%(asctime)s %(levelname)s %(message)s")


def load_robots(url):
    """robots.txt を読み込む。読めなければ None を返す。

    urllib.robotparser の read() は、404 のときに「全部許可」扱いになり、
    接続失敗のときは例外を投げる。どちらも自動巡回では危ないので、
    自分で取得して、200 のときだけ parse に渡す。
    """
    parts = urlparse(url)
    rp = urllib.robotparser.RobotFileParser()
    robots_url = parts.scheme + "://" + parts.netloc + "/robots.txt"
    rp.set_url(robots_url)
    try:
        res = requests.get(robots_url, headers={"User-Agent": UA}, timeout=20)
    except requests.RequestException as e:
        logging.error("robots.txt を取得できませんでした: %s", e)
        return None
    if res.status_code == 200:
        res.encoding = res.apparent_encoding or "utf-8"
        rp.parse(res.text.splitlines())
        return rp
    logging.error("robots.txt が HTTP %s を返しました。判断できないので動かしません。",
                  res.status_code)
    return None


def notify(text):
    try:
        requests.post(SLACK_WEBHOOK, json={"text": text}, timeout=10)
    except requests.RequestException as e:
        logging.warning("通知の送信に失敗: %s", e)


def in_stock(raw):
    """在庫があれば True、無ければ False、判定できなければ None。

    引数は res.content(bytes)。res.text を渡してはいけない。
    サーバーが Content-Type に charset を書いていない場合、requests は
    ISO-8859-1 と推定するため、日本語がすべて文字化けして
    どの語にも一致しなくなる(Amazon.co.jp が実際にそうなる)。
    """
    soup = BeautifulSoup(raw, "html.parser")
    body = soup.get_text(" ", strip=True)
    if not body:
        return None
    return any(word in body for word in IN_STOCK_WORDS)


def main():
    rp = load_robots(TARGET_URL)
    if rp is None:
        # 「意図した停止」なので終了コードは 0。
        # systemd 側を Restart=on-failure にしておけば、ここで本当に止まる。
        return 0

    if not rp.can_fetch(UA, TARGET_URL):
        logging.error("robots.txt がこのURLの取得を許可していません。終了します。")
        return 0

    delay = rp.crawl_delay(UA)
    interval = DEFAULT_INTERVAL if delay is None else max(int(delay), 1)
    logging.info("取得間隔: %s秒", interval)

    session = requests.Session()
    session.headers["User-Agent"] = UA

    errors = 0
    notified = False

    while True:
        # 連続失敗が続いたら、自分で止まる。
        if errors >= MAX_ERRORS:
            notify("%d回連続で取得に失敗したため停止しました\n%s" % (errors, TARGET_URL))
            logging.error("連続失敗のため停止します。")
            return 0

        try:
            res = session.get(TARGET_URL, timeout=20)

            # 429 / 503 は「今は来るな」という明確な合図。素直に待つ。
            if res.status_code in (429, 503):
                errors += 1
                retry_after = res.headers.get("Retry-After")
                if retry_after and retry_after.isdigit():
                    wait = min(int(retry_after), 3600)
                else:
                    wait = min(interval * pow(2, errors), 3600)
                logging.warning("HTTP %s。%s秒待機します。(連続%s回目)",
                                res.status_code, wait, errors)
                if errors < MAX_ERRORS:
                    time.sleep(wait)
                continue

            res.raise_for_status()
            errors = 0

            state = in_stock(res.content)
            if state is None:
                logging.warning("在庫の判定ができませんでした(ページ構造の変化の可能性)。")
            elif state:
                if not notified:
                    notify("在庫を検知しました\n" + TARGET_URL)
                    notified = True
                    logging.info("通知しました。在庫切れに戻るまで再通知しません。")
            else:
                notified = False
                logging.info("在庫なし。")

        except requests.RequestException as e:
            errors += 1
            logging.warning("取得に失敗: %s(連続%s回目)", e, errors)
            time.sleep(min(interval * pow(2, errors), 3600))
            continue

        # 等間隔で叩かない。人間はメトロノームではない。
        time.sleep(interval + random.uniform(0, interval * 0.3))


if __name__ == "__main__":
    sys.exit(main() or 0)

実行はこうです。

export TARGET_URL="https://example.com/products/12345"
export SLACK_WEBHOOK="https://hooks.slack.com/services/XXX/YYY/ZZZ"
python stock_watch.py

後でsystemdに載せる場合、環境変数は .env ファイルに書きます。このときexport を付けてはいけません。systemdの EnvironmentFile は KEY=value 形式しか読まず、export TARGET_URL=... と書くと変数名が「export TARGET_URL」になって無視されます。

TARGET_URL=https://example.com/products/12345
SLACK_WEBHOOK=https://hooks.slack.com/services/XXX/YYY/ZZZ

5-3. このコードで、他の解説と違う部分

6箇所あります。ここが本題です。とくに⑤と⑥は、実際に動かして初めて分かったもので、書いてある解説をほとんど見たことがありません。

① robots.txtを読んでから叩く。rp.can_fetch(UA, TARGET_URL) が False なら、何もせず終了します。人間が規約を読む代わりに、プログラムが機械可読な意思表示を読む。当たり前のようでいて、ほとんどの解説記事のコードには入っていません。

ただし、標準ライブラリの rp.read() をそのまま使ってはいけません。CPythonの実装では、robots.txtが404を返すと「全部許可」扱いになり、接続に失敗すると例外を投げて落ちます。つまり「robots.txtが無いサイトは無条件で叩き放題」という挙動になる。上のコードが自分で requests.get() して200のときだけ parse に渡しているのは、この既定動作を避けるためです。読めなければ動かしません。

② Crawl-delay を尊重する。間隔を自分で決めず、サイト側が指定した値を使います。指定が無い場合だけ既定の5分を使います。「何秒なら安全か」を推測する必要がなくなります。

③ 等間隔で叩かない。random.uniform(0, interval * 0.3) を足しています。人間の操作は正確に300秒おきには起きません。これは検知回避のためではなく、相手のサーバーに規則的な負荷の山を作らないためです。多数の人が同じスクリプトを毎時0分に走らせると、そこだけ負荷が集中します。

④ 自分で止まる。連続5回失敗したら通知して終了します。自動化で本当に怖いのは、動かなくなることではなく、おかしくなったまま回り続けることです。403が返り続けているのに叩き続けるスクリプトは、事故に向かってまっすぐ進んでいます。429が返ってきたときは Retry-After ヘッダを見て、指定があればその秒数だけ待ちます。

⑤ 在庫の判定は「肯定形」で書く。ここが最も重要です。

やりがちなのは、こう書くことです。

# これは危ない
return not any(word in body for word in SOLD_OUT_WORDS)

「在庫切れの文字が無いから在庫あり」という書き方です。一見合理的ですが、在庫と無関係なページを全部「在庫あり」と判定します。CAPTCHA画面も、エラーページも、空のレスポンスも、すべて「在庫あり」になる。

これは机上の話ではありません。実際に試したところ、Amazonの商品ページは、このスクリプトのUAに対してHTTP 200でbot検査の中間ページ(「下のボタンをクリックしてショッピングを続けてください」)を返しました。200なので raise_for_status() は素通りし、否定形の判定はTrueを返します。

結果どうなるか。在庫がゼロなのに「在庫を検知しました」と通知が飛び、notified = True になって、以後は本当に入荷しても二度と通知が来ません。自動化でいちばん質の悪い壊れ方です。だから上のコードは、在庫ありを示す語に一致したときだけTrueを返し、判定できなければ None を返して安全側に倒します。

⑥ res.text ではなく res.content を渡す。これも実際に踏みました。

Amazon.co.jp は Content-Type: text/html を、charset の指定なしで返します。この場合 requests はRFCに従ってISO-8859-1と推定するため、res.text の中で日本語が全滅します。

Content-Type: text/html
r.encoding: ISO-8859-1   |  r.apparent_encoding: utf-8
"カート" in r.text  -->  False

日本語のキーワードは、そもそも一度も一致しません。⑤と組み合わさると、「常に在庫ありと判定するだけの装置」ができあがります。res.content(バイト列)を BeautifulSoup に渡せば、HTML内の meta charset を見て正しく解釈してくれます。

5-4. JavaScriptで描画されるサイトの場合

在庫表示がページ読み込み後にJavaScriptで書き込まれる場合、requests では取れません。この場合の選択肢は2つです。

ブラウザの開発者ツールで実際の通信を見るのが第一手です。F12を押してNetworkタブを開き、ページを再読み込みすると、在庫情報がJSONで飛んでいることがよくあります。そのエンドポイントを直接取得できれば、HTMLを解析するより速く、軽く、壊れにくい。ただし非公開エンドポイントなので、robots.txtと規約の確認は必須です。

それが無理ならPlaywrightを使います。ただし、ブラウザを立ち上げた瞬間に後述の検知の話が全部乗ってきます。監視だけならメモリも食う。使わずに済むなら使わないほうがいいというのが、率直なところです。

5-5. AIにコードを書かせるときの使い方

前の版は、Claudeへのプロンプト文を17個並べていました。今回それをやめた理由を書きます。

大規模言語モデルは、対象サイトの現在の仕様を見てコードを書いているわけではありません。学習データから、もっともらしいコードを確率的に生成しています。だから、存在しないメソッドを自信満々で書きます。これは欠陥ではなく、そういう仕組みだというだけです。

ですから、AIに投げるべきなのは「コードを書いて」ではなく、「このコードのどこが壊れるか指摘して」のほうです。生成より検証のほうが、AIの得意分野に近い。

実際に使える投げ方を挙げます。

  • 「このスクリプトを24時間動かしたとき、止まる可能性のある箇所を全部挙げて。ネットワーク、パース、認証の順で」
  • 「このコードは対象サイトに1日あたり何リクエスト送りますか。計算過程も出して」
  • 「この except 節が握りつぶしている例外を列挙して。握りつぶすべきでないものはどれ」
  • 「このコードを、robots.txt を無視しない形に直すとしたらどこを変えますか」

コードを丸ごと書かせるより、この使い方のほうが結果的に速い。そして、自分が読めないコードを本番で回さずに済みます。

[LP]

6. なぜ購入まで自動化できないのか(技術の側から)

前の版は「Seleniumはサイト側の複雑なbot検知システムに弾かれにくい」と書いていました。これは逆です。訂正します。

6-1. W3Cの標準仕様が、自動操作の申告を定めている

W3C WebDriver仕様には、こう書かれています。

webdriver-active フラグは、ユーザーエージェントがリモート制御下にあるとき true に設定される

そして navigator.webdriver プロパティの目的は、

協調的なユーザーエージェントが、WebDriverによって制御されていることを文書に伝えるための標準的な方法を定義する

とされています。Seleniumが検知される第一の理由は、実装の粗さではなく、標準仕様がそう定めているからです。「私は自動操作されています」と名乗る仕組みが、規格として組み込まれている。

この事実を踏まえると、検知回避というテーマの性質が変わります。それは標準仕様が定めた申告を無効化する行為だからです。

6-2. reCAPTCHA v3は、CAPTCHAを出さずにスコアだけ下げる

Googleの公式ドキュメントによれば、reCAPTCHA v3はリクエストごとに0.0〜1.0のスコアを返します。1.0が「人間である可能性が高い」、0.0が「botである可能性が高い」。

そして決定的なのがこの一文です。

reCAPTCHA v3 はユーザーを中断させることが決してありません

botの側には「CAPTCHAが出た」という手がかりすら与えられません。画像を選ばされるわけでもなく、エラーが返るわけでもない。ただ静かにスコアが下がり、サイト側がそれをどう扱うかを決めます。

さらに、reCAPTCHAはそのサイトの実トラフィックを見て学習します。汎用の回避策が長持ちしない理由がここにあります。

6-3. Cloudflare と Akamai

Cloudflare Bot Managementは、リクエストごとに1〜99のbotスコアを付けます。検知エンジンは5系統(Heuristics/機械学習/Anomaly Detection/JavaScript Detections/Cloudflare Service。うちAnomaly Detectionは廃止が予告されています)で、公式ドキュメントによれば検知の大半を占めるのは機械学習です。特徴量はヘッダ、セッションの性質、ブラウザのシグナル。JavaScript Detectionsは「ヘッドレスブラウザを識別する」と明記されています。

Akamai Bot Managerは、防御対象としてinventory hoarding(在庫の買い占め)を名指ししています。そして「最初のリクエストから、すべての異常を検査する」としています。1回目から評価されているということです。

実際にレスポンスヘッダを確認したところ、楽天市場は server: AkamaiNetStorage を返し、Akamaiを利用していることが分かりました(Bot Managerを使っているかまでは外形からは判断できません)。メルカリのほうは server: cloudflare に加えて __cf_bm クッキーが実際にセットされました。これはCloudflareが「Bot ManagementまたはBot Fight Modeで保護されたサイトにのみ置かれる」と定義しているクッキーなので、bot管理機能が有効になっていることの裏付けになります。

6-4. 二要素認証とパスキー ── ここが実質的な終点

そして、いちばん静かに、いちばん決定的な変化がこれです。

Yahoo! JAPANは2026年4月14日、パスワードのみでのログインを順次終了すると告知しました。利用者はSMS認証への切り替えか、ワンタイムパスワードの設定を求められます。

これが自動化に与える影響は決定的です。

  • SMS認証:コードが別のデバイスに届きます。VPS上で動くPythonスクリプトからは、原理的に取得できません
  • パスキー(FIDO2):秘密鍵が認証器の中に閉じ込められ、しかもオリジンにひも付きます。ヘッドレスブラウザからの再現は成立しません

購入自動化の終わりは、規約ではなく認証方式の側から来ています。規約は解釈で争う余地がありますが、SMSが自分のスマホに届くという事実には、争う余地がありません。

なお、Amazon.co.jpと楽天市場(一般会員)が二要素認証を必須化しているかは、公式ヘルプに到達できず確認できませんでした。断定は避けます。

6-5. この記事に書かないこと

はっきり書いておきます。UAの偽装や検知回避ライブラリの使い方は、この記事には書きません。

前の版には書いてありました。しかも、その3つ前の節で「botを用いてアクセスする行為自体が利用規約違反にあたる」と自分で認めた直後にです。違反だと認定した行為の実行方法を同じ章で教えているのは、記事として筋が通っていません。この改稿でいちばん直したかったのは、この部分です。

[LP]

7. なぜ購入まで自動化しないほうがいいのか(規約と法の側から)

ここは慎重に書きます。筆者は法律の専門家ではありません。条文と、確認できた事例だけを並べます。個別の判断は弁護士にご相談ください。

なお、2025年6月1日に施行された改正刑法により、懲役と禁錮は廃止され「拘禁刑」に一本化されました(令和4年法律第67号)。以下の刑名はすべて拘禁刑で記載します。他サイトで「懲役」と書かれている記事は、この改正前の記述です。

7-1. 規約:自分で書いたコードだから許される、ということはない

楽天会員規約 第8条1項(9)は、「通常意図しない効果を及ぼす外部ツールの利用・作成・頒布」を禁止しています。条文に「作成」と書かれている点に注目してください。市販ツールに限定した話ではありません。

同項(10)はサーバーへの過度の負担を、(7)は複数アカウントの作成・保有を禁じています。

各社の規約の詳細は、市販ツール側の記事にまとめてあります。楽天が自動購入ツールを名指しで禁止していること、判断基準を公開しないとしていること、Amazonが商業目的の利用自体をライセンスの外に置いていること、ヤフオク!が自動出品だけを名指しで禁止していることなど、5社分を一次情報で整理してあります。

自動購入ツール・bot比較6選【2026年8月検証】今も動くのはどれか

7-2. 未遂処罰の非対称 ── ここが見落とされている

刑法の関係条文を並べます。

条文 法定刑 未遂処罰
233条(信用毀損及び業務妨害) 3年以下の拘禁刑または50万円以下の罰金 なし
234条の2(電子計算機損壊等業務妨害) 5年以下の拘禁刑または100万円以下の罰金 あり(2項)
246条の2(電子計算機使用詐欺) 10年以下の拘禁刑 あり

234条の2の第2項は、こう書かれています。

2 前項の罪の未遂は、罰する。

「サーバーを落としていないから大丈夫」という理屈は、234条の2の射程に入った瞬間に成立しません。結果が出ていなくても未遂で処罰されうるからです。

7-3. Librahack事件 ── 1秒に1回で、20日間勾留された

日本で「クローラを書いて逮捕された」事例として確認できるのは、いまも実質的にこれ1件です。

2010年、岡崎市立中央図書館の蔵書検索システムに接続障害が発生しました。同年5月25日、新着資料を取得するクローラを書いていた男性が偽計業務妨害の容疑で逮捕され、20日間勾留されます。6月14日、起訴猶予処分。

男性のクローラのアクセス間隔は、約1秒に1回でした。専門家の評価は「常識的」「礼儀正しい」というものです。

そして障害の真因は、図書館側のシステムにありました。1アクセスにつきデータベース接続を10分間保持するという実装で、頻度の高い機械的アクセスによって同時接続数が設定値を超え、障害が発生していました。報道によれば、1時間あたり400リクエストを超えると応答不能になり、修正版は2006年に存在したものの、岡崎市は不具合の存在を知らされないまま旧版を使い続けていたとされています。

ベンダーである三菱電機インフォメーションシステムズは、2010年11月30日、公式に非を認めました。「システムインテグレーターとしての責務を十分に果たせていなかった」「生じた問題の根本原因は弊社にある」とし、逮捕された男性本人に対しても謝罪しています。男性のアクセスについては「今の時点で見れば新着図書を見たいという意図であり、結果論だが1つのアクセスの形だと思う」として、対応すべきアクセスだったと説明しました(ただし、逮捕に至った責任への直接的な言及は避けています)。

この事件から読み取るべきことは、1つです。

技術的に正しく、礼儀正しく、真因が相手側にあり、後にベンダーが公式に非を認めた。それでも20日間勾留され、起訴猶予(=無罪ではない)で終わった。

「違法かどうか」と「逮捕されるかどうか」は、別の軸です。前者は条文で決まりますが、後者は相手のサーバーで何が起きたか、相手がどう受け取ったかで決まります。アクセス間隔を1秒にしていた人が捕まっている以上、「常識的な間隔なら安全」という保証はどこにもありません。

7-4. 不正アクセス禁止法は「自分のID」なら該当しない

ここは正確に切り分けておきます。誤解が多い箇所です。

不正アクセス禁止法2条4項1号は、不正アクセス行為を「他人の識別符号を入力して」と定義しています。2号・3号は、アクセス制御を「免れることができる情報又は指令」を入力する行為、いわゆるセキュリティホール攻撃です。

したがって、自分のIDとパスワードでスクリプト経由の自動ログインをする行為は、不正アクセス禁止法違反にはなりません。正規の認証情報を正規の手順で送っている限り、2号・3号にも当たりません。

ただし、これは不正アクセス禁止法に限った話です。次の3つは、そのまま残ります。

  1. 規約違反によるアカウント停止・会員資格の取消
  2. 民事上の債務不履行・不法行為
  3. 態様次第で刑法233条・234条の2

「不正アクセスにならない」は「適法である」ではありません。ここを混同した解説をよく見かけます。

7-5. 著作権法30条の4・47条の5は免罪符ではない

「情報解析目的なら著作権法で許されている」という説明も見かけます。条文を読むと、そこまで広くありません。

著作権法30条の4は、著作物を享受する目的でない場合に「その必要と認められる限度において」利用できるとしたうえで、ただし書きで「著作権者の利益を不当に害することとなる場合は、この限りでない」としています。三重の縛りがかかっています。

47条の5はさらに狭い。主語が「政令で定める基準に従つて行う者に限る」と限定され、許されるのは「軽微利用」だけです。検索サービスなどのサービス提供者を想定した規定で、個人が自分のために価格監視をする行為を想定した条文とは読めません。

そして最も重要な点。著作権法は著作権の問題しか解決しません。30条の4をクリアしても、業務妨害罪も規約違反も、何ひとつ消えません。

7-6. 「hiQ判決でスクレイピングは合法になった」という誤用について

米国の hiQ Labs v. LinkedIn(第9巡回区、2022年)を根拠に「スクレイピングは合法」と書く記事があります。購入botの文脈では、これは完全な誤用です。理由が4つあります。

  1. 米国第9巡回区の判断であり、日本法には一切適用されません
  2. 判断対象はログイン不要の公開ページのみ。判決自身が「公開ウェブサイトの特徴は、公開部分にアクセス制限が無いことだ」と述べています。ログインが必要な領域は射程外です
  3. 手続上は予備的差止命令の維持であって、本案の最終判断ではありません
  4. 判断されたのはCFAA(不正アクセス系の刑事法)に当たらないという点のみ。契約違反の責任は否定されていません

購入botは、定義上ログインを必要とします。「門が明確に存在する領域」で動くものなので、この判決の射程には最初から入っていません。

7-7. 参考:米国にはbot専用の法律がある

米国にはBOTS Actがあり、FTCは2021年1月22日に初の執行を発表しました。3社のチケットブローカーに対し、合計3,160万ドルの民事制裁金判決(支払能力を理由に大部分が執行猶予され、実支払は約370万ドル)。購入枚数は15万枚を超えていました。

行為態様が、自作botの実装とほぼ同型である点に注目してください。

  • 自動購入ソフトウェアでチケットを検索・確保
  • IPアドレスを秘匿するソフトウェアの使用
  • 数百の架空アカウントとクレジットカード(購入枚数の制限を回避するため)

日本に同種の法律はありませんが、自動購入bot自体を理由とした日本国内の刑事摘発事例は、今回の調査では確認できませんでした。これは「事例がない」という断定ではありません。確認できなかった、というのが正確な記述です。

8. AIにコードを書かせることの規約

前の版のタイトルは「コードは全部AIに書かせる」でした。ここも訂正が必要です。

8-1. Anthropicは「他プラットフォームの規約を回避する行為」を名指しで禁止している

Anthropic Usage Policyには、次の禁止事項があります。

他のプラットフォームやサービスのガードレールまたは規約を回避する行為

ECサイトの規約に反するbotを書かせる行為は、この条項の射程に入ります。違反時の措置は、アクセスの制限・停止・終了です。

8-2. Cursorは大文字で「あなたの全責任」と宣言している

Cursorの利用規約14条は、全大文字でこう書かれています。

本サービスの提案をいかなる形で利用する場合も、それはあなた自身のリスクにおいて行われるものであり、いかなる提案も真実の出典として依拠しないことに同意するものとします

1.7条も「この機能の利用から生じるいかなる影響についても、あなたが単独で責任を負う」と全大文字で規定しています。法律文書で大文字にするのは、強調が法的に必要とされる場合です。

8-3. OpenAIは「法的要件の代替物ではない」と明示している

OpenAI Usage Policiesにはこうあります。

本ポリシーは、人々がAIをどう使うべきかに影響を与えるべき法的要件・職業上の義務・倫理的義務の代替物ではありません

8-4. 三社に共通する構造

読み比べると、構造が見えてきます。

  1. その用途自体を禁止する
  2. 結果責任を利用者に全面的に帰属させる

正確に書くと、三社に共通するのは②だけです。①を名指しで持っているのはAnthropicで、CursorとOpenAIは「他社サービスの規約に違反する用途」を個別条項では禁じていません(OpenAIが回避を禁じているのは「自社の」安全機構です)。

それでも結論は変わりません。「AIに書かせれば責任を回避できる」は、規約の文言レベルで成立しません。三社とも、結果責任の所在を利用者に置いているからです。AIは道具の提供者であり、規約違反や刑事責任の主体は、キーボードを叩いた人間です。

[LP]

9. 実行環境:どこで動かすか

9-1. Linux VPSの月額(2026年8月時点・税込)

この記事のスクリプトはLinuxで動きます。Windows専用の市販ツールと違い、選択肢が広く、そのぶん安い。各社の公式料金ページで確認しました。

サービス プラン 月額(通常料金)
KAGOYA CLOUD VPS 1GB / 1コア 550円(月額上限)
さくらのVPS 512MB(石狩) 643円
さくらのVPS 1GB(石狩) 880円
さくらのVPS 2GB(石狩) 1,738円
ConoHa VPS 512MB 751円
ConoHa VPS 1GB 1,065円
ConoHa VPS 2GB 2,033円
Xserver VPS 2GB 2,200円

この記事のスクリプト(requests + BeautifulSoup)なら、512MBで足ります。月550〜880円の帯です(最安はKAGOYAの1GB・550円)。Playwrightでブラウザを立ち上げる構成にすると2GB前後が現実的な下限になり、月1,700〜2,200円に上がります。ブラウザを使わない設計にすると、費用が3分の1になるということです。

なお、ConoHaなどが提示している「月293円から」といった価格は36か月契約の前払い価格です。上の表は通常料金で揃えています。

9-2. GitHub Actionsの無料枠は使えるか

「VPSを借りずにGitHub Actionsで回せないか」という発想は自然です。無料枠は月2,000分あります。結論から書くと、在庫監視には使えますが、購入には構造的に間に合いません。

GitHubの公式ドキュメントに、はっきり書かれています。

schedule イベントは、GitHub Actions のワークフロー実行が高負荷の期間中に遅延することがあります。高負荷の時間帯には毎時0分が含まれます。負荷が十分に高い場合、キューに入ったジョブの一部は破棄されることがあります

スケジュールされたワークフローを実行できる最短の間隔は5分に1回です

最短5分、毎時0分前後は遅延、負荷次第でジョブごと破棄。数十秒で売り切れる商品に対して、この実行基盤は成立しません。無料枠の「回数」は潤沢ですが、購入botに必要なのは回数ではなくタイミングの精度です。

ただし、1日数回の価格チェックや、在庫の記録を取る用途には十分です。用途を選べば無料で回せます(パブリックリポジトリで標準ランナーを使う場合、そもそも実行時間の枠を消費しません)。

9-3. 常時稼働の設定

VPSで動かす場合、落ちたときに自動で復帰する仕組みを入れておきます。systemdのユニットファイルはこれだけです。

[Unit]
Description=Stock watcher
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/bot
EnvironmentFile=/home/ubuntu/bot/.env
ExecStart=/home/ubuntu/bot/.venv/bin/python /home/ubuntu/bot/stock_watch.py
Restart=on-failure
RestartSec=30

[Install]
WantedBy=multi-user.target

3箇所、意図があります。

Restart=on-failurealways ではない)。ここが重要です。always にすると、スクリプトが「robots.txtが許可していないので終了します」「5回連続で失敗したので停止します」と自分で判断して止まっても、systemdが30秒後に問答無用で再起動します。せっかく入れた「自分で止まる」仕組みが無効になり、30秒ごとに起動しては終了する動作を延々と繰り返すことになります。上のコードが意図した停止を終了コード0で返しているのは、on-failure と組み合わせるためです。

Wants= と After= の両方を書く。systemdの公式ドキュメントは、network-online.target について両方を指定するよう明記しています。After= だけだとターゲット自体が起動対象に入らず、順序指定が効きません。

RestartSec=30短くすると、エラーで落ちるスクリプトが高速で再起動を繰り返し、結果的に相手のサーバーを連打します。10秒に設定している解説をよく見ますが、監視用途では30秒以上を勧めます。

10. 壊れたときの直し方

10-1. セレクタは必ず壊れる

ECサイトはA/Bテストとbot対策のためにHTML構造を頻繁に変えます。「壊れるかどうか」ではなく「いつ壊れるか」の問題です。

だからこの記事のコードは、XPathやCSSセレクタを使わず、ページ全体のテキストに「在庫あり」を示す語が含まれるかで判定しています。粗い方法ですが、DOM構造の変更に強い。

ただし、5-3の⑤で書いたとおり、肯定形で書くことが前提です。「在庫切れの語が無いから在庫あり」と書くと、ページ構造が変わったときに壊れるのではなく、壊れたことに気づけない形で誤通知します。判定できないときは None を返し、通知せずログに残す。これが「壊れ方を選ぶ」ということです。

10-2. 壊れたことに気づく仕組み

いちばん怖いのは、動かなくなることではなく、動かなくなったことに気づかないまま放置されることです。対策は2つです。

定期的な生存通知。1日1回、「稼働中」とだけSlackに投げる処理を足します。通知が来なくなったら、それが異常の合図です。

ページのハッシュ値の監視。取得したHTMLのSHA-256を保存しておき、前回と大きく変わったら通知する。サイト側のリニューアルを、判定が壊れる前に検知できます。

10-3. AIに直させるときの投げ方

エラーが出たときにAIへ渡すべき情報は、次の4点です。この4つが揃っていないと、AIは推測で答えます。

  1. エラーメッセージの全文(一部だけ切り取らない。トレースバックごと渡す)
  2. 実行環境(OS、Pythonのバージョン、ライブラリのバージョン)
  3. 期待した動作と、実際の動作
  4. 直前に変更した箇所

そして、返ってきたコードをそのまま本番に入れないでください。「この修正で何が変わるか、1行ずつ説明して」と聞き返すのが、結局いちばん速い。

11. よくある質問

Q1. Pythonをほとんど書けなくても作れますか

この記事のスクリプトは、コピーして環境変数を2つ設定すれば動きます。ただし「動かす」ことと「運用する」ことは別です。相手のサイトは変わり続けるので、いずれ必ず直す場面が来ます。そのとき、自分が何を動かしているのか読めないと、直せません。AIに書かせるにしても、読めることは必要です。

Q2. 購入まで自動化しているツールが売られていますが、あれは動くのですか

市販ツール6本を公式サイトで1本ずつ確認したところ、2026年8月時点で「複数のECサイトを横断して自動で買う」機能の稼働を確認できたものは1本もありませんでした。最も有名な1本は開発・サポート終了が告知され、機能表では全自動注文が全サイトで不可になっていました。詳細はこちらの記事にまとめてあります。

Q3. Seleniumを使えばbot検知を回避できますか

逆です。W3C WebDriver仕様が navigator.webdriver を「WebDriverによって制御されていることを文書に伝えるための標準的な方法」として定義しています。自動操作であることを申告する仕組みが、規格として組み込まれています。前の版はここを逆に書いていました。訂正します。

Q4. 自分のIDでログインするだけなら、不正アクセスにはなりませんよね

不正アクセス禁止法の定義は「他人の識別符号を入力して」なので、自分のIDでの自動ログインは同法違反にはなりません。ただし、規約違反によるアカウント停止、民事上の責任、態様次第で刑法233条・234条の2は、いずれも残ります。「不正アクセスにならない」と「適法である」は違います。

Q5. アクセス間隔を何秒にすれば安全ですか

安全と言い切れる数字はありません。Librahack事件で逮捕された方のクローラは、1秒に1回でした。専門家が「礼儀正しい」と評価する水準です。それでも20日間勾留されています。

この記事のコードがrobots.txtの Crawl-delay を読みに行くのは、そのためです。自分で「安全な数字」を決めるのではなく、相手が明示した数字に従うのが、いちばん筋が通ります。指定が無い場合、監視用途なら5分間隔で十分です。

Q6. VPSは必須ですか

自宅PCを起動しっぱなしにできるなら不要です。ただし停電、回線断、OSの自動更新による再起動で止まります。監視の価値は「見ていない時間を埋めること」なので、止まると価値がゼロになります。月550〜880円で解決するなら、借りたほうが早いというのが率直なところです。

Q7. スマホだけで動かせますか

スクリプト自体はLinuxサーバー上で動くので、スマホは「通知を受け取る端末」になります。Slackアプリを入れておけば、それで完結します。スマホ上でbotを動かす必要はありません。

Q8. 結局、購入まで自動化することはできないのですか

技術的に不可能だとは言いません。ただ、次の全部を引き受ける必要があります。公式APIが無い経路を通ること。robots.txtで名指しで拒否されている導線を通ること。W3C仕様が定めた自動操作の申告を無効化すること。二要素認証とパスキーを迂回すること。そして、規約違反であることを承知したうえで、アカウントを失うリスクを負うこと。

それだけのものを引き受けて手に入るのが、「通知を見て自分で押すより、数十秒速い」という差です。割に合うかどうかは、ご自身で判断してください。この記事は、割に合わないと考えています。

まとめ

  1. 日本の主要EC5サービスを確認しましたが、一般消費者が公式APIで購入できるものは1つもありませんでした。公開APIは「検索・取得」か「出品者側の受注処理」のどちらかです。
  2. Amazonのrobots.txtは /gp/cart /ap/signin /gp/item-dispatch handle-buy-box を名指しで拒否し、101個のUAブロックのうち100個が全面禁止。そこにはScrapyもClaudeBotも GoogleAgent-Shopping も含まれます。
  3. Seleniumが検知されるのは実装の粗さではなく、W3C標準が「自動操作されている」と申告する仕組みを定めているからです。前の版の記述は逆でした。訂正します。
  4. reCAPTCHA v3は警告を出さずスコアだけ下げます。Akamaiは在庫の買い占めを名指しで防御対象にし、最初のリクエストから評価しています。
  5. Yahoo! JAPAN IDはパスワードのみのログインを順次終了します。SMSは別デバイスに届き、パスキーは秘密鍵が認証器から出ません。購入自動化の終わりは、規約ではなく認証方式の側から来ています。
  6. Librahack事件では、1秒に1回という礼儀正しいクローラを書いた人が20日間勾留されました。真因は相手側システムの欠陥で、ベンダーは後に公式に謝罪しています。「違法かどうか」と「逮捕されるかどうか」は別の軸です。
  7. 「AIに書かせれば責任を回避できる」は成立しません。Anthropicは「他プラットフォームの規約を回避する行為」を名指しで禁止し、Cursorは大文字で「あなたの全責任」と宣言しています。
  8. だからこの記事は在庫監視と通知までを作り、購入ボタンは人間が押す設計を勧めます。壊れず、間に合い、説明がつきます。月550〜880円のVPSで動きます。

最後に、このブログが繰り返し書いていることを、ここでも書きます。

代替されるものは安くなり、代替されないものだけが残ります。「速く買う」は機械に代替された時点で、誰にとっても優位ではなくなった能力です。全員が同じ道具を持てば、その道具は差になりません。残るのは、対策をかいくぐるための追加コストと、アカウントを失うリスクだけです。

一方で、何を仕入れるかの判断、検品と説明文の質、返品の少なさ、続く取引先。このあたりは、まだ機械に置き換わっていません。botに任せるべきなのは、そちらに使う時間を作るための「見張り」だと考えています。

この記事で参照した情報源

すべて一次情報にあたって確認しました。ツール販売者のアフィリエイト記事、せどりスクールの教材、「評判」系サイトは使用していません。

種別 参照した情報源
API Amazon SP-APIAmazon Creators API楽天ウェブサービスYahoo!ショッピングAPIヤフオク!API終了告知
robots.txt https://www.amazon.co.jp/robots.txt(2026年8月時点、全491行を取得して確認)
bot検知 W3C WebDriver仕様reCAPTCHA v3Cloudflare Bot ScoreAkamai Bot Manager
認証 Yahoo! JAPAN ID パスワードのみのログイン終了告知(2026年4月14日)
規約 楽天会員規約Anthropic Usage PolicyOpenAI Usage PoliciesCursor Terms of Service
法令 e-Gov法令検索:刑法不正アクセス禁止法著作権法。刑名の一本化は刑法等の一部を改正する法律(令和4年法律第67号)、2025年6月1日施行
Librahack事件 INTERNET Watch「三菱電機ISが岡崎市立図書館事件で謝罪」(2010年11月30日)
米国 FTC プレスリリース(2021年1月22日)/hiQ Labs v. LinkedIn, No. 17-16783(第9巡回区、2022年4月18日)
実行環境 さくらのVPSConoHa VPSXserver VPSKAGOYA CLOUD VPSGitHub Actions ドキュメント

本記事は法律の専門家によるものではありません。条文と公表資料に基づく整理であり、個別の事案についての判断は弁護士にご相談ください。

関連する内容は、こちらにまとめています。

[LP]

無料メール講座・全14通
壊れた中古品を利益に変える考え方

読んでみる

── 無料メール講座(全6章・14通)──
真面目に働いても給料が上がらないのは、
あなたの能力ではなく
買い手が一社しかいないからです。
売り手が多数いて、買い手が一者しかいない市場では、
価格は買い手の内部規則で決まります。
転職しても、買い手が別の一社に変わるだけ。
構造は変わりません。
必要なのは努力の量でも我慢でもなく、
会社以外に
「自分の判断にお金を払う相手」
を一つ持つこと。
月3万円で構いません。
それだけで、組織との距離の取り方が変わります。
動かない録画機器を1,000円以下で仕入れ、
中古ドライブ(部品代2,000円)に交換。
ついでに容量を増やして15,000円で販売。
── 20年の現場から、実例の一部
この無料講座で扱う内容
  • 壊れた商品のどこに価格差が眠るのか
    「歪みの4類型」
  • 手数料・送料・作業時間まで含めた
    「1件の利益計算式」
  • 絶対に手を出してはいけない
    「除外リスト7項目」
  • 6日目には、元手ゼロで
    今週できる実践ワーク(約70分)
※手を動かさずに稼ぐ方法をお探しの方には向きません。
そのことも講座内で明記しています。
20年
現場での実践
全14通・2週間。
1通3〜5分で、通勤中にも読み切れる分量です。
まずは14通の無料講座で、
「第二の買い手」という考え方を確認してください。
登録30秒・無料・いつでも解除できます


「第二の買い手」を作る14通を無料で読む →

※後半に有料商品のご案内があります。
第1通の冒頭で明記します。
転売

コメント