Qoo10とは——日本のEC市場での存在感
Qoo10(キューテン)は韓国・シンガポール発のグローバルECプラットフォームです。日本では特にコスメ・食品・韓国関連商品のカテゴリで高い人気を誇り、「メガ割」などのセールイベント時には通常の数倍のトラフィックが発生します。
仕入れ業者・セラーにとって、Qoo10のセール情報をリアルタイムに把握することはビジネス上重要です。本記事ではPuppeteerを使ったQoo10のセール情報収集と、公式APIが存在する場合の活用方法を解説します。
セール情報の収集——Puppeteerアプローチ
Qoo10のセールページはJavaScriptで動的にレンダリングされるため、Puppeteerが有効です。
const puppeteer = require('puppeteer');
/**
* Qoo10のセールページから商品情報を取得
*/
async function scrapeQoo10Sale(category = 'beauty') {
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 900 });
await page.setUserAgent(
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ' +
'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36'
);
try {
const url = `https://www.qoo10.jp/gmkt.inc/Goods/GoodsList.aspx?keyword=${encodeURIComponent(category)}&sort=sale`;
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
// 商品リストが読み込まれるまで待機
await page.waitForSelector('.goods_list', { timeout: 10000 }).catch(() => {});
// 商品データを取得
const items = await page.evaluate(() => {
const elements = document.querySelectorAll('.goods_item');
return Array.from(elements).map(el => ({
name: el.querySelector('.goods_name')?.textContent?.trim() ?? '',
salePrice: el.querySelector('.sale_price')?.textContent?.trim() ?? '',
originalPrice:el.querySelector('.org_price')?.textContent?.trim() ?? '',
discountRate: el.querySelector('.discount')?.textContent?.trim() ?? '',
url: el.querySelector('a')?.href ?? '',
imageUrl: el.querySelector('img')?.src ?? '',
}));
});
return items.filter(item => item.name);
} finally {
await browser.close();
}
}
セール商品の価格変化追跡
const fs = require('fs');
/**
* 前回取得データと比較して値下がりした商品を検出
*/
function detectPriceDrops(currentItems, historyPath) {
const history = fs.existsSync(historyPath)
? JSON.parse(fs.readFileSync(historyPath, 'utf8'))
: {};
const drops = [];
currentItems.forEach(item => {
const prevPrice = history[item.url];
const currPrice = parsePrice(item.salePrice);
if (prevPrice && currPrice < prevPrice) {
drops.push({
name: item.name,
prevPrice: prevPrice,
currPrice: currPrice,
dropAmount: prevPrice - currPrice,
dropRate: ((prevPrice - currPrice) / prevPrice * 100).toFixed(1),
url: item.url,
});
}
// 履歴を更新
history[item.url] = currPrice;
});
fs.writeFileSync(historyPath, JSON.stringify(history, null, 2));
return drops;
}
function parsePrice(priceStr) {
return parseInt(priceStr.replace(/[^\d]/g, ''), 10) || 0;
}
メガ割期間の特別収集
メガ割は四半期ごとに開催される大型セールで、割引率が特に大きくなります。
const cron = require('node-cron');
// 通常時:1日1回
let currentSchedule = cron.schedule('0 8 * * *', async () => {
await runCollection('daily');
}, { timezone: 'Asia/Tokyo' });
/**
* メガ割期間は頻度を上げる
*/
function startMegaSaleMode() {
currentSchedule.stop();
// メガ割期間中:30分ごとに収集
currentSchedule = cron.schedule('*/30 * * * *', async () => {
await runCollection('mega_sale');
const drops = await detectSignificantDrops();
if (drops.length > 0) {
await notifySlack(formatDropAlert(drops));
}
}, { timezone: 'Asia/Tokyo' });
console.log('メガ割モード: 30分間隔で収集中');
}
function formatDropAlert(drops) {
const lines = drops.map(d =>
`• ${d.name.substring(0, 30)}\n ¥${d.prevPrice.toLocaleString()} → ¥${d.currPrice.toLocaleString()} (▼${d.dropRate}%)`
);
return `:fire: *メガ割 価格下落アラート*\n\n${lines.join('\n\n')}`;
}
データをLaravelへ送る
const axios = require('axios');
async function syncToLaravel(items) {
const apiUrl = process.env.LARAVEL_API_URL;
const token = process.env.LARAVEL_API_TOKEN;
const response = await axios.post(
`${apiUrl}/api/v1/qoo10/items/sync`,
{ items },
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
timeout: 30000,
}
);
return response.data;
}
// Laravel側のAPI
class Qoo10ItemController extends Controller
{
public function sync(Request $request): JsonResponse
{
$validated = $request->validate([
'items' => ['required', 'array'],
'items.*.name' => ['required', 'string'],
'items.*.salePrice' => ['required', 'string'],
'items.*.url' => ['required', 'url'],
]);
$upserted = 0;
foreach ($validated['items'] as $item) {
Qoo10Item::updateOrCreate(
['url' => $item['url']],
[
'name' => $item['name'],
'sale_price' => $this->parsePrice($item['salePrice']),
'last_seen_at' => now(),
]
);
$upserted++;
}
return response()->json(['upserted' => $upserted]);
}
}
まとめ
Qoo10のセール情報自動収集は、仕入れ判断・価格設定・競合モニタリングに役立つツールです。Puppeteerによるスクレイピングとcronによる定期実行、Slackへのアラート通知を組み合わせることで、見逃しのない自動監視体制を構築できます。
EC自動化・データ収集ツールの開発についてはお気軽にご相談ください。