l

o

a

d

i

n

g

.

.

.

Amazonの商品リンクツールが調子悪いので修正した話 # GAS編

2026/04/19

トラブル プログラミング

t f B! P L
eyecatch 以前、自作して便利に使っていた、Amazonのアフェリエイトリンク(商品リンク)ツールが、最近調子が悪くなってきた。 GoogleChromeの拡張機能にdeveloperモードで入れていたものが、勝手に削除されるし、 Webツールのページで試してもエラーが出る。
以前ブログのリンク : Amazonアフェリエイトへの道 このツールは、個人的にブログを書く時に使う便利ツールだったので、修正して安定動作するようにしておきたいので、修正対応をしてみました。 ※ちなみに、この修正作業、この1記事では完結していません。

データ取得ロジックの変更

最近、GASを使ってクローリングをすることで、クロールサーバーを独自で用意しなくてもいいと考えて、データ取得をGAS化してみようと思いました。 元々は、会社で使っていたクラウドサーバーのUbuntu OSに、PHPでクローリングさせていた段ですが、それをGASの "UrlFetchApp" 機能でHTMLを取得すればいいと考えたわけです。 そして、実際にやってみると、さまざまな闇が見えてきて、泥沼に突入した経過をお楽しみください。

GASコード

GASで以下のコードを作って、デプロイ化すると、簡易なHTML+JSぐらいの構成で、インターネットから情報取得ができるようになります。 do_get.gs function doGet(e) { const url = e && e.parameter && e.parameter.url ? e.parameter.url : "" const amazon_data = new GetAmazon().init(url) const json = JSON.stringify(amazon_data) const res = ContentService.createTextOutput(json) res.setMimeType(ContentService.MimeType.JSON) return res } get_amazon.gs class GetAmazon { constructor(){ this.version = "1.2.0" this.dt = new Date() } init(url){ let html = this.load(url) const dom = this.dom(html) const datas = this.data(dom, url, html) return { status : dom && datas ? "success" : "error", varsion : this.version, uuid : new Uuid().id, datas : datas, } } load(url){ return UrlFetchApp.fetch(url, { method: "GET", contentType: "application/x-www-form-urlencoded", headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Accept": "text/html,application/xhtml+xml", "Accept-Language": "ja-JP,ja;q=0.9", "Accept-Encoding": "gzip", "Cookie": "lc-main=ja_JP; i18n-prefs=JPY", }, redirect: "follow", // CURLOPT_FOLLOWLOCATION followRedirects: true, muteHttpExceptions: true }).getContentText() } dom(html){ return HtmlParser.parse(html) } data(dom, url){ return { url : url, title : this.get_title(dom), asin : this.get_asin(dom), price_num : this.get_price_num(dom), unit : this.get_unit(dom), category : this.get_category(dom), image : this.get_image(dom), thumbnails : this.get_thumbnails(dom), datetime : this.get_datetime(), seller_name : this.get_seller_name(dom), seller_html : this.get_seller_html(dom), get_date : this.get_get_date(), get_time : this.get_get_time(), } } text(elm){//return elm.innerHTML return elm ? elm.textContent.trim() : null } attr(elm, name){ return elm ? elm.getAttribute(name) : null } get_title(dom){ const elm = dom.querySelector("title") return this.text(elm) } get_asin(dom){ const elm = dom.getElementById("ASIN") return this.attr(elm, "value") } get_price_num(dom){ const elm = dom.getElementById("twister-plus-price-data-price") return this.attr(elm, "value") } get_unit(dom){ const elm = dom.getElementById("twister-plus-price-data-price-unit") return this.attr(elm, "value") } get_category(dom){ const elm = dom.getElementById("nav-subnav") return this.attr(elm, "data-category") } get_image(dom){ const img = dom.getElementById("imgTagWrapperId") if(!img){return ""} const imgs = img.getElementsByTagName("img"); if(!imgs || !imgs.length){return ""} return imgs[0].getAttribute("src"); } get_thumbnails(dom){ const elm = dom.getElementById("altImages"); if(!elm){return ""} const arr = []; const ul_lists = elm.getElementsByTagName("ul"); if(!ul_lists || !ul_lists.length){return ""} for(let i=0; i<ul_lists.length; i++){ const li_lists = ul_lists[i].getElementsByTagName("li"); for(const li_list of li_lists){ if(li_list.getAttribute("src")){continue} const cls = li_list.getAttribute("class") if(!cls){continue} const classes = cls.split(" ") if(!classes.includes("item")){continue;} const img = li_list.querySelector("img") if(!img){continue} arr.push(img.getAttribute("src")) } } return arr } get_seller_name(dom){ const elm = dom.getElementById("sellerProfileTriggerId") return this.text(elm, "data-category") } get_seller_html(dom){ const elm = dom.getElementById("sellerProfileTriggerId") return this.attr(elm, "href") } get datetime(){ return { y : this.dt.getFullYear(), m : ("00"+ (this.dt.getMonth()+1)).slice(-2), d : ("00"+ (this.dt.getDate())).slice(-2), h : ("00"+ (this.dt.getHours())).slice(-2), i : ("00"+ (this.dt.getMinutes())).slice(-2), s : ("00"+ (this.dt.getSeconds())).slice(-2), } } get_datetime(){ const d = this.datetime return `${d.y}-${d.m}-${d.d} ${d.h}:${d.i}:${d.s}` } get_get_date(){ const d = this.datetime return `${d.y}-${d.m}-${d.d}` } get_get_time(){ const d = this.datetime return `${d.h}:${d.i}:${d.s}` } } "HtmlParser" は、ライブラリで以下の内容検索をすると簡単に取得できます。 スクリプトID : 1JTLPVXGW6Pq4zSAV5ED9XrWOPydVywumQZSOWN7l6KJ0UtWxtX3RbYO6/2 ご自身で設置する場合は、デプロイの方法は、過去のブログを参考にして実装してください。 参考ページ : https://blog.myntinc.com/2024/11/web-31-google.html

このコードのポイント

普通にfetchしただけでは、Amazonのエラーページが表示されてしまいました。 「APiI使えよ、このやろー」的なページです。 そこで次のヘッダ情報を付与してみました。 UrlFetchApp.fetch(url, { method: "GET", contentType: "application/x-www-form-urlencoded", headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Accept": "text/html,application/xhtml+xml", "Accept-Language": "ja-JP,ja;q=0.9", "Accept-Encoding": "gzip", "Cookie": "lc-main=ja_JP; i18n-prefs=JPY", }, redirect: "follow", followRedirects: true, muteHttpExceptions: true }).getContentText() そもそも、GASはUSに置いてあるサーバーで動作しているらしく、AmazonのHTMLを取得すると円ではなく、ドルで書かれたページが表示されていました。 なので、Languageを切り替えるのと、"followRedirects: true,"として、リダイレクト対応を許可するフラグが必要でした。 とにかく、これらのオプション+ヘッダ情報を付与しなければ、まともな情報取得ができませんでした。

Github Pagesにアップ

今回作った、GAS版のアフェリエイトリンクを、GithubPagesにアップしてみました。 https://yugeta.github.io/amazon_affiliate_link_maker/src/ こちらのページで、無事にAmazonの商品ページを取得することができるようになりました。 めでたし、めでたし・・・

Issue and Task

品質担保とは、安定運用して問題がなくなるまでつきまといます。 今回の改修で安心していたら、どうやら、Amazonの書籍ページのクローリングがうまくできませんでした。 どうしてもエラーページしか取得できないので、fetchオプションではなく、再びPHPバージョンにて検討してみた方がいいかもですね。

あとがき

GoogleChromeの規約で、「承認していない拡張機能は、すべて消しちまうぜ」警告は、以前に見てはいたんですが、 実際に消されてしまうと、なんとも不便なので、安定した正式版を作って、拡張機能としてもロンチしておきたいです。 もはやこのツールがないと、物足りなさというか、ブログの執筆に身が入らなくなっている自分に気がつきましたね。 ブログに華を添えてくれる、こうしたツール、みんな何故困らないんだろう? もしかして、自分だけ? でも、そんな自分に満足していることにも気がついた今回のブログでした。

人気の投稿

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。

ブログ アーカイブ