![title-image](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2lotc8L2sf6WVvSWzOVXbZE4STYI5G8p5-hoyliCuTTMW8itZ1PUlcpC_sP_WSyUEhxnuGrPa3Ah4kIgYK11rnpmlxLzkhliiypcgm_slLk6jbf_nIpIjDJQZXCm-zfnyqcwNUen38e8MbyeVlpHxaknaiVxhhXf8mdD8cUezvPiGAQl3lXSX07fS-1s/s320/pa-api3.webp)
アマゾンアソシエイトの画像リンクが廃止になり、API経由で商品画像、価格などの情報を取得必要になってしまいました。PyhonのSDKを使い画像つきの商品リンクを作成する方法を勉強し、取り急ぎオフラインでこれまで貼ったリンクを変換して貼り直すことはできました。
![thumbnail](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiT3MHE29_GS2BxXuh7zrDKA-ZMZjcnuIk8ANn8zlm0PHBoBCWNjlppp8nH-6F7sMJCa3bxIZnb6tc9I4fbV256h2zLgEfR6Egs1E3x56uyJQpWDlR5_9Euh5iTCrGxC88wdBKShLKR3ur_AxeSKls9Bf2EhbevZWzyCLdcDP1RBzW097bK3FkWvJeysQM/w160/pa-api2.webp)
アソシエイトのツールバーで作成した画像+テキストリンクをPA-APIを使い変換する
11月にアマゾンからアソシエイトツールバーで作成した「画像リンク」、「テキストと画像リンク」が廃止さ ...
しかしこの静的なリンクだと商品情報が更新されないため、毎回ページを表示するたびに動的にリンクを更新したいです。
オリジン間リソース共有(CORS)とは
メインページとは異なるサーバーから読み込んだリソースを取得しようとすると、通常ブラウザのセキュリティ機能でブロックされてしまいます。
![CORSの説明図](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisf8pY58xiNKfTR6_JvDOegcHDFNeGVbxaXJSVhkgV24AnEWl112znjG8uro4gOOwRpsoScsRMv2jQbeQAG_MRbXa2ITmXRZZcfgFTsSYt0Grk2GiX1MBA01wueaa88dF2a6h8ls0CC6Vuj2CmFcYfy6uf5M-cJEGdvLniz7bcLCyyCStOgkLe16GROCg/s320/01.webp)
サーバーからのレスポンスのヘッダーに、
Access-Control-Allow-Origin
を付けてもらうとブラウザが表示してくれます。
しかしPA-APIからのレスポンスには
Access-Control-Allow-Origin
が含まれていないのです。
上記以外にも、リクエストの際にも認証情報が必要でさらに複雑です。
FaaSでPA-APIからのレスポンスにAccess-Control-Allow-Originを付ける
ということで、FaaSを間に噛ませて、PA-APIの情報を取得して、そこにヘッダーを追加して返すことにします。
![FaaSを使用したCORSの説明図](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhB2b3CpxFgs3_d7yobpwAmBefWBnTx8GdkD4Sd0CiupLIgKrpKd8JAfkCNramqpGjk8r7DnhJPQQQX3sJUS87qggSnV63Q7UcsTU5SgiOclP0ss8mNXRXx6_JqIErtfMvMwTRAAq8Hl6KhaaKcXjF8bhA5hfuw6Z1in0u4_bFK77w70CB-xq4HyBT2MK8/s320/02.webp)
Netlify Functionsを使う
GithubとNetlifyのアカウントを持っていることを前提として以下説明します。アカウントの作成方法などは下記の記事にNetlifyで静的サイトをホスティングする方法をまとめているので、その中で説明していますのでご参考ください。また、Node.jsも使用します。
Hugo+NetlifyでJamstackブログの作り方 (1)事前準備
今までGoogleのサービス、Bloggerでブログを書いていましたがカスタマイズに制約を感じ、レスポンスも遅く ...
![thumbnail](https://jam.bchari.com/posts/quick-start-2/github.webp)
Hugo+NetlifyでJamstackブログの作り方 (2) GitHubへアップロード
Hugoでドラフト記事を作成し、ローカルサーバーで表示してみました。次はGitHubへのアップロードです ...
![thumbnail](https://jam.bchari.com/posts/quick-start-3/quick-start-3_01.webp)
Hugo+NetlifyでJamstackブログの作り方 (3) Netlifyでホスティング
Hugoでブログ記事を作成しGitHubへアップロードしました。最後にNetlifyでブログをホスティングします ...
準備
まずはプロジェクトのフォルダを作成し、
npm init
します。
$ mkdir paapiFunction
$ cd paapiFunction
$ npm init
PA-APIのNodeモジュール
amazon-paapi
をインストールします。アマゾン公式のSDKのラッパーですが、公式SDKを直接使うよりも簡潔なコードで使えるのでこちらがおすすめです。
$ npm install amazon-paapi --save
Functionを作る
https://docs.netlify.com/functions/get-started/?fn-language=js
を参考にFunctionを作ります。まず、コードを保存するフォルダが
netlify/functions
に指定されているため、フォルダを作ります。
$ mkdir -p netlify/functions
この中に、
関数名.js
というファイルを作り、デフォルト関数をエクスポートすると、その関数の返り値がNetlify
functionsでのレスポンスになります。
export default async (req, context) => {
return レスポンス
}
また、この関数を呼び出すURLは、デフォルトでは
https://ドメイン名/.netlify/functions/関数名
ですが、Javascriptファイル内で
config
値を設定することで変更することができます。
// URLを https://ドメイン名/paapi/「asin」とし、asinをパラメーターとして
// 関数本体へ渡す
export const config = {
path: "/paapi/:asin+",
};
上の例では、1つのページに複数の商品情報があることを想定し、pathの最後に+をつけると、/ASIN1/ASIN2/,,,のように複数のパラメターを渡すことができます。
あとは、
amazon-paapi
のサンプルコードと、
https://docs.netlify.com/functions/get-started/?fn-language=js
のサンプルコードをもとに下のようなコードを作りました。
const amazonPaapi = require("amazon-paapi");
const commonParameters = {
AccessKey: "アクセスキー",
SecretKey: "シークレットキー",
PartnerTag: "アソシエイトID",
PartnerType: "Associates",
Marketplace: "www.amazon.co.jp",
};
const requestParameters = {
ItemIds: ["B09MCRYG2Y"],
ItemIdType: "ASIN",
Condition: "New",
Resources: [
"Images.Primary.Medium",
"ItemInfo.Title",
"Offers.Listings.Price",
],
};
const allowed_origins = [ // 許可するドメイン
"https://bchari.com",
"https://www.bchari.com",
"https://6266051941055901697_df38389a483f423c9cb61ef4566b352b55a5f72d.blogspot.com",
"https://draft.blogger.com",
];
// 関数本体 リクエストのパラメーター asin をアマゾンPA-APIに問い合わせ、
// PA-APIからのレスポンスにAccess-Control-Allow-Originヘッダーを付ける
export default async (req, context) => {
const origin = req.headers.get("Origin");
console.log(context.params);
console.log(origin);
const asins = context.params.asin.split("/");
requestParameters.ItemIds = asins;
let res = "";
/** Promise */
await amazonPaapi
.GetItems(commonParameters, requestParameters)
.then((data) => {
res = JSON.stringify(data);
})
.catch((error) => {
console.log(error);
res = null;
});
let header;
if (allowed_origins.includes(origin)) {
header = {
"Access-Control-Allow-Origin": origin,
};
}
return new Response(res, {
status: 200,
statusText: "Netlify functions ppapi returned",
headers: header,
});
};
// URLを https://ドメイン名/paapi/「asin」とし、asinをパラメーターとして
// 関数本体へ渡す
export const config = {
path: "/paapi/:asin+",
};
Netlify CLIを使いローカルでテスト
ローカルでテストができるよう、NetlifyのCLIツールをインストールします。このモジュールはグローバルでインストールしました。これも使い方は Netlifyのドキュメント ほぼそのままですが、流れは下記です。
$ npm nstall netlify-cli -g
Netlifyにログインします。
$ netlify login
ユーザー名、パスワードを聞かれ、無事ログイン成功したら下記コマンドでローカルサーバーが立ち上がり作成したFunctionをテストできます。
$ netlify dev
下のように、サーバーがスタートします。
![サーバースタート](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtQ78IzV2fzw6Q_Y7alfqYss_h_inF2b2IaOVkrxJvHNh8M8IE-G9ZW9zMltOEoI90LPJT0XDAwD5gS1y4cwc0NZftxIfDOX1M7o_Mbf2sMXdToFtzd0nREpKsm6QET7rdsUcFKxrp4p2aSw9BUalLOD5a_TUkyL-16fP05Xbiiqb760M0QVXtdp-iBX8/s320/03.png)
ブラウザが起動し、
http://localhost:8888
が表示されますが、404
Not
Foundが表示されていると思います。試しに、ASIN番号B089X1S162の商品を検索してみます。アドレスバーに
http://localhost:8888/paapi/B089X1S162
と入力すると結果が返ってくれば成功です。
![functionのレスポンス](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkvts-KL6bZJPCC2YufOQX7xzgCXelj_M6R2VMSnj2MlX5rpx2oPQWz3asYVP7EvXQ5-o6l0lB6mzKCyLdnWNzkzApjzyN1AWwM3btCm2jUf4NATAbZvUvy7_yqm1o08fKmwRMV1OxFDxYDSxX6p3_CSfltDDejc_ibo5cBqUQId-Qkq2ksqfKMyXW-CE/s320/04.png)
デプロイ
Netlify CLIから直接デプロイすることもできますが、今回はGithubを使います。Githubに新たなレポジトリを作成し、ファイルをGithubで管理します。
$ git init
$ git add *
$ git commit -m"my first function"
$ git remote add origin https://github.com/ユーザ/[作成したリポジトリ].git
$ git git push origin master
これでGithubにアップロードできました。次はNetlifyで新しいサイトを作成します。「Import an existing project」を選びます。
![Netlifyで新しいサイトを作成](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKcwMOz1DuqekgQ-Z7GxmCUoZYaBEJRIadothuplzhSae1x4MoOS8cOfyjwFA_ih1UMmgj28-eanO-8U8aWl6rO2ueoCNhOYtwMGcrLPa8X43yqQrCj44fnfWsWYvm9snoLZRNR9VZdouO1cAoz2lQ_FGWXSnT1jvvTl4G-H2Av0dd_o9aKnq0vF3APK8/s320/05.png)
Githubを選びます。
![Githubを選びます](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhV-j6kQ11YI31oG5eBEEIXM94_ZV9OE3tMU9zcp_Cebzj861u32zLZiVeb7DFXOgbxilf1rHcvSNQnT2b0-lX5bL3DyA06hlEAtrWn2-kufFuPmCn0aQNKc-12aBOnOpSTnvaPi9Us0aV7dTWBFElKg4eiiSSpR0uOexEjTNEmL1DAonPGVghY0ZtzO3Q/s320/06.png)
レポジトリを指定します。今回、コード中にPA-APIのシーククレットキーを埋め込んでいるため、レポジトリは「Private」にしています。
![レポジトリを指定します](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQjUd21prbO5F5DIQL2ft0fKe4khnU08a4lmJI4stW-kAXziCvmCOd4YNDlYLrBoMVqJh6SdH0GX4b3L-5X-8-aH0TO7dDiVLfo6m33y46FREg80n4NiCKhkQEvfJfsnCUSuS3na38vS0LFwSfH24b6at3PFl9DXEWLz_gToJnTA3JkL_9G04yDNCqcrs/s320/07.png)
設定項目が出てきますが、何もいじらずにデプロイします。「Functions
Directory」が、ローカルに作ったフォルダ構成と同じ
netlify/functions
になっていますね。これを変更すればファイルの場所を変えることもできそうです。また、「Add
environment
variables」ボタンで環境変数が設定できます。シークレットキーを環境変数に保存して、コードからは環境変数を参照することでコードを公開してもシークレットキーを隠すことができます。
![Deployボタン](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnGn3XjWMiO_OqEeRl_CrkOr9oRvemVJBTA3TfMHaABJlarzyxCCsfE9zdwmA-RdlDwBS-xARYbbKwaY4YZvTonW8rV98ke07D_pYBIwOylKeoaBzWTUVz6Y0Dx_oiQhAh2enDDX0IjOm0LdGQpjccjILbeD90z_JjApIzYP0rq98_p3J3NornhIfysU8/s320/08.png)
デプロイが開始され、全部Completeになれば成功です。
![デプロイComplete](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmcqeKqGfxSwgr21p1TztfHeHVk70AG30HEq8snv44DcbdhNM4lX_lgCQOubrwOSDqhQW_y1DiBdMjQe6tM08t4y4QnE6H-JvezE96z7q9q_MYkj5CwXjAuHSLNupzb6ID6pWAGpcS03Ez1tqGSmpCqWSKKnvjjXReFHsvSUbCv27HxsI3OpkxQIFBZbk/s320/09.png)
https://サイト名.netlify.app/paapi/B075PPYDBV
にアクセスしてローカルでのテストと同様にレスポンスが表示されることを確認しましょう。
Bloggerでの使い方
記事作成画面で、ASINを含む
div
要素を書き込みます。
<div class="pa-ad-unit" data-asin="B089X1S162"></div>
<div class="pa-ad-unit" data-asin="B0B35C211B"></div>
<div class="pa-ad-unit" data-asin="B0BXP4VT3Y"></div>
記事のどこか、本文最後にでも広告のテンプレートを入れておきます。ちょっと長く冗長ですが、スクラッチパッドの吐き出したHTMLをほぼそのまま使いました。
<template id="tpl"
><div class="paapi5-pa-ad-unit">
<div class="paapi5-pa-product-container">
<div class="paapi5-pa-product-image">
<div class="paapi5-pa-product-image-wrapper">
<a
class="paapi5-pa-product-image-link"
href=""
title=""
target="_blank"
><img
class="paapi5-pa-product-image-source"
src=""
alt=""
width=""
height=""
/></a>
</div>
</div>
<div class="paapi5-pa-product-details">
<div class="paapi5-pa-product-title">
<a
class="paap5-pa-product-title-link"
href=""
title=""
target="_blank"
></a>
</div>
<div class="paapi5-pa-product-list-price">
<span class="paapi5-pa-product-list-price-value"></span>
</div>
<div class="paapi5-pa-product-prime-icon">
<span class="icon-prime-all"></span
><a
class="paap5-pa-product-title-link"
href=""
title=""
target="_blank"
><span class="buy-on-amazon">Amazonで買う</span></a
>
</div>
</div>
</div>
</div>
</template>
さらに
<script>
タグで囲んだ下記のコードも貼り付けます。DOMツリーの
div
要素のASINを取得して、先程のNetlify
Functionsからfetchしてレスポンスをテンプレートに書き込んで
div
の子要素に挿入するという内容です。
async function getItems(asins) {
const params = asins.join("/");
try {
const response = await fetch(
`https://sumicfunctions.netlify.app/paapi/${params}`,
{
mode: "cors",
},
);
if (!response.ok) {
throw new Error("Network response was not OK");
}
const items = await response.json();
return items;
} catch (err) {
console.log(err, "Fetch error");
return null;
}
}
async function renderItems() {
const ads = document.querySelectorAll(".pa-ad-unit");
const asins = [];
ads.forEach((elem) => {
const asin = elem.dataset.asin;
asins.push(asin);
});
const res = await getItems(asins);
ads.forEach((ad) => {
const template = document.querySelector("#tpl");
const clone = template.content.cloneNode(true);
const link = clone.querySelectorAll(".paap5-pa-product-title-link");
const item = res.ItemsResult.Items.find(
(elem) => elem.ASIN === ad.dataset.asin,
);
link.forEach((elem) => {
elem.href = item.DetailPageURL;
elem.title = item.ItemInfo.Title.DisplayValue;
if (elem.innerText === "") {
elem.innerText = item.ItemInfo.Title.DisplayValue;
}
});
const img_a = clone.querySelector(".paapi5-pa-product-image-link");
const img = img_a.firstChild;
img_a.href = item.DetailPageURL;
img_a.title = item.ItemInfo.Title.DisplayValue;
img.src = item.Images.Primary.Medium.URL;
img.alt = item.ItemInfo.Title.DisplayValue;
img.width = item.Images.Primary.Medium.Width;
img.height = item.Images.Primary.Medium.Height;
clone.querySelector(".paapi5-pa-product-list-price-value").innerText =
item.Offers?.Listings[0].Price.DisplayAmount ?? "";
ad.replaceWith(clone);
});
}
document.addEventListener("DOMContentLoaded", renderItems);
CSSは前の記事をご参考ください。
![thumbnail](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiT3MHE29_GS2BxXuh7zrDKA-ZMZjcnuIk8ANn8zlm0PHBoBCWNjlppp8nH-6F7sMJCa3bxIZnb6tc9I4fbV256h2zLgEfR6Egs1E3x56uyJQpWDlR5_9Euh5iTCrGxC88wdBKShLKR3ur_AxeSKls9Bf2EhbevZWzyCLdcDP1RBzW097bK3FkWvJeysQM/w160/pa-api2.webp)
アソシエイトのツールバーで作成した画像+テキストリンクをPA-APIを使い変換する
11月にアマゾンからアソシエイトツールバーで作成した「画像リンク」、「テキストと画像リンク」が廃止さ ...
作成した広告リンク
↓のような感じです。
先日Python SDKを使い静的に作成したものと見た目はあまり変わりませんが、毎回APIから最新情報を取得し表示しています。これで価格や評価など、時間とともに更新される情報も安心して載せることができます。
レスポンスに結構時間がかかるのと、たまに失敗するのが微妙なところでエラー処理をもっと考える必要がありそうですが、なんとかここまでたどり着けました。
まとめ
アマゾンアソシエイトの広告リンクを、FaaSを間に入れることでPA-APIから動的に情報を取得、表示できるようになりました。
ただ、Bloggerなど融通のあまり効かないブログサービスを利用している場合は、記事本文に結構長いJavascript、HTMLテンプレートを貼り付ける必要がありコピペ作業とはいえちょっと微妙ですね。Bloggerのテーマに入れてしまう手もありますが、そうすると広告の無い記事にもこれらを読み込ませてしまうのでそれはそれで嫌です。
今後のアマゾンアソシエイトの広告リンクに使いながら静的、動的リンクを比較してみたいと思います。
関連記事
![thumbnail](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiT3MHE29_GS2BxXuh7zrDKA-ZMZjcnuIk8ANn8zlm0PHBoBCWNjlppp8nH-6F7sMJCa3bxIZnb6tc9I4fbV256h2zLgEfR6Egs1E3x56uyJQpWDlR5_9Euh5iTCrGxC88wdBKShLKR3ur_AxeSKls9Bf2EhbevZWzyCLdcDP1RBzW097bK3FkWvJeysQM/w160/pa-api2.webp)
アソシエイトのツールバーで作成した画像+テキストリンクをPA-APIを使い変換する
11月にアマゾンからアソシエイトツールバーで作成した「画像リンク」、「テキストと画像リンク」が廃止さ ...
![thumbnail](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSYk1tPZAAiMVkAwPZjnX2Nv9V4_aDdYVk0DdjTdL6xglWcU5pG9Zl1OE5YTo2-fFVjfNszPzKeoR-qf5MsV6OIZxaQMPo0lJjsl4ryqtk1X6qcPostjneqmKpw4S2uOVU3zNYXtHPTs079tV28VUK2-WxQEuXXzmq50aAXcCqm7P9z8-Md-E7yba4IHQ/w160/0000.webp)
アマゾンアソシエイト アソシエイトツールバー画像リンク作成機能の廃止
11月30日に、突然アマゾンから「【重要】アソシエイトツールバー画像リンク作成機能 廃止のご連絡 ...
Hugo+NetlifyでJamstackブログの作り方 (1)事前準備
今までGoogleのサービス、Bloggerでブログを書いていましたがカスタマイズに制約を感じ、レスポンスも遅く ...
![thumbnail](https://jam.bchari.com/posts/quick-start-2/github.webp)
Hugo+NetlifyでJamstackブログの作り方 (2) GitHubへアップロード
Hugoでドラフト記事を作成し、ローカルサーバーで表示してみました。次はGitHubへのアップロードです ...
![thumbnail](https://jam.bchari.com/posts/quick-start-3/quick-start-3_01.webp)
Hugo+NetlifyでJamstackブログの作り方 (3) Netlifyでホスティング
Hugoでブログ記事を作成しGitHubへアップロードしました。最後にNetlifyでブログをホスティングします ...
0 件のコメント:
コメントを投稿