Yahoo商品検索APIが仕様変更になったので対応
旧APIはレスポンスがXMLで流石に2020年にもなってXMLはないな~と思っていた矢先ついにJSON形式にてレスポンスを返すようになった。
Yahoo商品検索v3
4月末頃に新APIのアナウンスがあり、旧APIの廃止は6月末なので確実に開発者泣かせである。2019年にAmazonのProduct Advertising APIがv5になったが1年半程度は旧APIのサポートが続けられたことからも今回はドラスティックな構造改革か、何かしらやむを得ない事情であったか。
まあYahooの商品検索APIとか使っている人はAmazonと比べたら相当少ないと思うのでそのまま廃止にならなかったことを素直に喜ぶこととする。ともあれ新仕様に対応していく必要がある。
現状のレスポンス
いま使用しているv1のレスポンスは次のようである。あれ?v2はと思ったがとりあえずずっとv1を私は使っておりそのままサポートし続けていたらしい。
リクエスト
https://shopping.yahooapis.jp/ShoppingWebService/V1/itemSearch?hits=2&query=パソコン&appid=my-app-id
私のアプリ、最安値検索でサポートしているクエリーパラメータは因みに
- affiliate_id: (アフィリエイトのID、商品が売れると1%お金が入る。基本大赤字)
- affiliate_type: アフィリエイトのタイプ、私の場合バリューコマースという会社を表すvcを指定
- hits: ヒット件数、最大50まで指定可能
- query: 検索ワード
- price_from: ●●円以上の商品を検索
- price_to: ●●円未満の商品を検索
をUIから指定できるようにしている。
このレスポンスは次のようになる(適宜端折って抜粋)
<ResultSet xmlns="urn:yahoo:jp:itemSearch"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:yahoo:jp:itemSearch http://shopping.yahooapis.jp/ShoppingWebService/V1/itemSearch.xsd" totalResultsAvailable="4409102" totalResultsReturned="2" firstResultPosition="1">
<Result>
<Request>
<Query>パソコン</Query>
</Request>
<Modules/>
<Hit index="1">
<Name>富士通 LIFEBOOK</Name>
<Description>[製品名] 中古パソコン 中古PC</Description>
<Headline>WEBカメラ付属 Microsoftoffice2019 中古パソコン Windows 10</Headline>
<Url>https://store.shopping.yahoo.co.jp/oa-plaza/noto-toshiba-i7-50off.html</Url>
<ReleaseDate/>
<Availability>instock</Availability>
<Code>oa-plaza_noto-toshiba-i7-50off</Code>
<Condition>used</Condition>
<Image>
<Id>oa-plaza_noto-toshiba-i7-50off</Id>
<Small>https://item-shopping.c.yimg.jp/i/c/oa-plaza_noto-toshiba-i7-50off</Small>
<Medium>https://item-shopping.c.yimg.jp/i/g/oa-plaza_noto-toshiba-i7-50off</Medium>
</Image>
<Review>
<Rate>4.36</Rate>
<Count>1638</Count>
<Url>https://shopping.yahoo.co.jp/review/item/list?store_id=oa-plaza&page_key=noto-toshiba-i7-50off</Url>
</Review>
<Affiliate><Rate>1.0</Rate></Affiliate>
<Price currency="JPY">9980</Price>
<PremiumPrice/>
<PriceLabel taxIncluded="true">
<FixedPrice/>
<DefaultPrice>29800</DefaultPrice>
<SalePrice>9980</SalePrice>
<PremiumPriceStatus>0</PremiumPriceStatus>
<PremiumPrice>9980</PremiumPrice>
<PremiumDiscountType>sale</PremiumDiscountType>
<PremiumDiscountRate>66</PremiumDiscountRate>
<PeriodStart>2020-05-26T08:03:00+09:00</PeriodStart>
<PeriodEnd>2020-06-08T23:59:00+09:00</PeriodEnd>
</PriceLabel>
<Point>
<Amount>298</Amount>
<Times>1</Times>
<PremiumAmount>99</PremiumAmount>
<PremiumTimes>1</PremiumTimes>
</Point>
<Shipping>
<Code>2</Code>
<Name>送料無料</Name>
</Shipping>
<Brands>
<Name>富士通 LIFEBOOK</Name>
</Brands>
<JanCode>2133013863909</JanCode>
</Hit>
<Hit index="2">
<Name>ほげほげ</Name>
<Description>ほげほげ</Description>
...
</Hit>
</Result>
</ResultSet>
新規APIを確認
基本的には過去に使っていたクエリーパラメータは設計としては特別な理由が無い限り残すので、新規APIに変わったとしてもこちら側は影響を受けないであろうと念のため確認。
appid,affiliate_id,affiliate_type,price_from,price_toは存在する。あれ?hitが無くなっている?確認するとhitsからresults
に値が変わっていた。まあ別にhitsで良かったんじゃね?と思うがとりあえずここが仕様変更箇所
あとはAPIのURLはV1がV3に変わったのでサンプルリクエストとしては次のようになる。
https://shopping.yahooapis.jp/ShoppingWebService/V3/itemSearch?hits=2&query=パソコン&appid=my-app-id
レスポンスは下記
{
"totalResultsAvailable": 5325532,
"totalResultsReturned": 2,
"firstResultsPosition": 1,
"request": {
"query": "パソコン"
},
"hits": [
{
"index": 1,
"name": "富士通 LIFEBOOK",
"description": "[製品名] 中古パソコン 中古PC",
"headLine": "WEBカメラ付属 Microsoftoffice2019 中古パソコン Windows 10",
"url": "https://store.shopping.yahoo.co.jp/oa-plaza/noto-toshiba-i7-50off.html",
"inStock": true,
"code": "oa-plaza_noto-toshiba-i7-50off",
"condition": "used",
"imageId": "oa-plaza_noto-toshiba-i7-50off",
"image": {
"small": "https://item-shopping.c.yimg.jp/i/c/oa-plaza_noto-toshiba-i7-50off",
"medium": "https://item-shopping.c.yimg.jp/i/g/oa-plaza_noto-toshiba-i7-50off"
},
"review": {
"rate": 4.36,
"count": 1638,
"url": "https://shopping.yahoo.co.jp/review/item/list?store_id=oa-plaza&page_key=noto-toshiba-i7-50off"
},
"affiliateRate": 1,
"price": 9980,
"premiumPrice": 9980,
"premiumPriceStatus": false,
"premiumDiscountType": "sale",
"premiumDiscountRate": 66,
"priceLabel": {
"taxable": true,
"defaultPrice": 29800,
"discountedPrice": 9980,
"fixedPrice": null,
"premiumPrice": null,
"periodStart": 1590447780,
"periodEnd": 1591628340
},
"point": {
"amount": 99,
"times": 1,
"premiumAmount": 99,
"premiumTimes": 1
},
"shipping": {
"code": 2,
"name": "送料無料"
},
"genreCategory": {
"id": 14249,
"name": "Windowsノート",
"depth": 3
},
"parentGenreCategories": [
{
"depth": 1,
"id": 2502,
"name": "スマホ、タブレット、パソコン"
},
{
"depth": 2,
"id": 14242,
"name": "ノートパソコン本体"
}
],
"brand": {
"id": 15944,
"name": "LIFEBOOK"
},
"parentBrands": [
{
"id": 1,
"name": "ブランド"
},
{
"id": 2366,
"name": "富士通"
}
],
"janCode": "2133013863909",
"isbn": "",
"releaseDate": null,
},
{
"index": 2,
"name": "ほげほげ",
"description": "ほげほげ",
}
]
}
JanCode
がjanCode
と最初の文字が小文字になっている。これは地味につらい。
設計変更
シンプル最安値検索というアプリでYahoo商品検索APIを使っているので修正していく。
現在の設計は一回さくらVPSにおいてある自分のサーバーを経由してYahoo商品検索APIにアクセスしており、サーバー側でXMLファイルをJsonに置き換えている。
axios.get(YAHOO.searchUrl, {
params
}).then((_response) => {
res.header('Content-Type', 'application/json; charset=UTF-8');
res.send(xmlParser.toJson(_response.data));
}).catch((e) => {
console.log(e);
});
これに対してV3用の次のAPIを追加
axios.get(YAHOO3.searchUrl, {
params
}).then((_response) => {
res.header('Content-Type', 'application/json; charset=UTF-8');
res.send(JSON.stringify(_response.data));
}).catch((e) => {
console.log(e);
});
暫く古い方も残しておかなくてはならない。
クライアント側はReactNaticeで作成している。レスポンスが小文字・大文字など地味に変わっているため修正する羽目になった。
特にiOSは審査に時間がかかったり厳しかったりするのでサーバ側の変更だけでいけるならばそれに越したことはない。サーバ側で単純にjsonを返さずに、必要な情報だけを返す前処理をやっておけばサーバ側のみの変更で済んだため、この設計は失敗だったといえる。まあ面倒なのでリファクタはしないが。。
アプリをサーバ・クライアント側の両面で設計する時は
- どちらの変更が面倒か
- 何がコントローラブルで、何がアンコントローラブルか?
等をもっと真剣に考え、変更が発生したときに柔軟に対処できるようにしておく必要があったという事である。ともあれクライアントサイドも大文字・小文字を書き換えるなどして無事に対応。後は再審査に通るのを待つのみである。