Zenn
🗿

[Dify]Google Search API検索、スクレイピング、記事要約、スプレッドシート格納までのワークフロー作成メモ

2025/01/27に公開

不格好だけどとりあえず動くところまでいったのでメモ

大まかな流れ

  1. ユーザーのキーワード入力
  2. Google Search API叩いて検索結果取得
  3. パース処理して配列化
  4. イテレーションループでスクレイピングと要約
  5. GAS用のJSONに加工
  6. GASのdoPost叩いてスプレッドシートに格納

全体設計

API検索から配列化までの手順

過去記事参照
https://zenn.dev/kayato/articles/96fde23704706d
https://zenn.dev/kayato/articles/5043cab7758ee1
https://zenn.dev/kayato/articles/009b23b5b49426

上記からコード変更した部分

イテレーション内の配列化コード

import json

def create_array(arg1, arg2, arg3, arg4):
    return [{"title": arg1, "link": arg2, "snippet": arg3, "text": arg4}]

def main(arg1: str, arg2: str, arg3: str, arg4: str) -> dict:
    try:
        # 引数を使って配列化
        result = create_array(arg1, arg2, arg3, arg4)
        return {"result": result}
    except Exception as e:
        # エラーハンドリング
        return {"result": {"error": f"Unexpected error: {str(e)}"}}

if __name__ == "__main__":
    # Example inputs
    arg1 = "Ethereum"
    arg2 = "https://ethereum.org/en/"
    arg3 = "Ethereum is a decentralized platform for applications."
    arg4 = ("Ethereumは革新的なアプリとブロックチェーンネットワークのための主要プラットフォームです。"
            "Web3最大の開発者エコシステムを擁しています。")

    # Call the main function and print the result
    result = main(arg1, arg2, arg3, arg4)
    print(json.dumps(result, indent=2, ensure_ascii=False))

GAS用のJSON加工

import json

def main(arg1) -> dict:
    try:
        # 入力がすでに辞書型かどうか確認
        if isinstance(arg1, str):
            # 文字列の場合、JSONとしてデコード
            input_json = json.loads(arg1)
        elif isinstance(arg1, dict) or isinstance(arg1, list):
            # 辞書型またはリスト型の場合、そのまま使用
            input_json = arg1
        else:
            # 想定外の型の場合はエラーを返す
            return {"result": {"error": "Input must be a JSON string, dictionary, or list"}}

        # 初期化:resultは辞書型で、itemsがリスト
        result = {"result": {"items": []}}

        # 入力が辞書型で "result" が存在する場合
        if isinstance(input_json, dict):
            items = input_json.get("result", [])
        elif isinstance(input_json, list):
            items = input_json  # リスト型の場合、そのまま扱う

        # 入力アイテムを処理
        for item in items:
            # 必要な要素を抽出
            transformed_item = {
                "title": item.get("title", ""),
                "link": item.get("link", ""),
                "snippet": item.get("snippet", ""),
                "text": item.get("text", "")
            }
            # 抽出したデータをitemsリストに追加
            result["result"]["items"].append(transformed_item)

        return result
    except json.JSONDecodeError as e:
        # JSONデコードエラー時の処理
        return {"result": {"error": f"Invalid JSON input: {str(e)}"}}
    except Exception as e:
        # その他の例外処理
        return {"result": {"error": f"Unexpected error: {str(e)}"}}

if __name__ == "__main__":
    # テスト用の入力データ(辞書型でも文字列型でも可)
    arg1 = [
        {
            "title": "Solana: Web3 Infrastructure for Everyone",
            "link": "https://solana.com/",
            "snippet": "Bring blockchain to the people. Solana supports experiences for power users, new consumers, and everyone in between.",
            "text": "Solanaは、世界中の企業によるツールや統合を支援する、数百万人のコミュニティを持つWeb3インフラである。その特徴として、取引手数料が低く(平均0.0025ドル未満)、ブロック生成時間が400ミリ秒と高速である点が挙げられる。毎秒数千件のトランザクションを処理できるスケーラビリティと、数千もの独立ノードによる分散化されたネットワークにより、データの安全と検閲耐性を確保している。さらに、プルーフオブステークとその他のイノベーションにより、環境への影響を最小限に抑え、1トランザクションあたりのエネルギー消費量は数回のGoogle検索程度に留まる。クリエイター、アーティスト、開発者にとって魅力的な、活気のあるコミュニティも存在する。\n"
        },
        {
            "title": "Solana price today, SOL to USD live price, marketcap and chart ...",
            "link": "https://coinmarketcap.com/currencies/solana/",
            "snippet": "The live Solana price today is $254.80 USD with a 24-hour trading volume of $4270478175.68 USD. We update our SOL to USD price in real-time.",
            "text": "Solanaは、ブロックチェーン技術のパーミッションレスな性質を利用して分散型金融(DeFi)ソリューションを提供する、機能性の高いオープンソースプロジェクトです。2017年に開始され、2020年3月にスイス・ジュネーブに拠点を置くSolana Foundationによって正式にローンチされました。\n\nSolanaプロトコルは、分散型アプリ(DApp)の作成を容易にするよう設計されており、プルーフ・オブ・ヒストリー(PoH)コンセンサスと、ブロックチェーンの基盤となるプルーフ・オブ・ステーク(PoS)コンセンサスを組み合わせることで、スケーラビリティの向上を目指しています。この革新的なハイブリッドコンセンサスモデルにより、小規模トレーダーと機関投資家双方から関心を集めています。Solana Foundationは、分散型金融をより大規模に利用可能にすることに重点を置いています。記事では、創設者、Solanaの独自性、SOLの流通量、ネットワークのセキュリティ、購入方法についても言及しています。\n"
        }
    ]

    # main関数を呼び出して結果を表示
    result = main(arg1)
    print(json.dumps(result, indent=2, ensure_ascii=False))

GASで実装しているコード

function doPost(e) {
  // スプレッドシートとシート1を取得
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = spreadsheet.getSheetByName('シート1');

  // シートの最終行を取得
  var lastRow = sheet.getLastRow();

  // POSTデータをパース
  var jsonData = JSON.parse(e.postData.contents);

  // ヘッダーがなければ追加
  if (lastRow === 0) {
    sheet.appendRow(['Title', 'Link', 'Snippet', 'Text']);
    lastRow = 1;
  }

  // JSONデータをスプレッドシートに書き込む
  if (jsonData.items) {
    jsonData.items.forEach(function(item) {
      sheet.appendRow([item.title, item.link, item.snippet, item.text]);
    });
  }

  // レスポンスを返す
  return ContentService.createTextOutput(JSON.stringify({
    status: 'success',
    message: 'Data written to the spreadsheet successfully.'
  })).setMimeType(ContentService.MimeType.JSON);
}

エラー対処法

スクレイピング

Failed to invoke tool reached maximum retries 3 for URL link
  • URL要素がちゃんと受け渡しできてなくても上記エラー
  • URL先がアクセスできないケースもあった

その他メモ

  • Google App ScriptのdoPostは単一ファイルでは一つのみの設定

※doGetおよびdoPostは、同居は可能ですが1プロジェクトで1個しか用意できないので、複数用意したい場合には仕組みで分けるか?複数プロジェクトを作って運用する必要があります。

https://officeforest.org/wp/2018/11/28/google-apps-scriptでget・post/
https://officeforest.org/wp/2019/03/25/google-apps-scriptを色々なアプリから実行する/

Discussion

ログインするとコメントできます