楽天と Yahoo! の商品 API を横断して「同じ商品の最安はどこか」を出そうとすると、価格計算より前に「そもそも同じ商品をどう同定するか」で詰まります。JAN(商品のバーコード番号)で結べれば早いのですが、ここに非対称があります。
-
Yahoo! ショッピング:
jan_codeパラメータで JAN を正引きでき、レスポンスにもjanCodeが乗る。 -
楽天 商品検索 API: JAN 専用パラメータが無い。
keywordに JAN 文字列を投げる間接突合になり、JAN 自体は商品説明文(itemCaption)に紛れていることが多い。
つまり Yahoo! は信頼して JAN 確定に使えますが、楽天は「keyword=JAN で出たヒットが本当にその商品か」を後から確かめないと使えません。この前提でマッチングを確実性で 3 段階に分けた設計を共有します。
確実性で 3 段階に分ける
全件を JAN で突合しようとせず、確実に結べるものだけを最上段に置きます。下段ほど確証が弱いので、横断比較と通知の権利を絞ります。
| 段 | 方法 | 横断比較 | 通知 |
|---|---|---|---|
| ① JAN 突合 | Yahoo! 正引き+楽天 keyword=JAN を確証スコアで判定 | 確定したものだけ | 出す |
| ② 名前ファジー一致 | 型番・容量・ブランドでスコア化→候補提示→ユーザー確認 | 確認後 | 確認済みのみ |
| ③ マッチ不能 | 単一モール(URL 貼り付け等)として扱う | しない | 単一モールの値下げのみ |
最小実装: 楽天ヒットの確証スコア
楽天の 1 ヒットごとに加点・減点してスコアを出し、閾値で 3 つに割ります。0.8 以上を確定、0.5 以上を要確認(通知不可)、それ未満は破棄です。
type Grade = "high" | "mid" | "low";
function janMatchScore(
hit: { itemName: string; itemCaption: string; itemPrice: number },
jan: string,
range: { min: number; max: number } | null,
): { grade: Grade; score: number } {
let s = 0;
// (1) 名前か説明文に JAN 文字列を含む(最も直接的な証拠)
if (hit.itemCaption.includes(jan) || hit.itemName.includes(jan)) s += 0.5;
// (2) 製品検索 API の価格レンジ妥当性
if (range) {
const lo = range.min * 0.7; // 下振れ30%許容(型落ち・並行品)
const hi = range.max * 1.5; // 上振れ50%許容(送料込・セット品)
s += hit.itemPrice >= lo && hit.itemPrice <= hi ? 0.3 : -0.4;
}
// (3) 型番か容量が読み取れる
if (/[a-z]+-?\d+/i.test(hit.itemName) || /\d+\s*(ml|g|枚|個)/i.test(hit.itemName)) s += 0.2;
const score = Math.max(0, Math.min(1, s));
const grade: Grade = score >= 0.8 ? "high" : score >= 0.5 ? "mid" : "low";
return { grade, score };
}
確証の弱いヒットを確定扱いしないこと(=low を捨て、mid を通知させないこと)が、別商品を同一商品として束ねる「誤名寄せ」を防ぐ一番効く制御でした。
ハマりどころ
-
製品検索 API(
productCode=JAN)が 404 を返しがち。価格レンジが取れないヒットが多く、(2) のレンジ加点に頼り切れません。そこで横断相手(Yahoo! の同 JAN ヒット)の代表価格を基準に、楽天ヒットの名前に相手の型番トークン(英数混在 5 字以上=ほぼ一意)が含まれ、かつ価格が相手価格の ±40% 以内なら確定へ昇格、という確証を足しました。型番一致だけだと偶然衝突するので、価格バンドで弾きます。 -
ファジー段は自動確定しない。型番・容量・ブランドが一致してもスコアは確率でしかないので、候補提示までで止め、ユーザーが「同じ商品」と確認したものだけをマスタ化します。誤って結ばれた組はワンタップで除外(突合候補から永久に外す)。通知が発火するのは JAN 確定とユーザー確認済みだけにしています。
まとめ
JAN が揃わない前提でも、確実性で 3 段階に落とせば横断比較は成立します。確実に JAN で結べるものだけを確定層に置き、確証の弱いものは候補提示、結べないものは単一モール通知へ。無理に全件 JAN 突合しないことが誤名寄せを防ぎます。
ポイント還元込みの実質価格の計算、誤マッチ報告の蓄積、通知の発火条件まわりの運用知見は Aulvem 本家にまとめました → 楽天APIがJANを返さない問題と、横断マッチングの3段階