Amazon価格チェッカーを作ってみた
こんにちは!ざいやです。
第1週目の「YouTube分析ツール」に続き、今週は実用性を重視して「Amazonの価格チェッカー」を開発しました。ブラウザから登録・管理ができる「1つの完成されたWebアプリケーション」の形に仕上げました。
今週の学びと、直面した壁をどう乗り越えたかの技術的な振り返りをコード付きで共有します!
★今回作ったツールの全体像
今回のアプリは、以下のような3つのパーツが連携して動く仕組みにしました。
管理画面(フロントエンド): ブラウザで開き、最大3品までのURLと目標価格を入力・削除できる画面(Expressで配信)。
データベース(JSONファイル): 登録されたデータを保持する、シンプルな products.json。
巡回バッチ(バックエンド): 1日1回(を想定して)、裏側でPlaywrightを動かしてAmazonをスクレイピングし、値下げを検知したらDiscordに通知するスクリプト。
★直面した大きな壁:Amazonの強固なボット判定
開発の初期段階で最大の壁となったのが、Amazonのセキュリティです。Playwrightで単純に商品ページを開こうとすると、サーバー側でアクセスを完全にブロックされ、30秒でタイムアウト(TimeoutError)してしまいました。裏側では「画像認証(CAPTCHA)」の画面が表示されて困っていました。
【解決策】ステルスプラグインの導入
これを突破するために、自動操縦であることを徹底的に隠蔽するプラグイン(中身はPlaywrightでも動く互換性の高いPuppeteer用のもの)を導入しました。
JavaScript
// batch.js より抜粋:ボット判定を回避する心臓部
const { chromium } = require('playwright-extra');
const stealth = require('puppeteer-extra-plugin-stealth')();
// Playwrightにステルスプラグインを登録(ブラウザの指紋を人間らしく偽装)
chromium.use(stealth);
(async () => {
const browser = await chromium.launch({ headless: true }); // 自動化を見据えて画面非表示
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...', // ブラウザ情報の偽装
locale: 'ja-JP',
});
// (中略)
// ページの全要素を待つと重いため、サーバーから応答が返ってきた瞬間(commit)に次の処理へ進む
await page.goto(url, { waitUntil: 'commit', timeout: 45000 });
await page.waitForTimeout(2000); // 人間らしさを出すための「2秒のタメ」
この「ステルス化」と「人間らしい待機時間の確保」を行うことで、Amazonの強固なガードをすり抜け、安定して最新価格をスクレイピングできるようになりました。
★UX(使いやすさ)へのこだわり:URLの自動クレンジング
3つの商品を登録して動かしてみた際、新たな問題に気づきました。AmazonのURLをブラウザからそのままコピペすると、検索の足跡(パラメータ)や日本語のエンコードによって、1つのURLが何行にも渡る異常に長い文字列になってしまいました。
これだと保存先の products.json が非常に見づらくなり、将来的なバグの原因にもなります。
【解決策】正規表現によるURLのスリム化
ユーザーがわざわざ手動でURLを削らなくても、ツール側が保存する直前に自動で「商品ID(ASINコード)」だけを抜き出すロジックをサーバー側に追加しました。
JavaScript
// index.js より抜粋:URLのデータクレンジング処理
app.post('/api/save', (req, res) => {
const { products } = req.body;
const cleanedProducts = products.map(product => {
if (!product.url) return product;
// 正規表現を使い、URLからAmazon特有の「/dp/【英数字10桁のASINコード】」を抽出
const match = product.url.match(/\/dp\/([A-Z0-9]{10})/i);
if (match) {
// マッチしたら余計なパラメータをすべて削り、最もスリムなURLに強制変換!
product.url = `https://www.amazon.co.jp/dp/${match[1]}`;
}
return product;
});
// 整形した綺麗なデータをファイルに書き込み
fs.writeFileSync(DATA_FILE_PATH, JSON.stringify(cleanedProducts, null, 2), 'utf8');
res.json({ success: true });
});
この工夫のおかげで、どんなに長いURLを貼り付けても、ファイル内には https://www.amazon.co.jp/dp/B0FDKTKVJL のような美しい形で保存されるようになりました。システムが扱いやすいだけでなく、人間が見てもスッキリして気持ちいい構造です。
今週のまとめと次への一歩
少し遅くなりましたが今週は、「自動化の難しさ(ボット対策)」と「入力データの整形(クレンジング)」という、実用的なスクレイピングに不可欠な2つの大きな学びを得ることができました。
目標価格を下回った際に、Discordに「値下げ通知」がピコン!と飛んできた瞬間は、これまでの苦労が吹き飛ぶほど嬉しかったです。
もし今後このツールをさらに進化させるなら、以下の二点に挑戦したみたいと思っています。
プログラム内部で定時実行(毎日夜20時など)を完結させる
価格の履歴を蓄積して、画面にグラフ表示する
3週目は何を作ろうかな。。。
