ブログに戻ります

Dify + Scrapeless: エージェントにカスタムツールでライブWebデータを提供する

Emily Chen
Emily Chen

Advanced Data Extraction Specialist

20-Aug-2026

TL;DR:

  • DifyマーケットプレイスのDeep SerpApiプラグインは、1つのパラメータ、queryを持つ1つのツールを正確に公開しているため、結果の縦、ページオフセット、または他のサイトが必要なリクエストは他の場所から来る必要があります。
  • カスタムツールは1つのOpenAPIファイルです。Difyはそれを1つの操作scraperRequestに解析し、1つのエンドポイントを通じて全Scrapeless scraper.*アクターファミリーに到達します。
  • Difyは、APIが拒否する値で2つの認証フィールドを事前に埋め込みます:ヘッダー名はAuthorizationにデフォルト設定され、ヘッダープリフィックスはBasicにデフォルト設定されます。いずれのデフォルトも401{"code":14404,"message":"invalid access token"}で返します。
  • Difyは入れ子のinputオブジェクトを文字列パラメータとして型付けするため、JSONテキストを出力するCodeノードがワークフロー内でそれを構築する信頼できる方法です。
  • Amazonプロダクトコールは約2.2 MBを返し、そのうちの1.9 MBは生のhtmlです。ペイロードがモデルに到達する前にCodeノードでresultを選択します。
  • 無料のScrapelessアカウントは、このガイド内のすべてのリクエストをカバーします。

ウェブツールを持たないDifyエージェントは、モデルの重みとあなたがアップロードした知識ベースの情報から回答します。今日のトップランクのページ、競合他社の現行価格、特定の都市で営業している配管工を尋ねると、流暢で古びたものを生成します。

Difyはそれをツールで解決し、1つを追加する2つの方法があります。このガイドでは、第2の方法、OpenAPIファイルから構築されたカスタムツールについて説明します。これにより、あなたのワークスペース内のすべてのエージェントとワークフローの呼び出し可能なアクションにScrapeless Scraping APIを変換します。

カスタムツールが追加するもの - プラグインが持っていないもの

公式のDifyマーケットプレイスのDeep SerpApiリストは、1つの必須パラメータqueryとAPIキー用の1つの認証フィールドを持つ1つのツールを公開しています。単純なGoogleクエリがワークフローの必要なものすべてである場合、インストールして読み進めるのをやめてください — クリック2回で動作しますし、Difyに基づいて構築されたビジネスニュースモニターはそれを中心に組み立てられた完全なワークフローを示しています。

その背後にあるScrapeless HTTPエンドポイントは、クエリ文字列以上のものを大幅に受け入れます。同じリクエスト形状で、ウェブ結果の代わりにローカルパックを選択したり、結果の2ページ目にオフセットしたり、完全にAmazonリストに切り替えたりします。これらはすべて、単一のqueryフィールドを通じては到達できません。

カスタムツールはそのギャップを埋めます。OpenAPIドキュメントを貼り付けると、Difyはそれから操作を読み取り、全アクターファミリーが1つの追加可能なツールになります。インストールするものも展開するものもなく、同じファイルはDify Cloudと自己ホスト型インスタンスの両方で機能します。

Scraping APIが返すもの

1つのエンドポイントがすべてのリクエストを処理します:POST https://api.scrapeless.com/api/v1/scraper/request。ボディには2つのフィールドがあり、actorはスクレイパーの名前を付け、inputはそのスクレイパーのパラメーターを持ちます。

レスポンスはHTMLの代わりに解析されたJSONです。scraper.google.searchのコールは、organic_resultsをトップレベルにmetadatapaginationsearch_informationの隣に配置します。tbm: lclを同じアクターに追加すると、それがlocal_results.places、評価、電話番号、住所を持つビジネスのブロックに置き換わります。scraper.amazonのコールは、解析されたプロダクトをresultの下にネストします。

この単一形状の設計が、1つのOpenAPI操作で十分な理由です。すべてのアクターのパラメーターの詳細は、Scraping APIドキュメントにあります。

前提条件

  • Difyワークスペース — クラウド、または1.0.0以降の自己ホスト型。ここで説明されている動作は、自己ホスト型の1.16.1インスタンスで測定されました。
  • ダッシュボードからのScrapeless APIキー。
  • ツールを追加するためのワークスペース権限。Difyはカスタムツールエンドポイントをワークスペースの管理者とオーナーに制限しています。

ステップ1: OpenAPIスキーマのインポート

Difyでツール → カスタム → カスタムツールの作成を開き、以下のドキュメントを貼り付けてください。これはOpenAPI 3.0.3仕様に対して有効であり、これはDifyのパーサーが期待するバージョンです。

yaml Copy
openapi: 3.0.3
info:
  title: Scrapeless Scraper API
  version: "1.0.0"
servers:
  - url: https://api.scrapeless.com
paths:
  /api/v1/scraper/request:
    post:
      operationId: scraperRequest
      summary: Run a scraper actor and return structured data
      security:
        - ApiTokenAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [actor, input]
              properties:
                actor:
                  type: string
                  description: Which scraper to run.
                  enum: [scraper.google.search, scraper.amazon]
                  example: scraper.google.search
                input:
                  type: object
                  description: Actor parameters. Keys depend on the actor.
                  additionalProperties: true
            examples:
              googleSearch:
                summary: Google SERP
                value:
                  actor: scraper.google.search
                  input:
                    q: web scraping api
              googleLocalPack:
                summary: Google local pack
                value:
                  actor: scraper.google.search
                  input:
                    q: plumbers in Austin, TX
                    tbm: lcl
              amazonProduct:
                summary: Amazon product by URL
                value:
                  actor: scraper.amazon
                  input:
                    action: product
                    url: https://www.amazon.com/dp/B09B8V1LZ3
      responses:
        '200':
          description: Parsed result. Shape depends on the actor.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
components:
  securitySchemes:
    ApiTokenAuth:
      type: apiKey
      in: header
      name: x-api-token

Difyはそれを正確に1つのツールに解析します。名前はoperationIdから取得されるため、ツールはscraperRequestと呼ばれ、2つのパラメーター:actorinputを取ります。3つの名前の付いた例がリクエストビルダーに表示され、AmazonのURLを手作業で入力する必要がなくなります。

ステップ2: すべての4つの認証フィールドを埋める

APIキー認証を選択し、すべてのフィールドを設定します。4つのうちの2つはこのAPIが拒否する値で事前に埋め込まれています:

フィールド 設定内容 Difyが事前に埋め込む内容
認証タイプ API Key (api_key_headerとして保存) None
ヘッダー名 x-api-token Authorization
あなたのScrapeless APIキー
ヘッダープリフィックス Custom Basic

プリフィックスフィールドは、よく間違いを招くものです。Difyはそれを値に連結するため、Basicで残すとヘッダーx-api-token: Basic <your-key>が送信されます。これは、Basic HTTP認証スキームが意味するものではなく、実際のBasic資格情報はbase64エンコードされたuser:passwordペアです。そして、Scrapelessはベアキーを期待するため、リクエストは拒否されます。Bearerも同様に失敗します。Customだけが、値を手を加えずに通過させます。

ヘッダー名がAuthorizationのままだと、同じ理由で失敗します: キーはAPIが読み取るヘッダーに届かないからです。

両方の間違いは同じ応答を生成し、Difyに触れる前にターミナルからどちらかを再現できます:

bash Copy
# Correct: bare key in x-api-token
curl -s -o /dev/null -w 'bare key      -> %{http_code}\n' \
  -X POST https://api.scrapeless.com/api/v1/scraper/request \
  -H "Content-Type: application/json" \
  -H "x-api-token: $SCRAPELESS_API_KEY" \
  -d '{"actor":"scraper.google.search","input":{"q":"web scraping api"}}'

# What Dify sends with the default prefix
curl -s -w '\nBasic prefix  -> %{http_code}\n' \
  -X POST https://api.scrapeless.com/api/v1/scraper/request \
  -H "Content-Type: application/json" \
  -H "x-api-token: Basic $SCRAPELESS_API_KEY" \
  -d '{"actor":"scraper.google.search","input":{"q":"web scraping api"}}'

# What Dify sends with the default header name
curl -s -w '\nAuthorization -> %{http_code}\n' \
  -X POST https://api.scrapeless.com/api/v1/scraper/request \
  -H "Content-Type: application/json" \
  -H "Authorization: $SCRAPELESS_API_KEY" \
  -d '{"actor":"scraper.google.search","input":{"q":"web scraping api"}}'
text Copy
bare key      -> 200
{"code":14404,"message":"invalid access token"}
Basic prefix  -> 401
{"code":14404,"message":"invalid access token"}
Authorization -> 401

ここでの401は、サーバーが受け取った資格情報が受け入れられないものであることを示すもので、これはまさにHTTPセマンティクス仕様書がそのステータスのために予約されているものです。ボディはそれをさらに狭めます: コード14404は具体的には使用できないトークンであり、形式が間違っているリクエストではありません。

ステップ3: 組み込みテストを実行

Difyのテストパネルは、あなたが入力した資格情報でエンドポイントを呼び出します。二つのパラメータを入力してください:

json Copy
{
  "actor": "scraper.google.search",
  "input": "{\"q\": \"web scraping api\"}"
}

機能する構成は約15 KBのSERP JSONを返します。壊れたプリフィックスは、上流のボディをそのまま持つ単一のエラーストリングを返します: Request failed with status code 401 and {"code":14404,"message":"invalid access token"}

テストペイロード内の引用符に注意してください。Difyはネストされたリクエストボディプロパティをフラットにするため、inputはオブジェクトではなく文字列パラメータとして登録されます — 解析されたスキーマはactorおよびinputを共にstringとして報告し、両方が必要です。実際のJSONオブジェクトもパネルで機能し、Difyはどちらの形式もAPIが期待するオブジェクトに正規化します。その変換は重要です: エンドポイントに対して手動で構築されたリクエストはオブジェクトを送信する必要があり、そこに文字列を送ると400 {"message":"invalid input body"}として戻ってきます。

テストがデータを返したら、プロバイダーを保存します。scraperRequestは、ワークスペース内のすべてのアプリのツールリストに表示されます。

無料プランで構築中ですか? Scrapelessアカウントを作成すると、このガイド内のリクエストが無料のクォータで実行されます。

返ってくるもの

封筒はアクターに依存し、各形状は下流で異なる処理を必要とします。

ウェブ検索。 scraper.google.search{"q": "web scraping api"}で8つのorganic_resultsが15 KBの応答で返され、metadatapaginationsearch_informationrelated_searchesinline_videosブロックが含まれます。各結果はtitlelinksnippetsourcepositionsnippet_highlighted_wordsを持ちます。

ローカルパック。 tbm: lclを追加することでorganic_resultslocal_results.placesに置き換わります — リクエストごとに20のビジネス。start: 20を設定すると次のページが返されます; 一つのクエリの二つの連続したページで、40のレコード中37が異なり、両方のページを保存するフローは、重複を仮定するのではなく、安定した何かにキーを合わせるべきです。

ローカルパックのフィールドは、CRMやスプレッドシートに到達する前にクリーンアップが必要です:

  • phonetypehoursは先頭にスペースが詰め込まれて到着し、一部の営業時間文字列は通常のものの代わりに狭い改行なしスペースを使用します。
  • phoneは一つのキャプチャで20レコード中15レコードに電話の形の値を保持しており; 残りは営業時間やOnline estimatesのようなサービスラベルを含みます。
  • place_idplace_id_searchlsig、およびthumbnailは全て20レコードで空でした。
  • gps_coordinatesは存在するが{"latitude": 0, "longitude": 0}と読み取り、場所を持たないので真理値チェックを通過します。

アマゾン。 scraper.amazonaction: productは2,226,755バイトを返しました。resultの下の解析された製品は63フィールドに渡って4,608バイトで; 残りの1,960,588バイトがリストの生のhtmlです。その全ペイロードをモデルに渡すのは高価で無意味です。

モデルに到達する前にレスポンスを絞る

ツールノードの直後にコードノードを配置します。これはPython 3またはJavaScriptを実行し、ツールの出力を入力変数として取り、後のノードがキーによって読み取る辞書を返します。そこにフィールドを選択することは何のコストもかからず、モデルのコンテキストを小さく保ちます:

python Copy
def main(response: dict) -> dict:
    places = (response.get("local_results") or {}).get("places") or []
    rows = []
    for place in places:
        contact = (place.get("phone") or "").strip()
        digits = sum(character.isdigit() for character in contact)
        rows.append({
            "name": (place.get("title") or "").strip(),
            "category": (place.get("type") or "").strip(),
            "rating": place.get("rating"),
            "reviews": place.get("reviews") or 0,
            "phone": contact if digits >= 10 else None,
            "note": None if digits >= 10 else contact,
            "address": (place.get("address") or "").strip(),
        })
    return {"rows": rows, "count": len(rows)}


# Local check against a live response. Leave everything below out of the Code node.
if __name__ == "__main__":
    import json, os, urllib.request

    body = json.dumps({
        "actor": "scraper.google.search",
        "input": {"q": "plumbers in Austin, TX", "tbm": "lcl"},
    }).encode()
    call = urllib.request.Request(
        "https://api.scrapeless.com/api/v1/scraper/request",
        data=body,
        headers={"Content-Type": "application/json",
                 "x-api-token": os.environ["SCRAPELESS_API_KEY"]},
    )
    with urllib.request.urlopen(call, timeout=180) as reply:
        cleaned = main(json.load(reply))

    print(cleaned["count"], "rows")
    print(json.dumps(cleaned["rows"][0], ensure_ascii=False))

上記のブロックはローカルチェックとしても機能します:環境内でキーを使って実行すると、1つのライブローカルパックを取得し、同じ関数を適用して最初のクリーンな行を印刷します。直接呼び出すと、input がオブジェクトとして送信され、APIは 400 {"message":"invalid input body"} の文字列で応答します。Dify は出力時にその文字列形式を変換するため、同じ値が両方の場所で機能します。

クリーニングは20件の生データを20件の使用可能なデータに変換します:余分なホワイトスペースのない名前とカテゴリ、フィールドが持つ場合のphoneの実際の数値、電話列に書き込まれるのではなくnoteに移動された営業時間のテキストです。

Amazon 形状の場合、同じノードはワンライナー — return {"product": response["result"]} — であり、ペイロードの99%を削減します。

エージェントまたはワークフローに接続する

両方の表面は同じ保存されたツールを使用しており、選択は誰がパラメータを選ぶかに関するものです。

エージェントでは、モデルが scraperRequest を呼び出すタイミングと、actor および input に何を入れるかを決定します。これは、指示がツールおよびデータ条件を明示的に指定する場合に機能します:

text Copy
When a question depends on current web content, call scraperRequest with
actor "scraper.google.search" and input {"q": "<the search terms>"}, read the
organic_results, and answer from those. Do not answer from memory when the
question is about current prices, rankings, or availability.

ワークフローでは、ツールノードに actor をピン留めし、上流ノードがクエリのみを提供できるようにします。input が文字列パラメータであるため、信頼できるパターンはJSONテキストを構築するコードノードです:

python Copy
def main(query: str) -> dict:
    import json
    return {"payload": json.dumps({"q": query, "tbm": "lcl"})}

payload をツールノードの input フィールドにワイヤリングします。Dify の ツールドキュメント は、周囲のノードの配線についてより詳細に説明しています。

Dify をセルフホストする場合

セルフホストされたインスタンスは、APIコンテナがインターネットに直接アクセスするのではなく、専用の ssrf_proxy コンテナを通じてツールHTTPをルーティングします。そのサービスが稼働していない場合、ツール呼び出しはDNSエラーで失敗します — [Errno -3] Temporary failure in name resolution — これは、コンテナが欠落しているのではなく、壊れたURLのように表示されます。完全なコンポーズスタックを起動し、apiweb だけでなく、同じツールがクラウドと同様に機能します。

このガイドの動作は、セルフホストされた1.16.1インスタンスで測定されました:スキーマは1つのツールにパースされ、認証テストはCustom をプレフィックスとして含む15,648バイトのSERP JSONを返し、保存されたプロバイダーは scraperRequest を接続可能なツールとしてリストしました。

結論

マーケットプレイスのプラグインは、1つのクエリ文字列をカバーします。カスタムツールは、その背後のエンドポイントをカバーしており、リードフローがローカルパックを読み取り、それをページングし、どこかにデータが到着する前にフィールドをクリーンする必要があるときに役立ちます。

セットアップコストは1つのOpenAPIファイルと4つの認証フィールドであり、そのうちの2つはDifyがデフォルトで不正確に埋め込みます。それらを正しく設定すれば、ファミリーのすべてのアクターがワークスペース内のすべてのアプリに利用可能になり、ペイロードを小さく保ち、列を清潔に保つコードノードがそれを整形します。

接続する準備はできていますか? 無料のScrapelessアカウントを作成する と、APIキーを取得し、上記のスキーマをワークスペースに貼り付けてください。使用とプランの制限については、Scrapeless料金ページにリストされています。

FAQ

Q: Deep SerpApiプラグインを使用すべきですか、それともカスタムツールを使用すべきですか?

プレーンなGoogleクエリだけで十分な場合はプラグインを使用してください — それは単一の query パラメータを持つ1つのツールを公開し、インストールには2回のクリックが必要です。ローカルパック、ページオフセット、Amazonリスト、または他のアクターを必要とする場合は、カスタムツールを使用してください。これらのパラメータはその単一のフィールドを通じては到達できません。

Q: なぜ私のDifyカスタムツールがcurlで同じキーが機能する時に401を返すのですか?

2つのDifyのデフォルト設定が、APIが読まない形式でキーを送信します。ヘッダー名のデフォルトは Authorization ではなく x-api-token になり、ヘッダーのプレフィックスのデフォルトは Basic になり、これによりDifyは x-api-token: Basic <key> を送信します。ヘッダー名を x-api-token に、プレフィックスを Custom に設定してください。

Q: なぜ input フィールドがオブジェクトではなく文字列なのですか?

DifyはOpenAPIドキュメントを解析するときに、ネストされたリクエストボディのプロパティをフラット化するため、ネストされたオブジェクトは文字列パラメータに変わります。Difyはどちらの形式も受け入れ、リクエストが出発する前にそれを正規化するため、json.dumps(...) を出力するコードノードがワークフローで構築するための信頼できる方法です。エンドポイントへの直接呼び出しはより厳格で、オブジェクトを要求します。

Q: これはDify Cloudおよびセルフホストでも機能しますか?

はい。カスタムツールはOpenAPIドキュメントと認証情報で構成されており、どちらにも何もインストールする必要はありません。セルフホストされたインスタンスにはもう1つの追加要件があります: ssrf_proxy コンテナが稼働している必要があります。これは、ツールHTTPの出力がそこを通じてルーティングされているからです。
Q: 1回のリクエストでどのくらいの結果が返されますか?

ウェブ検索で、このガイドに使用されるキャプチャで8つのオーガニック結果が返され、結果の数はクエリによって異なります。ローカルパックは1リクエストにつき20の場所を返し、start: 20が次のページを取得します。1つのクエリの連続ページは少し重複するため、書き込み時に重複を排除してください。

Q: Amazonのレスポンスがモデルのコンテキストを圧迫しないようにするにはどうすればよいですか?

ツールノードの後に配置されたコードノードでresultを選択します。製品呼び出しは2,226,755バイトを返し、そのうち1,960,588が生のhtmlフィールドで、解析された製品はわずか4,608でしたので、{"product": response["result"]}を返すことで、有用なものをすべて維持し、残りを削除します。

Q: 1つのカスタムツールで複数のアクターをカバーできますか?

はい、それがデザインのポイントです。エンドポイントはactorinputを受け取るため、単一のscraperRequest操作がアカウントがアクセスできるすべてのアクターに到達します。スキーマ内のenumに1つ追加することで、2つのツールなしでリクエストビルダーに露出します。

Q: APIキーはどこに置くべきですか?

ツールプロバイダーの資格情報フィールドに、Difyがシークレットとして保存し、呼び出し時に挿入します。ノードパラメータではなくそこに保持することで、エクスポートされたワークフローや複製されたアプリがキーを持ち運ぶことはありません。

Scrapelessでは、適用される法律、規制、およびWebサイトのプライバシーポリシーを厳密に遵守しながら、公開されているデータのみにアクセスします。 このブログのコンテンツは、デモンストレーションのみを目的としており、違法または侵害の活動は含まれません。 このブログまたはサードパーティのリンクからの情報の使用に対するすべての責任を保証せず、放棄します。 スクレイピング活動に従事する前に、法律顧問に相談し、ターゲットウェブサイトの利用規約を確認するか、必要な許可を取得してください。

最も人気のある記事

カタログ