こんにちは、学びの探求者です。
普段は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 ごとに以下を実行します:
- LLM(URL をそのまま渡す前処理)
- Scrape(Webページから必要情報を取得)
- LLM(Day5 と同じフォーマットで構造化抽出)
- 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 の試行錯誤の記録」として残しつつ、
後日あらためて、より安定した構成を別記事でまとめます。