はじめに
今までの要素を組み合わせて、せどり商品選定の自動化ワークフローの完全版を作成します。
ワークフロー完全版の全体図は下記となります。
<ワークフロー全体図>
本ワークフローを実行すると、下記ようなCSVファイルが生成されます。
<CSVファイルのサンプル>
CSVファイルの中身は、ChatGPTが選定した商品をYahoo!ショッピング、楽天市場、eBayで検索し、その結果が記録されています。CSVファイルの各カラムには下記の情報が記載されています。
- キーワード
 各ECサイトで検索時に使用した単語
- 商品名
 検索でヒットした商品のタイトル
- 店舗名
 出品店舗・ショップ名
- 商品説明
 商品の短い説明文。説明が取得できない場合は空欄
- 価格
 表示価格の数値。eBayはドル価格
- 検索サイト
 どのECサイトの検索結果を示す(Yahoo/楽天/eBay)
- 商品ページ
 商品詳細ページのURL。
- 検索日時
 その行(商品)を取得した時刻(JST)
ワークフローの動作概要
作成するワークフローの動作概要は下記となります。
事前に用意しておく情報
各サイトのAPI
本手順でワークフローを作成する前に、下記の情報を用意しておく必要があります。
- eBayのアクセストークン、リフレッシュトークン、認証コード
 ※認証コードは、ClientIDとClientを組み合わせてBase64コード
- YahooショッピングのAPIキー
- 楽天市場のAPIキー
上記の情報の取得方法は下記を参照してください。
n8nでせどり商品選定の自動化③(各ECサイトのAPI取得) – リラックスした生活を過ごすために
ebayのアクセストークンを保存したCSVファイル
ebayのアクセストークンの有効期限は120分です。
アクセストークンはeBayのデベロッパーサイトにアクセスすれば取得可能ですが、ワークフローを実行する前に、毎回取得するのは手間が掛かります。
アクセストークンは、リフレッシュトークンを用いることで取得することも可能です。
ワークフロー内でebayのアクセストークンの有効期限を確認し、有効期限が切れそうな場合はリフレッシュトークンでアクセストークンを取得するようにしています。
アクセストークンの有効期限、アクセストークンをリフレッシュトークンで取得するための情報を下記のCSVファイルに保存します。
<CSVファイルのパス>
C:\せどり商品検索の自動化\アクセストークン\ebay_アクセストークン.csv「ebay_アクセストークン.csv」には下記カラムで情報を記載してください。
- access_token:取得したアクセストークン
- expires_in:7200
- token_type:User Access Token
- token_acquired_at:取得した時刻
- auth_basic:Basic 取得した認証コード
- refresh_token:取得したリフレッシュトークン
「auth_basic」には認証コードを設定します。
認証コードを記入する際は「Basic VHN1~~」のように「Basic」を認証コードの前に付けてください。
<ebay_アクセストークン.csvのサンプル>
ワークフローの作成手順
「ebay_アクセストークン.csv」を「C:/せどり商品検索の自動化/アクセストークン/ebay_アクセストークン.csv」に配置した前提で進めます。
ワークフローの作成手順
ワークフローの作成手順は下記を参照してください。
n8nでせどり商品選定の自動化①(n8nの実行環境の構築) – リラックスした生活を過ごすために
「ebayトークン管理」の作成手順
1.「Add first step…」をクリックします
2.「Search nodes…」に「Read」を入力し、「Read/Write Files from Disk」をクリックします。
3.「Read Files(s) From Disk」をクリックする
4.追加したノードに下記を適用して「Back to canvas」をクリックします。
- ノード名:ebayアクセストークン読み込み1
- File(s) Selector:C:/せどり商品検索の自動化/アクセストークン/ebay_アクセストークン.csv
 ※ファイルパスは「¥」ではなく「/」で区切ります。
5.「ebayアクセストークン1読み込み」の「+」をクリックし「Search nodes…」に「Extract」を入力し、「Extract from File」をクリックします。
6.「Extract from CSV」をクリックします。
7.追加したノードに下記を適用して「Back to canvas」をクリックします。
- ノード名:ebay アクセストークン抽出1
- Add option:Header RowをOn
8.「ebay アクセストークン抽出1」の「+」をクリックし「Search nodes…」に「if」を入力し、「If」をクリックします。
9.Ifノードでは「ebay アクセストークンを取得した時刻から何分経ったか」を計算し、しきい値と比較してTrue/Falseで処理を分岐しています。
ebay アクセストークンの有効期限は120分です。現在の時刻とアクセストークンを取得した時刻の差分(取得からの経過時間)が110分以内であれば、Trueと判定しています。True側は後続処理に進みます。False側はリフレッシュトークンで新しいアクセストークン取得の処理に進みます。
追加したノードに下記を適用して「Back to canvas」をクリックします。
- ノード名:ebayアクセストークン取得日時の判定
- fx:{{ Math.floor((Date.now() – Date.parse($json.token_acquired_at)) / 60000) }}
- is less than or equal to
- 110
10.「ebayアクセストークン取得日時の判定」の「false」の「+」をクリックし「Search nodes…」に「http」を入力し、「HTTP Request」をクリックします。
11.HTTP Requestノードでは「ebayのリフレッシュトークンを使って access_token を再発行する」を行います。
追加したノードに下記を適用して「Back to canvas」をクリックします。
- ebayアクセストークン取得
- Method:POST
- URL:https://api.ebay.com/identity/v1/oauth2/token
- Authentication:None
- Send Header:ON
- Specify Headers:Using Fields Below
- Header Parameters
- Name:Content-Type
- Value:application/x-www-form-urlencoded
 ※「Add Parameter」をクリックすると項目を追加できます
- Name:Authorization
- Value:{{ $json.auth_basic }}
 
- Send Body:ON
- Body Content Type:Form Urlencoded
- Specify Body:Using Fields Below
- Body Parameters
- Name:grant_type
- Value:refresh_token
 ※「Add Parameter」をクリックすると項目を追加できます
- Name:refresh_token
- Value:{{ $json.refresh_token }}
 
12.「ebayアクセストークン取得」の「+」をクリックし「Search nodes…」に「code」を入力し、「Code」をクリックします。
13.ebayアクセストークンの取得時刻を設定します。
追加したノードに下記を適用して「Back to canvas」をクリックします。
- ノード名:ebayアクセストークン取得時刻を追加
- JavaScript:下記を記載
 clientId、clientSecret、refreshTokenは事前に取得しておいてください。
// === 設定 ==========================
const clientId     = 'XXXXXX-MySedori-PRD-XXXXX-XXXXXX';
const clientSecret = 'PRD-XXXXXX-XXXXX-XXXXXX-XXXXX-XXXXX';
const refreshToken = 'v^1.1#i^XXXXXXXXXXXXXXXXXX';
// ==================================
// 既存のデータ(HTTP Request のレスポンス)を取得
const inputData = items[0]?.json ?? {};
// JST時刻を付与(yyyy-MM-dd HH:mm:ss)
const jstTime = new Date(Date.now() + 9 * 60 * 60 * 1000)
  .toISOString().replace('T', ' ').substring(0, 19);
// Basic 認証文字列を生成("Basic xxxxxx")
const auth_basic = 'Basic ' + Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
// 出力データを作成
const outputData = {
  ...inputData,                // access_token / expires_in など元の応答
  token_acquired_at: jstTime,  // 取得時刻(JST)
  auth_basic,                  // Basic 認証ヘッダ値
  refresh_token: refreshToken  // リフレッシュトークン
};
// 1件で返す
return [{ json: outputData }];14.「ebayアクセストークン取得時刻を追加」の「+」をクリックし「Search nodes…」に「convert」を入力し、「Convert to File」をクリックします。
15.「Convert to CSV」をクリックします。
16.ebayアクセストークンと取得時刻をCSV形式に変換します。
追加したノードに下記を適用して「Back to canvas」をクリックします。
- ノード名:ebayアクセストークンのCSV形式に変換
- Options:Add option」をクリックして下記を追加
- Delimiter: ,
 
17.「ebayアクセストークンのCSV形式に変換」の「+」をクリックし「Search nodes…」に「write」を入力し、「Read/Write Files from Disk」をクリックします。
18.「Read File(s) From Disk」をクリックします。
19.CSV形式のデータを保存します。
追加したノードに下記を適用して「Back to canvas」をクリックします。
- ノード名:ebayアクセストークンの保存
- Operationdw」で「Write File to Disk」を選択
20.「ebayアクセストークンの保存」の右「●」をドラックして「ebayアクセストークン読み込み1」の左「●」でドロップし、線を繋げます。
以上で「ebayトークン管理」の作成手順は完了です。
「商品検索候補の生成」の作成手順
1.AI Agentによる商品選定のフローを作成します。
作成方法は下記を参照してください。
n8nでせどり商品選定の自動化②(AI Agentによる商品選定) – リラックスした生活を過ごすために
「Yahooショッピングの商品検索」の作成手順
1.「JSON形式のデータを配列に変換」の右「●」をクリックし「Search nodes…」に「loop」を入力し、「Loop Over Items(Split in Batches)」をクリックします。
2.ループ設定の変更はありません。
追加したノードの「Back to canvas」をクリックします。
3.不要な「Replace Me」ノードをクリックし「ゴミ箱」アイコンをクリックして削除します。
4.不要なループを削除します。
「Loop Over Items」の下から出ているループにカーソルを合わせて「ゴミ箱」をクリックします。
5.Yahooショッピングで商品検索を実行するためのノードを作成します。
「Loop Over Items」の下「+」をクリックし「Search nodes…」に「http」を入力し、「HTTP
Request」をクリックします。
6.YahooショッピングのAPI検索設定を行います。
追加したノードに下記を適用して「Back to canvas」をクリックします。
- ノード名:Yahoo API検索
- URLに下記を入力します。
https://shopping.yahooapis.jp/ShoppingWebService/V3/itemSearch?appid=【取得したYahoo API】&query={{ encodeURIComponent($json.キーワード) }}&results={{ $json.検索数 }}※Yahoo APIの取得方法は前回の記事を参照してください。
以上で「Yahooショッピング」の商品検索の作成手順は完了です。
「楽天市場の商品検索」の作成手順
1.楽天市場で商品検索を実行するためのノードを作成します。
「Loop Over Items」の下「+」をクリックし「Search nodes…」に「code」を入力し、「Code」をクリックします。
2.楽天市場でAPI検索を行うための前処理を行います。
楽天市場のAPI検索で「iphone 12」を検索すると失敗します。
「iphone 12」を単語単位で分解すると「キーワード」+「スペース」+「数字」の形式になっています。この「キーワード」+「スペース」+「数字」の形式だと、楽天市場のAPI検索は失敗してしまいます。
楽天市場のAPI検索をする際は「スペース」を削除した「キーワード」+「数字」に変換する必要があります。
<失敗するキーワードの例>
- iphone 12
- Mx Anywhere 3
<成功するキーワードの例>
- iphone12
- MxAnywhere3
追加したノードに下記を適用して「Back to canvas」をクリックします。
- ノード名:楽天API検索用キーワード生成
- JavaScriptに下記を入力します。
// 楽天API用:「スペース+数字」問題の解決
const originalKeyword = $json.キーワード;
// 「アルファベット/カタカナ + スペース + 数字」を「アルファベット/カタカナ + 数字」に変換
const fixedKeyword = originalKeyword
  .replace(/([a-zA-Z\u30A0-\u30FF])\s+(\d)/g, '$1$2')  // Master 3 → Master3
  .replace(/\s+/g, ' ')                                 // 余分な空白を整理
  .trim();
return items.map(item => ({
  json: {
    ...item.json,
    fixedKeyword: fixedKeyword
  }
}));
// 楽天API用:「スペース+数字」問題の解決
const originalKeyword = $json.キーワード;
// 「アルファベット/カタカナ + スペース + 数字」を「アルファベット/カタカナ + 数字」に変換
const fixedKeyword = originalKeyword
  .replace(/([a-zA-Z\u30A0-\u30FF])\s+(\d)/g, '$1$2')  // Master 3 → Master3
  .replace(/\s+/g, ' ')                                 // 余分な空白を整理
  .trim();
return items.map(item => ({
  json: {
    ...item.json,
    fixedKeyword: fixedKeyword
  }
}));
3.楽天市場で商品検索を実行するためのノードを作成します。
「楽天API検索用キーワード生成」の「+」をクリックし「Search nodes…」に「http」を入力し、「HTTP Request」をクリックします。
4.楽天市場のAPI検索設定を行います。
追加したノードに下記を適用して「Back to canvas」をクリックします。
- ノード名:楽天API検索
- URLに下記を入力します。
https://app.rakuten.co.jp/services/api/IchibaItem/Search/20220601?format=json&keyword={{ encodeURIComponent($json.fixedKeyword) }}&hits={{ $json.検索数 }}&applicationId=【取得した楽天API】/※楽天APIの取得方法は前回の記事を参照してください。
以上で「楽天市場の商品検索」の作成手順は完了です。
「ebayの商品検索」の作成手順
1.ebayでAPI検索を行うための前処理を行います。
「Loop Over Items」の下「+」をクリックし「Search nodes…」に「read」を入力し、「Read/write Files from Disk」をクリックします。
2.「Read Files(s) From Disk」をクリックします。
3.追加したノードに下記を適用して「Back to canvas」をクリックします。
- ノード名:ebayアクセストークン読み込み2
- File(s) Selector:C:/せどり商品検索の自動化/アクセストークン/ebay_アクセストークン.csv
4.「ebayアクセストークン読み込み2」の「+」をクリックし「Search nodes…」に「Extract」を入力し「Extract from File」をクリックします。
5.「Extract from CSV」をクリックします。
6.追加したノードに下記を適用して「Back to canvas」をクリックします。
- ノード名:ebay アクセストークン抽出2
- Add option:Header Row」を選択
7.ebayAPI検索で必要なキーワードとAPI情報を結合します。
「Loop Over Items」の下「+」をクリックし「Search nodes…」に「merge」を入力し、「Merge」をクリックします。
8.追加したノードに下記を適用して「Back to canvas」をクリックします。
- ノード名:ebayAPI検索用の情報生成
- Mode:Combine
- Combine by:Position
9.抽出したebayアクセストークンとキーワードを結合します。
「ebay アクセストークン抽出2」の「●」をドラックし「ebayAPI検索用の情報生成」の「Input2」にドロップします。
10.ebayで商品検索を実行するためのノードを作成します。
「ebayAPI検索用の情報生成」の「+」をクリックし「Search nodes…」に「http」を入力し、「HTTP Request」をクリックします。
11.ebayのAPI検索設定を行います。
追加したノードに下記を適用して「Back to canvas」をクリックします。
- ノード名:ebayAPI検索
- URLに下記を入力します。
https://api.ebay.com/buy/browse/v1/item_summary/search- Send Query Parameters:On
- Name:q
- Value:{{ $json[‘keyword’] }}
 
- 「Add Parameter」をクリック
- Name:limit
- Value:{{ $json[‘検索数’] }}
 
- Send Headers:On
- Name:Authorization
- Value:Bearer {{ $json[‘access_token’] }}
 
※ebayAPIの取得方法は前回の記事を参照してください。
以上で「ebayの商品検索」の作成手順は完了です。
3サイトの検索結果の結合
1.各サイトのAPI検索で取得した情報をを結合します。
「Loop Over Items」の下「+」をクリックし「Search nodes…」に「merge」を入力し、「Merge」をクリックします。
2.追加したノードに下記を適用して「Back to canvas」をクリックします。
- ノード名:各サイトの検索結果のまとめ
- Number of Inputs:4
3.「Yahoo API検索」「楽天API検索」「ebayAPI検索」の「●」をドラックし「各サイトの検索結果のまとめ」のInput2~4に順々にドロップします。
4.取得した各サイトの検索結果を下記のフォーマットに整理するノードを作成します。
- キーワード、商品名、店舗名、商品説明、価格、価格サイト、商品ページ、検索日時
「各サイトの検索結果のまとめ」の「+」をクリックし「Search nodes…」に「code」を入力し、「Code」をクリックします。
5.追加したノードに下記を適用して「Back to canvas」をクリックします。
- コード名:取得情報の整理
- 「JavaScript」に下記を設定
const results = [];
// ---------- キーワード抽出ユーティリティ ----------
function pickKeywordFromJson(j) {
  const str = v => (typeof v === 'string' ? v.trim() : null);
  const cands = [
    j['キーワード'],
    j.keyword,
    j.request?.query,              // Yahoo等
    j.request?.q,                  // eBay等
    j.request?.params?.keyword,    // 楽天等
    j.request?.params?.q,
    j.query,
    j.q,
    j.input?.keyword,
    j.input?.['キーワード'],
  ];
  for (const v of cands) {
    const s = str(v);
    if (s) return s;
  }
  // request が配列の場合の保険
  if (Array.isArray(j.request)) {
    for (const r of j.request) {
      const s = str(r?.query || r?.q || r?.params?.keyword || r?.params?.q);
      if (s) return s;
    }
  }
  return null;
}
// 全アイテムを走査して最初に見つかったキーワードを採用
let keyword = null;
for (let i = items.length - 1; i >= 0; i--) { // 後ろから探す
  const k = pickKeywordFromJson(items[i].json || {});
  if (k) {
    keyword = k;
    console.log('Keyword found at index', i, ':', k);
    break;
  }
}
if (!keyword) keyword = 'N/A';
// ---------- 日本時間 ----------
const searchDatetime = new Date().toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' });
// ---------- 各APIの結果処理 ----------
let yahooProcessed = false;
let rakutenProcessed = false;
let ebayProcessed = false;
for (const item of items) {
  const json = item.json || {};
  // Yahoo
  if (Array.isArray(json.hits)) {
    console.log('Processing Yahoo data, hits:', json.hits.length);
    yahooProcessed = true;
    if (json.hits.length > 0) {
      for (const hit of json.hits) {
        results.push({
          キーワード: keyword,
          商品名: hit.name || '',
          店舗名: hit.seller?.name || '',
          商品説明: (hit.description || '').replace(/
/g, ' ').replace(/<[^>]*>/g, ''),
          価格: hit.price ?? '',
          検索サイト: 'Yahoo',
          商品ページ: hit.url || '',
          検索日時: searchDatetime,
        });
      }
    } else {
      results.push({
        キーワード: keyword,
        商品名: '',
        店舗名: '',
        商品説明: '',
        価格: '',
        検索サイト: 'Yahoo',
        商品ページ: '',
        検索日時: searchDatetime,
      });
    }
    continue;
  }
  // 楽天
  if (Array.isArray(json.Items)) {
    console.log('Processing Rakuten data, items:', json.Items.length);
    rakutenProcessed = true;
    if (json.Items.length > 0) {
      for (const wrapper of json.Items) {
        const hit = wrapper.Item || {};
        results.push({
          キーワード: keyword,
          商品名: hit.itemName || '',
          店舗名: hit.shopName || '',
          商品説明: (hit.itemCaption || '').replace(/
/g, ' ').replace(/<[^>]*>/g, ''),
          価格: hit.itemPrice ?? '',
          検索サイト: '楽天',
          商品ページ: hit.itemUrl || '',
          検索日時: searchDatetime,
        });
      }
    } else {
      results.push({
        キーワード: keyword,
        商品名: '',
        店舗名: '',
        商品説明: '',
        価格: '',
        検索サイト: '楽天',
        商品ページ: '',
        検索日時: searchDatetime,
      });
    }
    continue;
  }
  // eBay
  if (Array.isArray(json.itemSummaries)) {
    console.log('Processing eBay data, items:', json.itemSummaries.length);
    ebayProcessed = true;
    if (json.itemSummaries.length > 0) {
      for (const hit of json.itemSummaries) {
        console.log('eBay item:', hit.title, hit.seller?.username, hit.price?.value);
        results.push({
          キーワード: keyword,
          商品名: hit.title || '',
          店舗名: hit.seller?.username || '',
          商品説明: '', // eBay は説明未取得
          価格: hit.price?.value ?? '',
          検索サイト: 'eBay',
          商品ページ: hit.itemWebUrl || '',
          検索日時: searchDatetime,
        });
      }
    } else {
      results.push({
        キーワード: keyword,
        商品名: '',
        店舗名: '',
        商品説明: '',
        価格: '',
        検索サイト: 'eBay',
        商品ページ: '',
        検索日時: searchDatetime,
      });
    }
    continue;
  }
  // 不明な構造のログ(参考)
  if (Object.keys(json).length > 0) {
    console.log('Unknown data structure keys:', Object.keys(json));
  }
}
// どのAPIも処理されなかった場合のフォールバック
if (!yahooProcessed && !rakutenProcessed && !ebayProcessed) {
  console.log('No API data found, create empty records.');
  for (const site of ['Yahoo', '楽天', 'eBay']) {
    results.push({
      キーワード: keyword,
      商品名: '',
      店舗名: '',
      商品説明: '',
      価格: '',
      検索サイト: site,
      商品ページ: '',
      検索日時: searchDatetime,
    });
  }
}
console.log('Total results generated:', results.length);
if (results[0]) console.log('Sample result:', results[0]);
return results.map(r => ({ json: r }));6.各サイトに負荷を掛けないように待ち時間を設定します。
「取得情報の整理」の「+」をクリックし「Search nodes…」に「wait」を入力し、「Wait」をクリックします。
7.追加したノードに下記を適用して「Back to canvas」をクリックします。
- ノード名:待ち
- Wait Amount:2
8.検索条件の内容で繰り返し検索するようにします。
「待ち」の「●」をドラックし「Loop Over Items」の左「■」にドロップします。
以上で3サイトの検索結果の結合は完了です。
取得情報のCSVファイルへ保存
1.取得情報をCSVファイルに保存します。
「Loop Over Items」の上「+」をクリックし「Search nodes…」に「convert」を入力し「Convert to File」をクリックします。
2.「Convert to CSV」をクリックします。
3.追加したノードに下記を適用して「Back to canvas」をクリックします。
- コード名:取得情報をCSV形式に変換
- Add option:Delimiter
4.「取得情報をCSV形式に変換」の「+」をクリックし「Search nodes…」に「write」を入力し「Read/write Files from Disk」をクリックします。
5.「Write File to Disk」をクリックします。
6.追加したノードに下記を適用して「Back to canvas」をクリックします。
- ノード名:取得情報をCSV形式で保存
- 「File Path and Name」に下記を設定
C:/せどり商品検索の自動化/検索結果/せどり_分析_{{ $now.plus({hours: 13}).format('yyyy-MM-dd_HHmmss') }}.csv以上で取得情報のCSVファイルへ保存は完了です。
ワークフローの実行
「Excute workflow」をクリックすると作成したワークフローを実行します。
実行後にエラーが発生しているノードが無いことを確認します。
実行が完了すると検索結果が下記ファイルに保存されます。
C:\せどり商品検索の自動化\検索結果\せどり_分析_yyyy_mm_dd_hhMMss.csv<せどり_分析のサンプル>
以上です。

コメント