カテゴリー
[Node.js] PuppeteerでAmazonアソシエイトのURL生成
※ 当ページには【広告/PR】を含む場合があります。
2020/12/02
Amazonのサイトから製品比較のサイトを構築するときには、Amazonのアソシエイトツールバーで生成したテキストリンクは概ね次のように長いクエリ文字を繋いでとても長々しいURLになってしまっています。
https://www.amazon.co.jp/dp/B01AJ7DH9O/ref=as_li_ss_tl?ie=UTF8&linkCode=ll1&tag=hogepiyo000-11&linkId=0123456789abcdef0123456789abcdef&language=ja_JP
これだと長くて貼り付けるのが嫌だという方のために、以下のような
短縮URL
https://amzn.to/3fVmrD5
たしかにこれだと貼り付けるのも楽なのですが、URL文字列からASINも分からないようになってしまいます。
後々で「コレは何の商品リンクだっけ?」と元のリンク先の商品を手繰るにとても手間がかかります。
もっとスマートなリンクの貼り方はないものかと色々と調べたところ、Amazon.co.jpの特定の商品に対してシンプルなテキストリンクを作成したい場合、公式の
https://amazon.co.jp/dp/ASIN/ref=nosim?tag=あなたのアソシエイトID
これならばURL短縮形ほど短くはないものの、後々URLのリンクを見ても商品のASINが確認出来ています。
この点で、いいとこ取りのリンクフォーマットになっていると思います。
特に自分のサイトで商品紹介をする場合、amazonのアフィリエイトリンクURLと画像の取得をブラウザから手で一つ一つ張ったり保存したりするのがそろそろ野暮ったく感じるようになってくるようになります。
ここは一つ
今回はpuppeteerを使ってAmazonアフィリエイトURLと関連の画像と説明文を同時にローカルに保存してくれるだけのスクリプトを作成する際の勘どころを解説してみます。
Pupperteerの動作準備
以前、
今回もDockerコンテナ上からヘッドレスChromeを動かすので、インストール手順などを知りたい方はこちらのリンクから導入方法〜動作確認をまずご確認ください。
shelljsとargparseのインストール
今回はnodejsのスクリプトをツール風に仕立てるため、nodejs側から各種shellコマンドを呼び出せる
fs、httpなどのnodejsのネイティブなユーティリティだけでもオンライン上からのファイルを読み書きは可能ですが、画像を特定のurlから引っ張ってきてローカルにバイナリ保存するプログラムをわざわざ実装するのは面倒です。
Alpineのdockerイメージではもともとwgetが使えますので、shelljsからwgetを呼び出してファイルを取得・保存する方法を取るのがより効率的なやり方かと思います。
プロジェクトへのインストールは以下のようにするだけです。
$ yarn add shelljs argparse -S
$ yarn add @types/shelljs @types/argparse -D
typescriptから使うので定義ファイルをdevdependenciesに追加しておきます。
Amazonの商品ページ操作例① ~ 物販系
Amazonの商品ページでは、取り扱われている商品のカテゴリーのよって微妙にhtmlの構造が違いますので、カテゴリー別にページの構造をよく解析しながらpuppeteerの要素セレクタでゴリゴリとスクレイピングしていくスタイルになります。
まずはAmazonの商品ページで一番取扱の多いであろう物販品のページフォーマットからやってみます。
ASINコードの探し方
何はともあれ商品のASINコードは最低限見つけてこないといけませんので、お目当ての商品をAmazonのページで探ります。
例えば会議用のwebカメラを紹介してみたい場合、商品ページのURLアドレスの
dp/

ここからこの商品のASINコードが
B08HQXVL9Y
ASINコードが分かると、puppeteerでアクセスする商品ページのアドレスも決まり、この例では
https://amazon.co.jp/dp/B08HQXVL9Y
PuppeteerでAmazon商品タイトルを取得
まずは商品ページ(
https://amazon.co.jp/dp/B08HQXVL9Y
取得させたい要素の解析はブラウザ上で行います。 今回はChromeを使いますが、どのブラウザでも基本的には同様のことが行えると思います。
まず、puppeteerで取り込むターゲット要素の上で右クリックし、
[検証]

特に、要素にid属性があるとセレクターの識別クエリの選択は楽になります。
商品のタイトルは全て
id="productTitle"
#productTitle
では
$ tree -I node_modules
.
├── Dockerfile
├── docker-compose.yml
├── package.json
├── src
│ └── index.ts
├── tsconfig.json
└── yarn.lock
まずは
src/index.ts
import puppeteer from 'puppeteer';
import * as argparse from 'argparse';
import * as shell from 'shelljs';
(async () => {
//👇コマンドとして実行時に引数として設定される
const parser = new argparse.ArgumentParser({
description: 'Amazon Affiliate Link Generator'
});
parser.add_argument('-a', '--asin', {
type: 'str',
help: 'Targeted ASIN code what you want.'
});
const args_ = parser.parse_args();
//👇テキストや画像の一時保管先
// '/usr/src/app/tmp'というフォルダ内にリリースを保存
const _cwd = '/usr/src/app/tmp';
shell.rm('-rf', _cwd);
shell.mkdir(_cwd);
//👇シェルスクリプトでの echo '...文字列' > ${_cwd}/output.txt 相当
//shelljsを使うとテキストの保存もワンライナーで作成できる
//ちなみにAmazonアフィリエイトのユーザーIDは一例のためhoge000-00としています
shell.echo(`https://amazon.co.jp/dp/${args_.asin}/ref=nosim?tag=hoge000-00`).to(`${_cwd}/output.txt`);
const browser = await puppeteer.launch({
executablePath: '/usr/bin/chromium-browser',
args: ['--disable-dev-shm-usage', '--no-sandbox']
});
try {
const page = await browser.newPage();
await page.goto(`https://www.amazon.co.jp/dp/${args_.asin}`);
//👇①商品のタイトル要素を抽出
const title_ = await page.$eval('#productTitle', el => el.innerHTML);
//👇テキストファイルにタイトルを書き込み
//$ echo "${title_}" >> ${_cwd}/output.txt 相当のコマンド
shell.echo(`${title_}`.replace(/^\n/gm, '')).toEnd(`${_cwd}/output.txt`);
//👇②商品説明の要素を抽出
//コードは後述
//👇③価格情報の要素を抽出
//コードは後述
//👇④商品画像の習得
//コードは後述
} catch (e) {
throw e;
} finally {
await browser.close();
}
})();
これでひとまずプログラムを走らせてみます。
#👇src/index.tsがビルドされる
$ yarn build
#👇-aもしくは--asinオプションに商品のASINコードを指定
$ yarn tap -a B08HQXVL9Y
上手くプログラムが走ると、ルートの
tmp
output.txt
https://amazon.co.jp/dp/B08HQXVL9Y/ref=nosim?tag=hoge000-00
GOPPA ウェブカメラ オートフォーカス機能搭載 フルHD 200万画素 1920×1080対応 マイク内蔵 GP-UCAM2FA/E
PuppeteerでAmazon商品紹介を取得
商品紹介の部分は、ul要素内の複数のli内に箇条書きのリスト項目で表示されている形式のため、内容文をすべて引っこ抜くためには、ul要素のinnerHtmlをそのまま習得するか、各liを全て個別に抜き取るなどの方法がかんがえられます。

puppeteerにはセレクターで複数マッチさせるのに使えるメソッドとして、
$$
$$eval
evaluate + querySelectorALL
の3つが用意されています。
この3つは微妙に使い方と作用が違います。
使い分けの詳細については
では先程の
index.ts
//...中略
//👇②商品説明の要素を抽出
const feature_ = await page.$$eval('#feature-bullets > ul > li > span',
elms => elms.map(el => el.innerHTML)
);
for (const elm of feature_) {
shell.echo(`${elm}`.replace(/^\n/gm, '')).toEnd(`${_cwd}/output.txt`);
}
//...以下略
再度、ビルドしてスクリプトを実行すると、
output.txt
https://amazon.co.jp/dp/B08HQXVL9Y/ref=nosim?tag=hoge000-00
GOPPA ウェブカメラ オートフォーカス機能搭載 フルHD 200万画素 1920×1080対応 マイク内蔵 GP-UCAM2FA/E
<span id="replacementPartsFitmentBulletInner"> <a class="a-link-normal hsx-rpp-fitment-focus" href="#">モデル番号を入力してください</a>
<span>これが適合するか確認:</span>
</span>
GOPPA製「GP-UCAM2FA」は、テレワークやビデオ配信に最適な200万画素WEBカメラです。オートフォーカスによる鮮明な映像、30FPSのなめらか動き、デジタルマイク内蔵など必要な機能をコンパクトに搭載していますので、ZoomやGoogle Meet、TeamsなどのWeb会議やオンライン飲み会でご利用いただけます。
有効画素数:有効画素数、色数:有効画素数、最大解像度:1920×1080、最大フレームレート:30 FPS
フォーカス:オートフォーカス、オートフォーカス:5cm〜∞、F値:F2.2、視野角度:75度
対応OS:Windows 10、Windows 8、Windows 7、macOS 10.12 Sierra 以降
保証期間:6か月
ただし、抽出した結果をみていただくと、li要素の中身に更にspan要素が入れ子になっている場合もあります。
完全にキレイな文として抜き出せるかどうかは、サイトがきれいな構造設計をされいるかに依るところが大きいです。
ここらへんはある程度文を抽出できたら自分で細かい内容を修正するように妥協する他ありません。
Puppeteerで商品の値段情報を取得
値段情報は商品ページの複数ヶ所に散らばっていたり、商品ページによって不規則に変化しているようです。
今回のやり方もあくまでも目安ですが、値段は
id="priceblock_ourprice"
id="price-shipping-message"

ということで
index.ts
//...中略
//👇③価格情報の要素を抽出
const price_ = await page.$eval('#priceblock_ourprice', el => el.innerHTML);
const shipment_ = await page.$eval('#price-shipping-message > b', el => el.innerHTML);
shell.echo(`${price_}`.replace(/^\n/gm, '') + ` ${shipment_}`).toEnd(`${_cwd}/output.txt`);
//...以下略
これをビルド・実行すると、output.txtに以下の価格情報が追加されていると思います。
¥5,506 通常配送無料
Puppeteerで画像の取得
商品の見出しとなる画像を一つurlリンクを辿って取得し、ローカルに保存もしたいときがあると思います。
商品ページの見出し画像のリンクは、
id="imgTagWrapperId"

リソース画像の保存先が分かれば、あとはwgetでローカルに取得できます。
以下がコーディングの一例です。
//...中略
//👇④商品画像の習得
const imgUrl_ = await page.$eval('#imgTagWrapperId > img', el => el.getAttribute('src'));
shell.exec(`wget ${imgUrl_} -O ${_cwd}/amazon.jpg`);
//...以下略
ここでのポイントはshelljsによって外部のwgetが実行されているのですが、当然ながらシステムにwgetがインストールされて使えるようになっていないと使えません。
Amazonの商品ページ操作例② ~ 電子書籍(Kindle)系
物販品以外での例も取り上げてみます。
商品カテゴリーが違うとhtml構造もガラッと違うため、AmazonアフィリエイトURLの自動生成を行う場合は、スクリプトツールもカテゴリー別にして管理したほうが良いかもしれません。
特に電子書籍やソフトウェアは発送情報はないので値段だけを抽出するところもあり、物販とは販売方法も異なります。
まず以下に電子書籍の商品ページの取得コード例を修正するポイントだけを載せています。
//...中略
//👇①商品のタイトル要素を抽出
const title_ = await page.$eval('#productTitle', el => el.innerHTML);
shell.echo(`${title_}`.replace(/^\n/gm, '')).toEnd(`${_cwd}/output.txt`);
//👇②商品説明の要素を抽出
const elementHandle: any = await page.$('#bookDesc_iframe_wrapper > iframe');
const frame = await elementHandle.contentFrame();
subtitleSelector = '#iframeContent';
await frame.waitForSelector(subtitleSelector);
const feature_ = await frame.$eval(subtitleSelector, (el: any) => el.innerHTML);
shell.echo(`${feature_}`.replace(/^\n/gm, '')).toEnd(`${_cwd}/output.txt`);
//👇③価格情報の要素を抽出
const price_ = await page.$eval('#tmmSwatches > ul > li:nth-child(1) > span > span:nth-child(1) > span > a', el => el.innerHTML);
shell.echo(`${price_}`.replace(/^\n/gm, '')).toEnd(`${_cwd}/output.txt`);
//👇④商品画像の習得
const imgUrl_ = await page.$eval('#ebooksImgBlkFront', el => el.getAttribute('src'));
shell.exec(`wget ${imgUrl_} -O ${_cwd}/amazon.jpg`);
//...以下略
電子書籍の商品ページの各要素取得に関しては、
②

サイトにもよりますが、iframeの中身はメインのhtmlが読み込みが完了してから実行されるjsスクリプトによってレンダリングされる仕組みがあります。
きちんと読み込み完了後にiframe要素を取り込むようにpuppeteerへ知らせないとiframeの中身は取得できません。
そこでiframe要素を取り出し、
contentFrame
iframe要素内で目的の要素がレンダリング完了となるまで、
waitForSelector
まとめ
今回はpuppeteerを利用して、AmazonアフィリエイトのURLに関連するリソースを含めてを自動生成する手段について考察してみました。
Amazonの商品ページも商品カテゴリーごとにhtmlの構造が違うので、自動抽出ツールもコレ1つ使っていればOK、というものにはならないかも知れませんが、自前でスクリプトを作成する指針にしていただければ幸いです。
関連書籍
以下は「Puppeteer」を特集した参考書籍です。
記事を書いた人
ナンデモ系エンジニア
主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。
カテゴリー