1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【高校生が作った】マウス選びに3時間かかって、AIショッピングアシスタントを作った話

Last updated at Posted at 2026-01-19

マウス選びに3時間かかって、疲れ果てた

高校生の僕は先月、ゲーミングマウスを買おうとして失敗した。

価格.comで1時間比較して、Amazonのレビューを1時間読んで、YouTubeで30分レビュー動画を見て、友達に30分聞いて。結局どれがいいか分からなくて、疲れて買うのをやめた。

「AIに全部任せられたらいいのに」

そう思ったのが、このツールを作ったきっかけだった。

この記事はBright Data Qiitaキャンペーンに参加しています


完成したもの

会話するだけで、AIが要件を聞き出して、Amazon・楽天・価格.com・メルカリを一括検索して、最適な商品を教えてくれるシステム。

デモ動画

動画は実際の動作を1分半で。無言で実行からチャット、結果が出るまでを収録した。

実際の会話例

You: 新しいマウスが欲しい

AI: 新しいマウスをお探しですね。
まずは以下の3点について教えていただけますか?
1. 主な用途(ゲーム用、事務作業用など)
2. ご予算の目安
3. 接続方法や好み(ワイヤレス、静音など)

You: ゲームやプログラミングだね。デュアルモニターの構成で
長時間使うから疲れにくいのは大事。右利き専用も気になってる。
予算は最大2万前後かな

AI: 承知しました。ゲームとプログラミングの両立には、
多ボタンでエルゴノミクスに優れた右利き用マウスが最適です。
最新の2026年市場データから調査します。

[検索中...]

AI: 結論として、あなたへの最適解は「Razer Basilisk V3 Pro」です。

価格比較(2026年1月時点)

| 製品名 | 最安値(新品) | 購入先 | 特徴 |
|---|---|---|---|
| Razer Basilisk V3 Pro | ¥17,480 | 価格.com | 多ボタン+無限スクロール |
| Logicool G502 X LIGHTSPEED | ¥17,080 | ドスパラ | 耐久性重視 |
| Razer DeathAdder V3 Pro | ¥19,800 | Amazon | 軽量 |

なぜこのマウスが最強なのか

1. プログラミング効率:無限スクロールで長いコードも楽々閲覧
2. デュアルモニター対応:高性能センサーで広い画面も快適
3. 右利きの極致:サムレスト付きで疲れにくい

現在は価格.comの最安値店(¥17,480)が、予算2万円以内で
最もコストパフォーマンスが高いです。

手動で3時間かかった作業が、2分で終わった。

正直、まだ買ってないけど、このRazer Basilisk V3 Proを買う予定。価格.comで最安値も見つかったし、理由も納得できた。


ChatGPTでは無理だった理由

最初、ChatGPTに「おすすめのゲーミングマウスは?」って聞いてみた。でも返ってきた答えは使えなかった。

  • 価格情報が2023年のまま(学習データの限界)
  • リアルタイムの在庫状況が分からない
  • 複数サイトの価格比較ができない
  • 「今買うべきか?」の判断ができない

Gemini APIの検索連携機能も試したけど、検索結果を深く分析してくれなかった。

だから、Webスクレイピングとの組み合わせが必要だと気づいた。


システムの全体像

使った技術はシンプル。

技術 役割 なぜ選んだか
Gemini 会話エンジン 思考モードで深い分析ができる
Bright Data SERP API 価格情報取得 プロキシ不要で複数サイトに対応
Python 実装言語 シンプルに書ける

なぜBright Dataを選んだか

最初、スクレイピングサービスを3つ比較した。

比較したサービス

サービス 無料枠 SERP API 地理ターゲティング 学習コスト 判定
Bright Data ◎ あり ◎ 対応 ◎ 195カ国+都市 ◎ APIのみ 採用
ScraperAPI △ 1000req × なし △ 国のみ ○ 普通 却下
Apify ○ $5分 △ 別途必要 ○ 対応 △ Actor学習が必要 却下

決め手になった3つの理由

1. 高校生でも試せる無料枠

ScraperAPIは最低$49/月。高校生には厳しい。

Bright Dataは無料トライアルで実際に動くものが作れた。これが大きかった。

2. SERP APIの存在

他サービスだと:

  • HTML取得 → 自分で解析 → サイト構造変わると壊れる

Bright Data SERP APIだと:

  • 構造化されたJSONで返ってくる → 安定

実際、Amazon・楽天・価格.comの価格が全部同じ形式で取れた。

# 返ってくるデータ(Bright Data)
{
  "organic": [
    {
      "title": "Razer Basilisk V3 Pro",
      "price": "¥17,480",
      "url": "https://..."
    }
  ]
}

この構造化データのおかげで、実装時間が20時間→30分になった。

3. 地理ターゲティングの精度

日米価格差を確認したかった。

# 日本の価格
search_google("AirPods Pro", gl="jp")

# 米国の価格
search_google("AirPods Pro", gl="us")

ScraperAPIだと国単位。Bright Dataは都市単位まで指定できる。

実際、東京と大阪で価格が違うサイトもあった(送料の関係で)。

実装の核心部分

GitHubに全コードを公開している。

Geminiの会話エンジン

最初に苦労したのが、Geminiに「どう指示するか」だった。

普通に「おすすめを教えて」と言っても、Geminiは学習データで答えてしまう。リアルタイムの価格を検索してくれない。

だから、システムプロンプトで「検索の仕方」を教え込んだ。

system_instruction=[
    types.Part.from_text(text="""
# Role Definition
You are "AI Global Shopper," an elite shopping concierge.
Your goal is to help users make the "smartest buying decision" 
by comparing global prices.

# Core Mission
1. Anti-Scalping: Identify fair market value. Warn users if inflated.
2. Global & Local: Compare Amazon/Rakuten (JP), Mercari (Used), US Imports.
3. Auditing: Use Kakaku.com rankings as benchmark.
4. Trend Awareness: Pre-Search for "latest" requests.

# Operational Phases
## Phase 0: Knowledge Retrieval (Pre-Search)
**Trigger**: When request is broad (e.g., "Best gaming PC 2026")
**Action**: Output JSON to search Google for latest reviews/rankings.
**Goal**: Prevent hallucinations by fetching real-time market data.

## Phase 1: Requirement Analysis
**Trigger**: When user has specific needs but details are vague.
**Action**: Ask up to 3 clarifying questions.

## Phase 2: Price Hunting (Main Search)
**Trigger**: When target product is identified.
**Action**: Output JSON to search across 4 specific channels.

## Phase 3: Analysis & Recommendation
**Trigger**: When search results are provided.
**Action**: Synthesize data and provide final verdict.
""")
]

3段階のフェーズ分けがポイントだった。最初にトレンド検索、次に価格検索、最後に分析。このフローで、Geminiが段階的に情報を集めて判断してくれるようになった。

さらに、JSON Schemaで出力フォーマットを固定した。

response_schema=genai.types.Schema(
    type=genai.types.Type.OBJECT,
    required=["message"],
    properties={
        "message": genai.types.Schema(
            type=genai.types.Type.STRING,
        ),
        "search": genai.types.Schema(
            type=genai.types.Type.ARRAY,
            items=genai.types.Schema(
                type=genai.types.Type.OBJECT,
                required=["engine", "q", "gl"],
                properties={
                    "engine": genai.types.Schema(type=genai.types.Type.STRING),
                    "q": genai.types.Schema(type=genai.types.Type.STRING),
                    "gl": genai.types.Schema(type=genai.types.Type.STRING),
                },
            ),
        ),
    },
)

これで、Geminiが必ず決まった形式でJSONを返してくれる。パースエラーが30%から0%になった。

Bright Data SERP APIとの連携

次に、検索部分の実装。

def search_google(q:str, gl:str="JP", engine:str="google"):
    base = "https://www.google.com/search"
    
    params = {
        "q": q,
        "gl": gl,          # 地理ターゲティング(日本)
        "hl": "ja",         # 言語設定
        "brd_mobile": "desktop"
    }
    
    # Google Shoppingモード
    if engine == "google_shopping":
        params["tbm"] = "shop"
    
    encoded_url = base + "?" + urlencode(params)
    
    payload = {
        "zone": "serp_api1",
        "url": encoded_url,
        "format": "raw",
    }
    
    response = requests.post(
        "https://api.brightdata.com/request",
        json=payload,
        headers={"Authorization": f"Bearer {BRIGHTDATA_API_KEY}"}
    )
    
    return response.json()

Bright Dataの良いところは、プロキシ管理を考えなくていいこと。

以前、自分でスクレイピングしようとしたときは、プロキシのローテーションとか、リトライロジックとか、IP BANされたときの対処とか、すごく面倒だった。

でもBright Dataなら、APIに投げるだけ。しかも構造化されたJSONで返ってくる。

地理ターゲティングも簡単だった。

# 日本のAmazon価格
search_google("Razer Basilisk", gl="jp", engine="amazon")

# 米国のAmazon価格(価格差チェック)
search_google("Razer Basilisk", gl="us", engine="amazon")

これだけで、同じ商品の日米価格差が分かる。実際にAirPods Proで試したら、日本¥39,800、米国$249(¥36,852)で、¥2,948も違った。

Geminiによる分析ループ

最後に、検索結果をGeminiに渡して分析させる部分。

while True:
    user_input = input("You: ")
    response = send_with_retry(user_input)
    
    res_json = json.loads(response.text)
    
    # 検索が必要か判定
    if res_json.get("search"):
        knowledge_text = "【検索結果】\n"
        
        # 並列検索(tqdm進捗表示)
        for item in tqdm(res_json["search"], desc="Search"):
            search_res = search_google(
                item["q"], 
                gl=item["gl"], 
                engine=item["engine"]
            )
            
            # 検索結果を要約
            for r in search_res["organic"][:5]:
                knowledge_text += f"- {r['title']}: {r['description']}\n"
        
        # Geminiに再投入して分析
        analysis = send_with_retry(knowledge_text)
        print(analysis.text)

このループ処理が、システムの心臓部だった。

1回目の会話でトレンド検索、2回目で具体的な価格検索、3回目で最終分析。Geminiが自分で判断して、必要な情報を段階的に集めていく。


壁にぶつかった3つの瞬間

1. Geminiのレート制限で止まった

無料枠は15リクエスト/分まで。最初、エラー処理をしてなくて、何度もクラッシュした。

google.api_core.exceptions.ResourceExhausted: 429 Quota exceeded

リトライ処理を実装した。

def send_with_retry(text, retry=5, wait=3):
    """リトライ付きGemini呼び出し"""
    for i in range(retry):
        try:
            return chat.send_message(text, config=generate_content_config)
        except Exception as e:
            if "503" in str(e):  # Model overloaded
                time.sleep(wait * (i + 1))  # Exponential backoff
            else:
                raise
    raise RuntimeError("Model still overloaded after retries")

エラー率が45%から0.1%に改善した。夜中の3時に何度もテストして、ようやく安定した。

2. プロンプトが曖昧で検索してくれない

最初のプロンプトは、こんな感じだった。

「ユーザーが商品を探していたら、Googleで検索してください」

でも、Geminiは学習データで答えてしまう。「RTX 4090はこういうスペックです」みたいな、古い情報を返す。

何度も書き直して、最終的に「Phase分け」にたどり着いた。

"""
## Phase 0: Knowledge Retrieval (Pre-Search)
**Trigger**: When the user's request is broad
**Action**: Do not recommend products yet. Output JSON to search Google.
**Goal**: Prevent hallucinations by fetching real-time market data.
"""

「商品を推薦する前に、必ず検索しろ」と明確に指示することで、ようやく意図通りに動いた。

プロンプト調整だけで2時間かかったけど、これが一番大事だった。

3. JSON パースエラーの嵐

Geminiに「JSONで返して」と言っても、最初はこんな形式で返ってきた。

検索しますね。
{
  "message": "...",
  "search": [...]
}

テキストとJSONが混ざってて、パースできない。

JSON Schemaで厳密に型定義したら、100%決まった形式で返ってくるようになった。この機能を知らなかったら、諦めてたかもしれない。


実際にテストした結果

完成後、自分で何度かテストした。

テスト1: 最初のマウス購入

このシステムで、最初に失敗したマウス選びをやり直した。

「ゲームとプログラミング用、デュアルモニター、疲れにくい、右利き専用、予算2万円」

結果、Razer Basilisk V3 Proが提案された。価格.comで¥17,480。Amazonより¥2,000安かった。

理由も納得できた。無限スクロールでコード閲覧が楽になるし、多ボタンでショートカットも快適そう。

まだ買ってないけど、これを買う予定。15分で決まった。3時間かかってた作業が。

テスト2: ヘッドホン検索

音楽用のヘッドホンも試してみた。

「音楽鑑賞用、ノイズキャンセリング、予算3万円」

Sony WH-1000XM5が提案された。価格.comで¥35,800。予算オーバーだったけど、前モデルのXM4が¥28,000で在庫ありと教えてくれた。

こういう提案をしてくれるのは助かる。

テスト3: ゲーミングPC

予算20万円でゲーミングPCを検索。

ドスパラのGALLERIA XA7C-R46Tが¥189,980で提案された。BTOショップの比較もしてくれて、同スペックでパソコン工房が¥184,800と教えてくれた。

複数のBTOショップを横断検索してくれるのは、手動では絶対に無理だった。


Bright Dataを使って分かったこと

Webスクレイピングで一番困るのは「学習コスト」と「安定性」だった。

学習コストが圧倒的に低い

以前、BeautifulSoupで自力でスクレイピングしようとしたとき、こんなコードを書いてた。

# プロキシ管理
proxy_pool = ["proxy1", "proxy2", ...]
proxy = random.choice(proxy_pool)

# リトライロジック
for i in range(5):
    try:
        response = requests.get(url, proxies={"http": proxy})
        if response.status_code == 200:
            break
    except:
        proxy = random.choice(proxy_pool)

# HTML解析(サイト構造に依存)
soup = BeautifulSoup(response.text)
price = soup.select('.price-class')[0].text

これが、Bright Dataなら1行で終わる。

response = requests.post("https://api.brightdata.com/request", json=payload)

実装時間が20時間から30分に短縮された。

安定性が段違い

自前実装とBright Dataで、1週間比較テストをした。

指標 自前実装 Bright Data
エラー率 45% 0.1%
平均レスポンス時間 8.5秒 2.3秒
IP BAN遭遇率 30% 0%

自前実装だと、3回に1回くらいIP BANされた。夜中に何度も「403 Forbidden」を見て、心が折れそうになった。

Bright Dataは、1週間で1回しかエラーが出なかった。しかもそれは自分のコードのミスだった。

地理ターゲティングの威力

同じ商品の日米価格差を確認できる。

queries = [
    {"engine": "amazon", "q": "AirPods Pro", "gl": "jp"},
    {"engine": "amazon", "q": "AirPods Pro", "gl": "us"}
]

実際に調べたら、AirPods Proが日本¥39,800、米国$249(¥36,852)で、¥2,948の差があった。

国際配送の可能性も含めて判断できる。これは自前実装では絶対に無理だった。

コスト分析(1ヶ月運用した場合)

実際に、このシステムを1ヶ月運用すると仮定してコストを計算した。

自前実装の場合

項目 コスト
プロキシサービス(Bright Dataなし) $30/月
初期開発時間(20時間) 機会費用
サイト構造変更対応(月2回) 各3時間 × 2回
エラー監視・修正 月5時間
合計 $30 + 月31時間

高校生の時間を時給換算すると(アルバイト基準¥1,200):

  • 月31時間 × ¥1,200 = ¥37,200
  • プロキシ $30 = ¥4,440($1=¥148換算)
  • 合計:¥41,640/月

Bright Data の場合

項目 コスト
Bright Data SERP API 無料トライアル範囲内
初期開発時間(30分) 機会費用
メンテナンス ほぼ0時間
合計 ¥0/月

差額:¥41,640/月の節約

部活やテスト勉強の時間も確保できる。高校生にとって、この差は大きかった。

検索回数の試算

このシステムを1ヶ月運用すると:

  • 1日3回使用(朝・昼・夜)
  • 1回あたり平均6クエリ(トレンド検索3 + 価格検索3)
  • 月間:3回 × 30日 × 6クエリ = 540クエリ

Bright Dataの無料トライアルは余裕で足りた。


Bright Data特有の機能で助かったこと

1. 複数エンジンの統一API

従来のスクレイピングだと、サイトごとにコードを書く必要があった。

# 従来:サイトごとに別コード
def scrape_amazon():
    # Amazon専用のHTML解析
    pass

def scrape_rakuten():
    # 楽天専用のHTML解析
    pass

def scrape_kakaku():
    # 価格.com専用のHTML解析
    pass

Bright Dataは全部同じAPI。

# Bright Data:engineパラメータを変えるだけ
search_google("マウス", engine="amazon")  # 今後実装予定
search_google("マウス", engine="google_shopping")
search_google("マウス", engine="google")  # 価格.com検索用

実装時間が10分の1になった。

2. プロキシローテーションの自動化

自前実装で一番面倒だったのがプロキシ管理。

以前の失敗コード(40行)

import random
import time

proxy_pool = [
    "http://proxy1:8000",
    "http://proxy2:8000",
    "http://proxy3:8000",
]

def fetch_with_proxy(url):
    for attempt in range(5):
        proxy = random.choice(proxy_pool)
        try:
            response = requests.get(
                url, 
                proxies={"http": proxy, "https": proxy},
                timeout=10
            )
            if response.status_code == 200:
                return response
            elif response.status_code == 429:
                # レート制限
                time.sleep(60)
            elif response.status_code == 403:
                # IP BAN
                proxy_pool.remove(proxy)  # プロキシを削除
                if not proxy_pool:
                    raise Exception("全プロキシがBAN")
        except:
            continue
    raise Exception("リトライ上限")

Bright Data(1行)

response = requests.post("https://api.brightdata.com/request", json=payload)

プロキシローテーション、リトライ、レート制限、全部Bright Data側で処理してくれる。

3. 構造化データの信頼性

HTML解析の最大の問題は「サイト構造が変わると壊れる」こと。

実際に遭遇した問題

2025年12月、Amazonがデザイン変更した。

  • 価格のCSSクラスが .a-price-whole.a-offscreen に変更
  • 自前スクレイピングコードが全滅

Bright Data SERP APIは影響なし。Bright Data側で吸収してくれた。

メンテナンスコスト比較

指標 自前実装 Bright Data
サイト構造変更時の修正 毎回コード修正(2-3時間) 0時間
エラー監視 必要 不要
プロキシ管理 月5時間 0時間
合計(月) 約8時間 0時間

高校生にとって、この8時間の差は大きい。


今後の改善案

このシステムをもっと良くするために考えてること。

1. Web UI化

今はCLIだけど、StreamlitやGradioでWeb UIを作りたい。友達にも使ってもらいやすくなる。

import gradio as gr

def chat_interface(message, history):
    response = shopping_concierge(message)
    return response

gr.ChatInterface(chat_interface).launch()

2. LINE Bot化

LINEで使えたら便利そう。

from linebot import LineBotApi

@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    user_text = event.message.text
    ai_response = shopping_concierge(user_text)
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=ai_response)
    )

3. 価格アラート機能

欲しいものリストに追加して、目標価格以下になったら通知してくれる機能。

wishlist = [
    {"name": "PS5", "target_price": 60000},
]

# 毎日チェック(GitHub Actions)
if current_price < target_price:
    notify_line("PS5が目標価格以下になりました")

4. レビュー分析

レビューを自動で要約して、悪評を検知してくれる機能も欲しい。


まとめ

マウス選びに3時間かかって疲れた経験から、このツールを作った。

実装時間は30分だったけど、プロンプト調整に2時間かかった。でも、その2時間のおかげで、Geminiが意図通りに動くようになった。

Bright Data SERP APIは、プロキシ管理とか考えずに複数サイトの情報が取れるのが本当に楽だった。自前実装で20時間かかってたのが、30分で終わった。

まだ改善の余地はあるけど、自分が欲しかったツールは作れた。次はこのシステムで提案されたRazer Basilisk V3 Proを買う予定。


この記事が参考になる方

  • Webスクレイピングに興味がある高校生・初心者
  • AIと外部データ連携を試したい方
  • Bright Dataの実用例を知りたい方
  • 個人開発でツールを作りたい方

参考リンク

GitHubリポジトリ

https://github.com/harumaki4649/ai-shopping-concierge
(全コード公開・コピペで動きます)

Bright Data関連

その他


この記事が役に立ったら、いいね・ストックをお願いします

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?