Dify + Scrapeless: エージェントにカスタムツールでライブWebデータを提供する
Advanced Data Extraction Specialist
TL;DR:
- DifyマーケットプレイスのDeep SerpApiプラグインは、1つのパラメータ、
queryを持つ1つのツールを正確に公開しているため、結果の縦、ページオフセット、または他のサイトが必要なリクエストは他の場所から来る必要があります。 - カスタムツールは1つのOpenAPIファイルです。Difyはそれを1つの操作
scraperRequestに解析し、1つのエンドポイントを通じて全Scrapelessscraper.*アクターファミリーに到達します。 - 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をトップレベルにmetadata、pagination、search_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
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つのパラメーター:actorとinputを取ります。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
# 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
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
{
"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の応答で返され、metadata、pagination、search_information、related_searches、inline_videosブロックが含まれます。各結果はtitle、link、snippet、source、position、snippet_highlighted_wordsを持ちます。
ローカルパック。 tbm: lclを追加することでorganic_resultsがlocal_results.placesに置き換わります — リクエストごとに20のビジネス。start: 20を設定すると次のページが返されます; 一つのクエリの二つの連続したページで、40のレコード中37が異なり、両方のページを保存するフローは、重複を仮定するのではなく、安定した何かにキーを合わせるべきです。
ローカルパックのフィールドは、CRMやスプレッドシートに到達する前にクリーンアップが必要です:
phone、type、hoursは先頭にスペースが詰め込まれて到着し、一部の営業時間文字列は通常のものの代わりに狭い改行なしスペースを使用します。phoneは一つのキャプチャで20レコード中15レコードに電話の形の値を保持しており; 残りは営業時間やOnline estimatesのようなサービスラベルを含みます。place_id、place_id_search、lsig、およびthumbnailは全て20レコードで空でした。gps_coordinatesは存在するが{"latitude": 0, "longitude": 0}と読み取り、場所を持たないので真理値チェックを通過します。
アマゾン。 scraper.amazonとaction: productは2,226,755バイトを返しました。resultの下の解析された製品は63フィールドに渡って4,608バイトで; 残りの1,960,588バイトがリストの生のhtmlです。その全ペイロードをモデルに渡すのは高価で無意味です。
モデルに到達する前にレスポンスを絞る
ツールノードの直後にコードノードを配置します。これはPython 3またはJavaScriptを実行し、ツールの出力を入力変数として取り、後のノードがキーによって読み取る辞書を返します。そこにフィールドを選択することは何のコストもかからず、モデルのコンテキストを小さく保ちます:
python
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
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
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のように表示されます。完全なコンポーズスタックを起動し、api や web だけでなく、同じツールがクラウドと同様に機能します。
このガイドの動作は、セルフホストされた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つのカスタムツールで複数のアクターをカバーできますか?
はい、それがデザインのポイントです。エンドポイントはactorとinputを受け取るため、単一のscraperRequest操作がアカウントがアクセスできるすべてのアクターに到達します。スキーマ内のenumに1つ追加することで、2つのツールなしでリクエストビルダーに露出します。
Q: APIキーはどこに置くべきですか?
ツールプロバイダーの資格情報フィールドに、Difyがシークレットとして保存し、呼び出し時に挿入します。ノードパラメータではなくそこに保持することで、エクスポートされたワークフローや複製されたアプリがキーを持ち運ぶことはありません。
Scrapelessでは、適用される法律、規制、およびWebサイトのプライバシーポリシーを厳密に遵守しながら、公開されているデータのみにアクセスします。 このブログのコンテンツは、デモンストレーションのみを目的としており、違法または侵害の活動は含まれません。 このブログまたはサードパーティのリンクからの情報の使用に対するすべての責任を保証せず、放棄します。 スクレイピング活動に従事する前に、法律顧問に相談し、ターゲットウェブサイトの利用規約を確認するか、必要な許可を取得してください。



