n8nによる「本格的ワークフロー」のハンズオン第5回です。
今回はLoop処理内で処理中のカレントデータを、Loop内で共用できるレコードのように扱っていく方法を見ていきましょう。
前回までで「記事ごとにページにアクセスして情報を取得する」を取得できるところまで来ました。ここまでのワークフローの構築で、既に「欲しい情報」は全て取得できるようになっています。
- ブログサイトにリクエストして、サムネイルページのHTMLを取得
- 記事一覧のHTMLを解析し、記事群のメタ情報(タイトル・URL・公開日)を配列に取得
- ループを回して記事ごとに本文ページにアクセス
- ループ内で記事ごとにHTMLを解析し、本文・カテゴリ・執筆者を取得
ここまでで作成したワークフローは下図のようになっているはずです。

ここからは、取得できている情報を、活用していく方法を見ていきます。
今回は、情報をより取り扱いやすくするために、Make Fieldsノードを用いて、Loop内でデータを「レコード」として扱っていく方法を見ていきます。
Edit Fieldsノードを使う理由
このハンズオンでは、ここまでのところ、データは「ノードの処理結果」としてノードに紐づいているデータとして、入力値・出力値を取り扱うのみでした。n8nでは、処理済みノードの出力値に $('前処理ノード名') でアクセスできるため、基本的にはそれで本質的に困ることはありません。
ただ、細かく情報処理をしていくにあたっては、ノード単位ではなく「記事」という単位で情報を集約したくなります。各データにノードを介してアクセスはできるものの、現状のワークフローでは、記事の情報が下記のように「散っている」状況です。
■Loop外
- タイトル・URL・公開日:『Code arrange field』の出力値として配列保有
- 本文・カテゴリ・執筆者:『Loop Over Items』の出力値として配列保有
■Loop内(記事ごと)
- タイトル・URL・公開日:『Loop Over Items』のloop側に出力
- 本文・カテゴリ・執筆者:『HTML Parse-each』の出力値として保有
このままでも、データを扱うことは可能ですが、「記事」の単位で情報を扱っていきたい場面では、見通しが悪く取り扱いにくい格好になります。物理的なデータアクセスは可能ですが、プログラミングの世界に慣れてきているエンジニアとしては、概念として「記事」単位のレコードが欲しくなるところです。
そこで、「記事」の塊としてデータをセットするために、Edit Fieldsノードを活用します。
10. Edit Fieldsノード:記事データの集約
Edit Fields ノードを設置してみましょう。

Loop Over Items の loop側に渡される、『タイトル・URL・公開日』をもらってレコードを作成するために、Loop Over Items と HTTP Req-each の間にノードを設置していきます。
例によって設定ダイアログが開くので、Add Field をクリックして下記のように設定していきましょう。
- name:articleTitle
- type:String
- {{ $json.articleTitle }}
- name:articleURL
- type:String
- {{ $json.articleURL }}
- name:articleDate
- type:String
- {{ $json.articleDate }}
- name:objDate
- type:String
- {{ $json.objDate }}

ここでは、loopから入力された値を、そのまま同じ名前で引き継いでいるだけですが、記事単体のデータを「ここで一度集めてレコードっぽくしている」という「概念」をもたせることができて、ワークフローの見通しを良くすることができます。
試運転してみて、下記のように、右辺に記事の情報が引き継がれていればOKです。

ここまでで、ワークフローは以下のようになりました。

Loop Over Itms と HTTP Req-each の間に Make Fieldを挟みましたが、Make Fieldではloopの入力値をそのまま出力値として引き継いでいるので、HTTP Req-each 側は特に変更すること無くこれまでどおり動作します。
11. 2つめのEdit Fieldsノード:記事データの集約2
Loop内での各記事の情報取得で得られたカテゴリ・執筆者・本文を、上記で設置した「概念上の記事レコード」にさらに集約していってみましょう。
Edit Fieldをもう1つ、Loop内の最後、HTML Parse-eachにつなげる形で設置します。

このEdit Fieldは下記の様に設定していきましょう。
- name:articleTitle
- type:String
- {{ $('Make Field').item.json.articleTitle }}
- name:articleURL
- type:String
- {{ $('Make Field').item.json.articleURL}}
- name:articleDate
- type:String
- {{ $('Make Field').item.json.articleDate }}
- name:objDate
- type:String
- {{ $('Make Field').item.json.objDate }}
- name:ArticleAuther
- type:String
- {{ $json.ArticleAuther }}
- name:ArticleCategory
- type:String
- {{ $json.ArticleCategory }}
- name:ArticleContent
- type:String
- {{ $json.ArticleContent }}
タイトル(articleTitle)、記事URL(articleURL)、公開日(articleDate, objDate)は、先程1つ目のEdit Fieldノード として作った 'Make Field'から値をもらってきます。
過去のノードの出力値なので、$('Make Field').item.json.**** で参照します。
執筆者(ArticleAuther)、カテゴリ(ArticleCategory)、記事本文(ArticleContent)は直前ノードで取得されているので、$jsonから取得できます。
この設定で試運転してみて、下記のように右辺に目的に値が出力されていればOKです。

各ノードの出力に散っていた値を、1つのレコードのようにまとめて、後続ノードに出力できていることが分かります。
この2つ目のEditノードは、Loop内処理の最後のノードであるため、この出力はLoopの出力配列にそのままPushされることになります。
ワークフローは以下のようになりました。
結果を見てみる
Loop over Items の出力先のCodeノードのプログラムを少し編集して、結果を見てみましょう。
const customOutput = [];
for (const item of $input.all()) {
const cuOut = {};
const cuIn = item.json;
cuOut["articleTitle"] = cuIn["articleTitle"];
cuOut["articleURL"] = cuIn["articleURL"];
cuOut["articleDate"] = cuIn["articleDate"];
cuOut["ArticleContentHead"] = cuIn["ArticleContent"].substr(0,100);
cuOut["ArticleCategory"] = cuIn["ArticleCategory"];
cuOut["ArticleAuther"] = cuIn["ArticleAuther"];
customOutput.push(cuOut);
}
return customOutput;
前回までの内容だと、Loop内のHTML Parse-each の出力値(本文・カテゴリ・執筆者)しか扱えていませんでしたが、今回はEditFieldsにより値が集約されたので、Loopの出力値として、記事レコード概念のデータを全部(タイトル・URL・公開日・執筆者・カテゴリ・本文)取り扱えるようになっています。

試運転してみると、右辺に「記事の情報」が集約されて出力されていることが分かります。
もうひと工夫してみる
現状では『記事本文の頭出し』の100文字切り出しを、loop外のCodeノードで行っていますが、この処理もLoopの中に入れてみましょう。
同様にLoop内にCodeノードを設置してもよいのですが、簡単な内容であればEdit Fieldsの中で対応できてしまいます。
キャンバスでEdieFieldsをダブルクリックして設定ダイアログを開き、下記を追加(Add Field)してみましょう。
- name:ArticleContentHeadsUp
- type:String
- {{ $json.ArticleContent.substr(0,100) }}
この内容で試運転してみると、下記のように「100文字分切り出した」新しいフィールドが追加されている事がわかります。

この状態で、先程のLoop外のCodeも、値自体の加工はせず出力対象の選択のみに絞ったシンプルなコードに変更します。
const customOutput = [];
const showTarget = [
'articleTitle',
'articleURL',
'articleDate',
'ArticleContentHeadsUp',
'ArticleCategory',
'ArticleAuther'
];
for (const item of $input.all()) {
const cuOut = {};
const cuIn = item.json;
for(const target of showTarget){
cuOut[target] = cuIn[target];
}
customOutput.push(cuOut);
}
return customOutput;
結果を見てみると以下のようになります。

このように、各ノードの役割を明確にし、それぞれは複雑にしないこともポイントになります。
全体系での実行の様子は下記です。最終のCodeノードの出力がLogでも意図したとおりになっていることが見て取れます。

今回はここまでにしましょう。
次回はAIを用いた本文の要約を取り上げていきます。
- トリガーの設置【Done】
- 特定のURLから内容を取得する ▶ HTTP Request ノード【Done】
- HTMLから要素を部会・取得する ▶ HTMLノード【Done】
- 配列データの扱い方と暗黙的ループ処理【Done】
- コードによる自由な処理 ▶ Codeノード【Done】
- 明示的なループ処理 ▶ Loopノード【Done】
- フィールド生成・制御 ▶ MakeFieldsノード【Done:今回解説】
- AIノードへの情報連携 ▶ Basic LLM Chain → NEXT
- 処理済みの任意ノードの値の活用する【Done:今回追加で解説】
- Googleスプレッドシートへのレコード出力 ▶ Google Sheetノード
- Google Sheet API への アクセス許可の手順 ▶ Google Sheets account 設定

