スキップしてメイン コンテンツに移動

その4 Excel VBAでAmazon Product Advertising API


引っ越すためついに本気で書籍リストを作っていると、どれほど少しでも省力化したくなり、しつこくバージョンアップ。

ISBNのない外国の本も検索したい

ISBNのない本が思ったより多かった。フランス語のアクサンとか入力したくない。ISBNでの検索はamazon.com, amazon.frなど検索先を切り替えているように、タイトル等での検索も各国サイトを利用したい。

最初は「jpで見つからなければcom、見つからなければfr、、、」と思ったけど、だいぶ待たされちゃうし、その本のデータをどこで探したいかは決まってるので、それぞれ別のプロシージャを定義して実行できるようにした。
Public Sub searchBookInfoFromAmazonFr(dummy As Integer)
    searchBookInfo (amazonFr)
End Sub


さて、クイックアクセスツールバーに登録するのだけど、用意されてるアイコンじゃわかりづらい。各国の旗がいいんじゃないか。と思ってからが長かった・・・が、まとめれば:
  • 旗アイコンはここで入手。
  • Ribbon Editor でリボンに自前のタブ・ボタンを登録
    • 対象のExcelファイルを開く
    • 右のDocument Explorerペインで「CustomUI Parts」を追加
    • 表示される編集ペインのImageタブを開き、好きな画像を追加(From File System)
    • Codeタブに下記Xmlを書いて保存、終了
  • Excelを開くとカスタム・ボタンが登録されているので、ふつうにクイックアクセスツールバーにも登録
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui">
  <ribbon startFromScratch="false">
    <tabs>
      <tab id="customMacro" label="custom macro">
        <group id="customGroup" label="Search from...">
          <button id="button1" label="Com" image="usa" size="normal" onAction="'searchBookInfoFromAmazonCom 1'" />
          <button id="button2" label="France" image="France" size="normal" onAction="'searchBookInfoFromAmazonFr 1'" />
          <button id="button3" label="Spain" image="Spain" size="normal" onAction="'searchBookInfoFromAmazonEs 1'" />
        </group>
      </tab>
    </tabs>
  </ribbon>
</customUI>

まとめればそれだけなんだけどボタンを押すとマクロが2回実行されるという、どうも昔からあるらしい謎仕様にはまった。
onAction="'foo()'"
→「実行できません。このブックでマクロが使用できないか、またはすべてのマクロが無効になっている可能性があります。」
onAction="foo()"
→ 2回実行され、2回目は意味不明なエラー。
onAction="foo"
onAction="'foo'"
→「引数の数が一致していません。または不正なプロパティを指定しています。」
やっと動いたのは
  • プロシージャにはダミー引数を定義する
  • onAction にはシングルクォート・括弧なしで書く
onAction="'foo arg'"
だいぶ気持ち悪いけどやむなし。


ピッとしたらパッと入力させたい

バーコードリーダーでISBNをピッピッと軽快に入力しても、マクロ実行のためにEnter押さなきゃいけなかった。バーコードリーダーを置いて、キーボードに手を持っていくのが地味にめんどくさい。
こんなフォームを作り、ISBNが入力され次第、データを取得してシートに書き出し、次の入力を待つようにした。快適なり。
裏では10桁ないし13桁入力されるまでChangeイベントをスルーするとか、Changeイベントは描画更新前に起きるらしく最後の1桁が表示されないとか、雑な作りだけども、それも自分用マクロの楽しいところ。
もっと適したイベントがあれば知りたい・・・
Private Sub isbn_Change()
    If Len(isbn.Value) < 3 Then
        Exit Sub
    ElseIf (Left(isbn.Value, 3) = "978") And (Len(isbn.Value) < 13) Then
        Exit Sub
    ElseIf (Len(isbn.Value) < 10) Then
        Exit Sub
    End If
    
    Cells(ActiveCell.row, 1).Value = isbn.Value
    Application.Cursor = xlWait
    main.setBookInfo
    Cells(ActiveCell.row + 1, 1).Select
    isbn.Value = ""
    Application.Cursor = xlDefault
    isbn.SetFocus
    
End Sub

コメント

  1. 初めまして。

    エクセルでできる蔵書リストを探しておりましたら、
    こちらに辿り着きました。

    とても便利で使わせていただいております。

    先日、CDやDVDもたくさんあったので管理できると
    嬉しいなと思い自分で試そう
    としたのですが、さっぱりわかりませんでした。

    CDやDVDは無理なのでしょうか、もし出来たら本当に嬉しいです。

    このままでも十分素晴らしいのですが、もし簡単に出来たらと思いコメント
    させていただきました。

    蔵書の管理が便利なりうれしいです。
    本当にありがとうございました。

    返信削除
  2. コメント&ご利用ありがとうございます。

    CDやDVDも、リクエスト・パラメータを以下のように変えれば検索できます。
    私のマクロでいうと、helperモジュールのgetSignedUrlFor()でパラメータを連結している部分をいじります。
    Signature以外のパラメータはアルファベット順に連結しなければならないのが、ちょっとした落とし穴です。

    ■バーコード値で特定の商品を検索する場合(Operation=ItemLookup)、
    ・ItemIdにバーコード値を指定
    ・IdType=EANを追加
    ・SearchIndexを追加(値はMusic、DVD、Videoなど。Allでも可)

    ■歌手名やタイトルで検索する場合(Operation=ItemSearch)
    ・SearchIndexを追加(Music、DVD、Videoなど)
    ・指定したSearchIndexで有効な絞り込みパラメータを使う
     (MusicならArtist、Performer、VideoならActor、Directorなど。
      SearchIndexをAllにしてしまうと、Keywordsしか使えません)

    -------
    また、返ってくるXMLの属性は、検索されたインデックスが何かによって変わるので、値を取り出す処理もそれに応じて変える必要があります。
    マクロでいうと helper.getAttributeMaps() の
    map.Add "title", attributesNode.SelectSingleNode("Title").Text
    などを適切な属性に置き換えます。

    -------
    、、、という感じで、CDやDVDを検索することはできるのですが、
    既存のマクロに統合する良い方法を思いつけません、、、。
    とりあえず、DVDを検索するサンプルを
    https://github.com/anarchiStraw/excel/tree/forDVD
    に置きました。
    https://github.com/anarchiStraw/excel/commit/13716a04704b54454153d5ba7d1e1808a62de044
    で変更箇所を見られます。また何かありましたらコメントください。

    返信削除
  3. 非常に参考になるマクロを公開して頂きありがとうございます。

    ISBNとJANで検索したいのでDVDを検索するサンプルを利用してみました。
    セルにJAN&ISBN入力して「AmazonデータをISBNで検索」をクリックすると
    情報取得できましたが、連続実行でJANを入力すると10桁に変換されてエラーになります。
    VBAを見てもどの辺りを書きかえると良いのか分かりませんでした…

    本/CD/DVD/ゲーム/ホビーの商品管理に使いたいたくて
    私が必要としている機能は、
    A列にISBNかJANを入力したら自動的に
    B列に商品タイトル
    C列にランキング
    D列に価格
    を表示するといった感じです。

    キーワードからの検索や海外検索は使いませんし自動実行をMsgBoxの表示から入力するのではなく
    自動実行を基本動作としてA列セルにJANを連続で入力できるようにしたいと考えており、
    何かよい方法はないかと思いコメントさせて頂きました。

    返信削除
  4. 確認&お返事が遅くなってすみません。

    1) A列に入力したら自動的に実行したい
    下記のように、入力するワークシートのChangeイベントでデータ取得処理を呼べば実行できます。
    注意点としてsetBookInfoは「選択中の行(Selection)」についてデータ取得しますので、ISBN・JANを入力したあと「Enter」ではなく「Ctrl+Enter」で入力値を確定してください。

    ---------------------------------
    Private Sub Worksheet_Change(ByVal Target As Range)
    With Target
    If (.Cells.Count = 1) Then
    If (.Column = 1) And (.Value <> "") Then
    main.setBookInfo
    End If
    End If
    End With
    End Sub
    ---------------------------------

    あと、マクロのなかでもA列を書き換えていて、Changeイベントがループしてしまうので
    main.pasteValues ファンクションの下記行を消してください。
    ----------------------------------
    ws.Cells(row, colIsbn).Value = map("ean")
    ----------------------------------

    ただ、マクロはバックグラウンド実行できませんので、「データ取得中に次のISBNをどんどん入力する」のは無理です。
    なので、現状の 「入力・入力・入力→実行(3件取得) 」方式に比べて
    検討されている「入力→実行→入力→実行」方式 がすごく便利かどうか・・・?
    という気がしますが、まぁそこは好みや操作方法次第ですね。

    JANが10桁に変換されてしまう件は、長くなるので次のコメントで書きます。

    返信削除
  5. 2) JANが10桁に変換されてしまう
    これはですね・・・もともと私が作った目的が「ISBNで本を検索する」だったので、JANやEANを考えない作りになっています。

    具体的には、Amazon Product Advertising API へのリクエストで
     ・IdTypeを指定しない → ASINと判断される
     ・ItemIdには10桁のISBNを指定 → ASIN=10桁のISBN なので
     ・SearchIndex → 不要
    ということです。つまり13桁のISBNは10桁に変換しています。

    amiさんのニーズですと
     ・IdType=EAN
     ・ItemId ・・・EAN、JAN、13桁ISBN はそのまま。10桁ISBNは13桁に変換
     ・SearchIndex=All
    加えて商品・ランクを取得するために
     ・ResponseGroup=Large
    と、リクエストパラメータを変えればよいと思います。
    リクエストパラメータの生成は helper.signedUrlFor
    レスポンスから値を取り出すのは helper.getAttributeMaps
    セルへの書き出しは main.pasteValues
    でやっています。

    また、マクロ実行時に、リクエストしたURLをイミディエイトウィンドウに出力しており、
    これをWebブラウザからリクエストすると、結果のXMLが見られます。
    (有効期間が短いので急ぐ必要ありますが)
    結果XMLのどこに欲しいデータがあるか調べるのに便利です。

    いちおう動く形のをこちらに入れました。
    https://github.com/anarchiStraw/excel/tree/EAN-ALL-Large

    ISBN10桁→13桁変換はしていません。
    きっちり動作確認とか、不要な処理を削るとかもしていませんので、あくまでご参考まで、でお願いします。

    返信削除
  6. anarchiStrawさん 丁寧な回答ありがとうございます。

    たしかに「Ctrl+Enter」では余計な手間が増えてしまいますから
    JAN/ISBNをまとめて入力して、後からデータを取得する方が現実的のようです。

    anarchiStrawさんに作って頂いたコードとVBAの本を見ながら
    ここは何を書いてあるのだろうかと悩み格闘する日々です(笑)

    VBAを学ぶ良い機会となっていますから、なんとか自分が求めるものを
    作リたいと思います。

    また分からない点を質問させて頂くことがあるかと思いますが
    ご指導よろしくお願いいたします。 感謝



    返信削除
  7. anarchiStrawさん

    10桁のISBNデータが取得できないのを何とかしたいと思っていますが、
    helperのSelect Case Len(isbn)以下の部分でCase10を
    13桁に変換させるようなコードの追加が必要になるのでしょうか?

    返信削除
  8. amiさん。また遅い返事ですみません。もう解決してるかもしれませんが

    二通りの対応が考えられます。
    A) 10桁のISBNを13桁に変換し、EAN扱いで検索する
    B) IDの変換はせず、10桁ならIdType=ISBN、そうでなければEANとして検索する

    Aならば
    ・toAsin()と似た構造でtoEan()を作り、入力値が10桁の場合、13桁ISBNに変換する
    ・mainでtoAsinでなくtoEanを呼ぶ
    ・(副作用として)helper.endpointsでの国判定時に"978"を取り除く

    Bならば
    ・helper.toAsin() は不要なので削除
    ・trimとハイフン除去は必要ならする(mainででも、何か関数ででも)
    ・helper.signedUrlForで指定するIdTypeを、ISBNかEANか切り替える。安直には
      & IIf(IsMissing(asin), "", "&IdType=" & IIf(Len(asin) = 10, "ISBN", "EAN")) _

    Aの例をgithubにコミットしました。参考にしてください。
    https://github.com/anarchiStraw/excel/tree/EAN-ALL-Large

    返信削除
  9. お忙しいところ何回も質問して申し訳ありません。

    いろいろ方法があるのですね。
    参考にさせていただき少しずつ勉強していきます。
    本当にありがとうございました。

    返信削除

コメントを投稿

このブログの人気の投稿

GAS 同一カラムを複数条件でfilterできない件

Google Apps Script でSpreadsheetをデータベース代わりにwebアプリをポチポチ作ってて、スクリプトからSpreadsheetにフィルタかける場合、同一カラムに複数条件を設定できないことを知って残念……。 フィルタのかけ方はこんな感じ。 日付を範囲指定したいけど、After、Before片方しかつけられない。 var dataFile = SpreadsheetApp.openById(DATA_FILE_ID); var sh = dataFile.getSheetByName(LOG_SHEET_NAME); var criteriaDateAfter = SpreadsheetApp.newFilterCriteria().whenDateAfter(new Date(targetDate)).build(); var criteriaUserEqual = SpreadsheetApp.newFilterCriteria().whenTextEqualTo(user.id).build(); if (sh.getFilter()) { sh.getFilter().remove(); } var r = sh.getRange(1, 1, sh.getLastRow(), sh.getLastColumn()) .createFilter() .setColumnFilterCriteria(DATE_COL, criteriaDateAfter) .setColumnFilterCriteria(USER_ID_COL, criteriaUserEqual) .getRange(); FilterCriteriaに2回条件設定してみたり var criteriaDateRange = SpreadsheetApp.newFilterCriteria() .whenDateAfter(new Date(date1)) .whenDateBefore(new Date(date2)).build(); 同じカラムに2回条件設定してみたり var r = sh.getRange(1, 1, sh.get

兵士というもの

体制への信頼、懐疑、雰囲気といった変化していく現象を、あとから測定するためのひとつの方法が、行動を突き止めるというやり方である。 国立銀行への貯蓄高、死亡広告の文面、アドルフと命名された子どもの数、教会脱退者などなど。民族同胞の雰囲気が頂点に達したのは1937年から1939年の間、1941年以降は急速に低下した。 政治的に始まった抑圧を日常的実践へと移しかえたのは、人々が受動的であり、抑圧を容認し、批判的な言動を同じ考えの持ち主の間でしか行わなかったからであった。p.55 これこそが、近代的人間が一見暴力とは無縁であるかのように見える原因なのだ。人々は暴力を想定しておらず、暴力が起きたならば、それはなぜなのかつねに説明を探し求める。たとえ、何らかの手段としての暴力ではなかったとしてもである。それにたいして、自らの身体的不可侵性が保障されていると信じていない者は、常に暴力を想定し、それが起きても動揺することはない。したがって信頼と暴力のバランスはつねに微妙で難しいものとなる。p.77 暴力というものが反文明的なものであり、抑圧されなければならず、深刻な場合には撲滅しなければならないものという形を取るようになったのは、歴史的に見ればようやく近代になってからのことである。暴力それ自体が非難されるべきこととされ、もちろん手段としての暴力は避けられないとしてもその都度正当化が必要とされるか、もし起こってしまったとすれば説明が必要なものとなった。p.78 しかしたとえば、人間が性欲を持つということに理由づけは必要だろうか。食べたり飲んだり息をしたりすることに[中略]したがって説明が求められるのはその様態であって、根本的な動機ではないのだ。おそらく暴力の場合にも、そのように考えることが有益であろう。[中略]結局のところ人類が生き延びたのは、平和を作り出す能力ゆえではなく、狩猟のさいや、食料を争うあらゆる種類のライバルにたいして行使した暴力ゆえなのである。p.78 家庭という領域では依然としてパートナーや子供、ペットに対する暴力が存在しているし、教会や学生寮といった閉鎖された社会領域でも同様である。[中略]おそらく、日常生活から暴力が無縁になるにつれ、象徴的もしくは代理的に行使される暴力への欲求が高まるのだろう。そして国家間でも依然として、暴力は独占からはほど遠い。p.79

佐藤忠良・安野光雅『ねがいは「普通」』

創作の目標のひとつにしたい佐藤忠良さんの対談。 絵をまた描きはじめた目で読むと、あちこち響いてくる。 満州に行かされていたんです。じきにソ連が参戦し、突撃ってことになって――。僕は戦線から逃げ出したんですよ。隊長を誘惑してね。その時、逃げるっていっても行く先がはっきりしないんですよ。日本海を泳いで帰るわけにもいかない。あのころ三十三歳くらいでしたか、元気だったんですね。先が見えないなら、地続きの、かねて憧れていたパリまで、歩いて行くより仕方がない――真剣に考えたんです。 人の顔をつくるとき、その人の怒りや喜びや過ごしてきた時間――粘土の中にね、過去と現在と未来までも、かっこいい言い方すると時間性をぶち込もうとするんです。それが彫刻家の苦しさだと思う。[中略]永く鑑賞に耐える芸術は、時間性を持たなくては――。  彫刻って、手でいちばん苦労するんです。手の動き方一つで、きざになったり、甘ったれたものになってしまったり。手の位置にも苦労しますね。 たとえばリンゴを描きたいというときは、その作家の全内容が投影して、書きたくなるわけです。描きたいなって思ったときに、作者のあらゆる哲学的なものや、思想的なものが投影できれば、絵のリンゴのほうが実際のリンゴよりよく見えてくる――。 でも、シベリアに抑留されていた三年間、男ばかりで過ごしていると、本当に、すべてのことを見せ合ってしまう。その時、我々日本人っていうのは、教養と肉体がバラバラになっていると思いました。 僕は日本にいるときから、ヤギなんかずいぶん描いていて、シベリアにいたときは描く紙がないから、心に絵を描くみたいにしてヤギを見ていたんです。 いや、目の前にして言うのはなんですが、何度見ても飽きないものをつくるのは難しいことです。特に気品あるものを作るというのは――。(安野) 僕はいつも思うんですよ。隣人への憐れみがない芸術は嘘ですよ。<中略>気品のないもの、隣人へのいたわりのないものから本物の芸術は生まれてこない。芸術だけではないのですが。 (山根 いずれにせよ作品にまとめるとか、彫刻に生かそうということで、お二人はデッサンをなさるわけですよね) まあそうですが。彫刻に生かそうというか、何か栄養を蓄積するようなことなんですよ。<中略> 素描