# DifyとGAS連携でWebサイト情報を自動収集！LLMとコード実行ノード活用術 (データ整形編)

# DifyとGAS連携でWebサイト情報を自動収集！LLMとCodeノード活用術 (データ整形編)

## はじめに

こんにちは！今回は、Difyを使って複数のWebサイトから情報を収集し、Google Apps Script (GAS) を経由してGoogleスプレッドシートに自動で記録するワークフローの一部をご紹介します。

特に、LLMが出力した**「構造化されたデータ（JSON配列）」をコード実行ノードで扱いやすい形に整形**し、その後のGAS連携に繋げる部分に焦点を当てて解説します。

「LLMの出力データをプログラムで処理したいけど、どうすればいいの？」という方や、「Difyのコード実行ってどう使うの？」という方の参考になれば嬉しいです。

## ワークフローの全体像（今回解説する部分）

![スクリーンショット 2025-05-09 11.08.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4082672/3ae31184-9301-4030-a2ab-c56746593c93.png)



今回ご紹介するワークフローは、概念的には以下のようになります。

1.  **Webサイト群:** 収集したい情報がある複数のWebサイト。
2.  **Difyワークフロー:**
    * **LLMノード:** Webサイトの内容を読み込み、要約・構造化する。
    * **(今回解説)** **コード実行ノード:** LLMが出力した構造化データを、後続ノード（HTTPリクエストなど）が扱いやすいように整形する。
    * **HTTPリクエストノード:** コード実行ノードで整形したデータをGASのWebアプリに送信する。
3.  **GAS (Google Apps Script):** Difyから送信されたデータを受け取り、Googleスプレッドシートに書き込む。
4.  **Googleスプレッドシート:** 収集・整形された情報が記録される。

**今回は、このDifyワークフローにおける「LLMノード」と「コード実行ノード」の連携部分、特にLLMの出力をコード実行ノードで処理する部分に絞って解説します。**

## Part 1: LLMノードでWebサイト情報を要約・構造化する

まず、DifyのワークフローにLLMノードを追加します。このノードでは、複数のWebサイトのURLを入力として受け取り、LLMにその内容を要約させ、さらに後続の処理で扱いやすいように**特定の構造を持ったJSON形式**で出力させます。

![スクリーンショット 2025-05-09 11.09.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4082672/2615d2d4-bd5f-45f3-afbb-87edb73346ab.png)


### LLMの選定

使用するLLMは、Difyで利用可能なモデルの中から、要約精度やJSON出力の安定性が高いものを選びましょう。最初は検証しやすいモデルから試してみるのがおすすめです。

### プロンプトの設定

LLMに意図した通りの構造で出力させるために、プロンプトが非常に重要になります。今回は、以下のようなプロンプトを設定します。


*あなたはプロフェッショナルな編集者です。
以下のWebサイトの内容を読み込み、各サイト毎に内容を300文字以内で簡潔かつ分かりやすく要約してください。*



**要約した各サイトのデータは、後続の処理（例：GASへのPOST）で扱いやすいように、以下の「##出力形式」に従ったJSON配列として出力してください。**

##出力形式
```JSON：qiita.rb
[
  {
    "overview": "ここに1つ目のサイトの要約内容を記載 (300字以内)"
  },
  {
    "overview": "ここに2つ目のサイトの要約内容を記載 (300字以内)"
  },
  // ... サイトの数だけ繰り返す
]
```
**ポイント:**

* **役割指定**: LLMに「プロフェッショナルなマーケター」としての役割を与えることで、出力内容の質を高めやすくします。
* **出力形式の具体化**: ただ「`JSON`で出して」ではなく、どのようなキー名（今回は`overview`）で、どのような構造（オブジェクトの配列）にしてほしいのかを明確に指示します。マークダウン形式の`JSON` (`` ```json\n...\n``` ``) で囲んで出力するように指示することも、コード実行ノードでの処理を楽にするテクニックです。
* **文字数制限**: 各要約の文字数を指定することで、出力が長くなりすぎるのを防ぎます。
* **GAS連携を意識**: ``keyは overveiw と設定してください`` という指示は、最終的に`GAS`側でデータを受け取って処理する際に、この `overview` というキー名を基準にデータを取り扱えるようにするためです。

### Dify LLMノードの設定 (出力変数名)

`LLM`ノードの設定で、このノードの**出力変数名**を設定します。この名前が、後続の`Code`ノードで入力として参照する際のキーとなります。ここでは、後述の`Code`ノードのコードに合わせて、出力変数名を例えば `site_summaries_raw` のように設定します。

* **変数名**: `site_summaries_raw` (任意ですが、何が出力されるか分かりやすい名前にしましょう)
* **タイプ**: `String` (`LLM`の出力は基本的に文字列です)

## Part 2: コード実行ノードでLLM出力を整形する

`LLM`ノードからの出力は、指定した`JSON`形式になっているはずですが、多くの場合、`` ```json\n `` と `` \n``` `` のようなマークダウンで囲まれた単なる「文字列」として出力されます。また、`LLM`の出力は稀にフォーマットが崩れる可能性もあります。

後続の`HTTP`リクエストノードなどで、このデータを「`JSON`オブジェクトの配列」として正しく扱うためには、この文字列をパースし、必要な部分を抽出する処理が必要です。ここで`コード実行`ノードの出番です。

### なぜコード実行ノードが必要か？

* **マークダウンの除去**: `LLM`が出力する`JSON`文字列は、コードブロックとしてマークダウン記法で囲まれていることが多いため、これを取り除く必要があります。
* **文字列をデータ構造へ変換**: 単なる文字列として受け取った`JSON`を、`JavaScript`が理解できる`JSON`オブジェクトや配列としてパース（解析）する必要があります。
* **必要なデータの抽出**: 今回の場合、各オブジェクトから `overview` の値だけを抽出し、それらをまとめて新しい配列にする、といった整形を行います。

### コード実行ノードの設定

`LLM`ノードの後ろに`Code`ノードを追加します。

![スクリーンショット 2025-05-09 11.15.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4082672/e190d9e1-2310-4f00-b5e1-4e3f5465585c.png)


* **入力変数**:
    前の`LLM`ノードの出力変数名（例: `site_summaries_raw`）と同じ名前で入力変数を設定します。
    * **変数名**: `site_summaries_raw`
    * **タイプ**: `String`

```JavaScript：qiita.rb
function main({ site_summaries_raw }) {
  // コード実行ノードの入力変数名 'site_summaries_raw' から文字列を取得
  const rawJsonString = site_summaries_raw;

  // 入力文字列がnull, undefined, または空文字でないことを確認
  if (!rawJsonString) {
      console.error("入力データがありません。");
      return {
          summaries_for_gas: [] // 空の配列を返す
      };
  }

  // マーダウン形式 (```json\n と \n```) を取り除く
  // ※LLMの出力によっては不要な場合もありますが、汎用性を考慮して記述
  let jsonString = rawJsonString.replace('```json\n', '').replace('\n```', '');

  // 前後の余分な空白文字も取り除く
  jsonString = jsonString.trim();

  let dataArray = [];
  try {
      // JSON文字列をパースしてJavaScriptのオブジェクト/配列に変換
      // ※エラーが発生する可能性があるのでtry-catchで囲むのが安全です
      dataArray = JSON.parse(jsonString);
  } catch (error) {
      console.error("JSONのパースに失敗しました:", error);
      // パース失敗時は空の配列、またはエラーを示す値を返すなどの対応が可能
      return {
          summaries_for_gas: [] // パース失敗時は空の配列を返す例
      };
  }


  // dataArray が配列であり、かつ各要素が期待する形式であるかを確認しながら処理
  // 各要素から 'overview' の値を抽出し、新しい文字列の配列を作成します。
  const extractedOverviews = [];
  if (Array.isArray(dataArray)) {
      for (const item of dataArray) {
          // 各要素がオブジェクトであり、かつ 'overview' プロパティ(文字列)を持つか確認
          if (item && typeof item === 'object' && typeof item.overview === 'string') {
              extractedOverviews.push(item.overview);
          } else {
              // 予期しない形式の配列要素があった場合のログ出力やスキップ
              console.warn("予期しない形式の配列要素をスキップします:", item);
          }
      }
  } else {
      console.error("パース結果が配列ではありませんでした:", dataArray);
      // パース結果が配列でない場合の処理
      return {
          summaries_for_gas: [] // 配列でない場合は空の配列を返す例
      };
  }

  // コード実行ノードの出力として、抽出した配列を 'summaries_for_gas' キーに格納して返す
  return {
      summaries_for_gas: extractedOverviews
  };
}
```
### コード解説:

1.  `function main({ site_summaries_raw }) { ... }`: Difyのコード実行ノードのエントリーポイントとなる関数です。 `{ site_summaries_raw }` の部分で、入力変数として設定した `site_summaries_raw` の値を取得しています。
2.  `const rawJsonString = site_summaries_raw;`: 入力された文字列を変数に格納します。
3.  `if (!rawJsonString) { ... }`: 入力データが空でないかチェックする基本的なエラー処理です。
4.  `let jsonString = rawJsonString.replace(...)`: LLMの出力によく付与される `` ```json\n `` と `` \n``` `` というマークダウン文字列を取り除きます。
5.  `jsonString = jsonString.trim();`: 前後の余分な空白文字を取り除きます。
6.  `try { ... } catch (error) { ... }`: `JSON.parse()` は、入力文字列が不正な形式だとエラーになります。必ず `try-catch` で囲み、エラー発生時の挙動を定義しましょう。
7.  `dataArray = JSON.parse(jsonString);`: クリーンアップした文字列を、JavaScriptのオブジェクトや配列に変換（パース）します。LLMへのプロンプト通りであれば、ここではオブジェクトの配列になります。
8.  `const extractedOverviews = []; if (Array.isArray(dataArray)) { ... }`: パース結果が本当に配列であるかを確認し、処理を進めます。
9.  `for (const item of dataArray) { ... }`: 配列 `dataArray` の各要素を順番に処理します。
10. `if (item && typeof item === 'object' && typeof item.overview === 'string') { ... }`: 各要素が、存在するオブジェクトであり、かつ `overview` という名前の文字列型のプロパティを持っているかを確認しています。これにより、LLMが意図しない形式のデータを出力した場合でもエラーを防ぎ、安全に処理できます。
11. `extractedOverviews.push(item.overview);`: 条件を満たした要素から `overview` の値（要約文字列）を取り出し、新しい配列 `extractedOverviews` に追加します。
12. `return { summaries_for_gas: extractedOverviews };`: コード実行ノードの処理結果を、出力変数名 `summaries_for_gas` に格納して返します。この `extractedOverviews` は文字列の配列になっています。

**コード実行ノードの設定確認:**

* 入力変数名がLLMノードの出力変数名と一致しているか？ (`site_summaries_raw`)
* 出力変数名が任意の名前に設定されているか？ (`summaries_for_gas`)
* 出力変数のタイプが `Array[String]` になっているか？

これが正しく設定できていれば、このコード実行ノードの出力変数 `summaries_for_gas` には、各Webサイトの要約（文字列）が配列として格納されます。

## Part 3: ワークフローのその後（HTTPリクエストノードとGAS）

コード実行ノードで整形された文字列の配列 `summaries_for_gas` は、そのまま`HTTPリクエストノード`の入力として使用できます。

`HTTPリクエストノード`の設定で、`GAS`のWebアプリのURLを指定し、コード実行ノードの出力変数 `summaries_for_gas` をリクエストボディなどに含めて`POST`すれば、`GAS`側でデータを受け取ってスプレッドシートに書き込む、という流れが実現できます。

`GAS`側のコードでは、`POST`された`JSON`データを解析し、スプレッドシートの適切な位置に書き込む処理を記述します。

この`HTTPリクエストノード`以降の具体的な設定や`GAS`コードについては、本記事では割愛しますが、コード実行ノードでデータを扱いやすい形に整形しておくことで、後続の処理が非常にシンプルになります。

こちらの処理については、また別の記事をアップします。

## まとめ

今回は、Difyを使って複数のWebサイトの情報を収集・要約し、それを`GAS`で利用するために、`LLMノード`の出力する`JSON`文字列をコード実行ノード`で整形するプロセスを解説しました。

* `LLMノード`では、プロンプトを工夫して特定の構造（`JSON`配列）で出力させることがポイントです。
* `コード実行ノード`では、`LLM`の出力に付与される可能性のあるマークダウンを除去し、`JSON`文字列をパースしてJavaScriptの配列データに変換します。
* さらに、配列の各要素から必要な値（今回は`overview`）を安全に抽出する処理を加えることで、後続のノードが扱いやすい文字列の配列を作成しました。

このデータ整形ステップがあることで、`Dify`と外部サービス（`GAS`に限らず）との連携がスムーズになり、より複雑で便利なワークフローを構築できるようになります。

ぜひ、この記事を参考に、`Dify`の`LLMノード`と`コード実行ノード`を使ったデータ処理に挑戦してみてください！