0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【アドカレ2025 Day6】Difyのイテレーションで複数URLを一気に処理して、スプシにまとめて書き込んだ話

0
Last updated at Posted at 2025-12-06

こんにちは、学びの探求者です。

普段はnoteで活動しています。

2025年のQiitaアドベントカレンダーでは、
「ノーコード/ローコードで、自分のコンテンツ基盤を自動化していく」
をテーマに、25日間の仕組みづくりを記録していきます。
ぜひ、応援してください。

アドカレ 6 日目は、前日の Day5 をさらに進化させて、

複数の URL を一気にスクレイピング → LLM で項目抽出 → Google スプレッドシートに追記する自動化ワークフローを Dify で組んでみました。

想像の 10 倍むずかしかったですが(笑)、動いたときの嬉しさは格別でした!

今日のゴール:URL を渡すだけでスプシに行が増える仕組み

Day6 の目標はとても明確です:

  • URL を複数行ペースト
  • イテレーションで 1 行ずつ順番に処理
  • LLM が meta 情報やセクション情報を抽出
  • Code ノードで rows に整形
  • Google Sheets に 追加書き込み(追記)

ここまでを 完全自動化 します。


全体アーキテクチャ

開始
  ↓
URL整形(Code)
  ↓
イテレーション(URLごとのスクレイピングと抽出)
  ↓
Batch Get(スプレッドシート最終行を取得)
  ↓
Code_range(書き込む range を作る)
  ↓
Code_merge / Code3(values を JSON 形式に整形)
  ↓
Batch Update(スプレッドシートへ追記)

URLごとに LLM が解析し、それを Python で横方向にまとめ、最後にまとめてスプレッドシートへ流し込みます。


イテレーション:複数 URL を 1 件ずつ処理する

開始ノードで、複数行の URL を貼り付けます:

https://example.com
https://another.com
https://xxxx.jp

これを Code ノードで配列に変換:

def main(urls_text: str):
    urls = [u.strip() for u in urls_text.strip().split("\n") if u.strip()]
    return {"urls": urls}

この urls(配列)をイテレーションに渡すと、
1 回のループで 1 URL が流れる構造 になります。


イテレーション内で行う処理

イテレーション内では、URL ごとに以下を実行します:

  1. LLM(URL をそのまま渡す前処理)
  2. Scrape(Webページから必要情報を取得)
  3. LLM(Day5 と同じフォーマットで構造化抽出)
  4. Code_merge(URL と抽出結果をまとめて 1 行の dict にする)

LLM の抽出では、以下の 10 項目を JSON 形式で出しています:

  • url
  • meta_title
  • meta_description
  • og_title
  • og_description
  • og_image
  • h1
  • hero_lead
  • key_sections
  • proof_points

抽出プロンプトは省略しますが、Day5 と同様です。


Batch Get(シートの最終行を取る)

ここは Day2 と同じです。詳細は以下をご参照ください:
https://qiita.com/nao_manabitan/items/a3fb7e3b69806ed5d327

Batch Get により、「次の書き込み開始位置(=最終行)」を取得します。


Code_range(書き込み先の範囲を作る)

Batch Update は range を明示しないと追記できない ので、
最後の行番号 + 今回追加する件数 に応じて動的に range を生成します。

def main(last_row: int, item_count: int):
    start_row = max(last_row + 1, 2)  # ヘッダーが1行目と仮定
    end_row = start_row + item_count - 1
    sheet_range = f"シート1!A{start_row}:J{end_row}"
    return {"sheet_range": sheet_range}

これで、書き込み位置が毎回ズレても安全に追記できます。


Code3(values を JSON 化)

Batch Update が受け取れる形式に rows を整形します。

import json

def main(rows: list):
    data_json = json.dumps(rows, ensure_ascii=False)
    return {"data_json": data_json}

Batch Update(スプシへ追記)

Batch Update が要求する形式:

[
  {
    "range": "シート1!A2:J4",
    "values": [
      ["url", "meta_title", ...],
      ["https://example.com", "タイトル1", ...],
      ...
    ]
  }
]

この JSON を Code3 → Batch Update の順で渡します。


Day6 の “最大の山場”

正直、今日の難所はスプシ用のデータを整形するところでした

  • イテレーションの object を直接 Batch Update が受け取れない
  • rows の形式が少しでも違うと JSON が壊れる
  • range を固定すると上書きされる
  • LLM の array 出力もそのままでは使えない

最終的に整理したのは、

✔ rows(中身)は Code3 で組み立てる

✔ range(場所)は Code_range で作る

この二段構えを理解した瞬間、一気に仕組みが安定しました。


まとめ:Day6 は ETL(中間変換)の本質を学ぶ日となりました。

このワークフローが理解できれば、
Day7 以降で Notion / Slack / DB などへ自動連携する際にも応用できそうです。ワクワク。

特に:

  • データを整形して渡す(Transform)
  • どこに流し込むかを決める(Load)

という考え方は非常に重要だと思いました。


追記(2025-12-06)

この記事で紹介しているワークフローは「Day6 時点のスナップショット」です。

実際に動かしてみて、

  • それっぽいデータは取れているものの、ページ本文をどこまで正しく分析できているか
  • 複数 URL を渡したときに、同じ URL が重複して処理されているケースがあること
  • range の指定によっては、既存データを「追記」ではなく「上書き」してしまうこと

など、まだ改善したい点がいくつか見つかりました。

そのため、今後の Day7 以降で、

  • URL とテキストの紐づけ(イテレーション内の扱い)の見直し
  • LLM に頼りすぎず、Code ノード側で JSON を組み立てる設計の強化
  • スプレッドシート側の range 設計(完全な追記モード)の調整

を進めていく予定です。

公開時点では「Day6 の試行錯誤の記録」として残しつつ、
後日あらためて、より安定した構成を別記事でまとめます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?