Activepieces + Scrapeless: コード不要のローカルリードフロー
Web Data Collection Specialist
TL;DR:
- Activepiecesは、組み込みのHTTPピースを使用してライブ検索データにアクセスできるため、リードフローにはカスタムピース、公開パッケージ、ホスティングサービスが不要です。
- Google Searchアクターは、リクエストごとに約20のローカルビジネスを返します — 20、21、22の場所にキャプチャされました — したがって、ローカルフローは通常、1回の呼び出しと重複排除キーが必要であり、ページネーションループは不要です。
- 解析されたレスポンスは
bodyの下にあり、{{step_1.body.local_results.places}}が作業参照になります;bodyを削除するとフロー内でループはゼロ回実行されますが、成功としてレポートされます。 phoneフィールドは、約半数のレコードに電話番号を保持しています — 残りは営業時間またはサービスラベルを持っています — したがって、コードピースは単にそれを切り取るのではなく、検証する必要があります。- APIキーは、ヘッダーフィールドに入力するのではなく、プロジェクトレベルの値として保存してください。フローはエクスポートされ、共有されるからです。
What This Flow Gives You
カテゴリと都市をローカルビジネスの行に変換する作業中のActivepiecesフロー — 名前、カテゴリ、評価、レビュー数、電話 — CRM、シート、またはデータベース用に準備されています。
Activepiecesはアプリをうまくオーケストレーションし、ページを取得しません。検索結果は、scraper.google.searchアクターを通じてDeep SerpApiから取得され、解析されたローカルパックがJSONとして返されます。以下のフローは、piece-http 0.11.18を使用した自己ホスティングのActivepieces 0.82.0インスタンス上で構築されました。
Prerequisites
- Activepiecesインスタンス、クラウドまたは自己ホスティング
- Scrapeless APIキー — 無料アカウントを作成する
- 行の宛先ピース: Google Sheets、Airtable、Postgres、またはあなたのCRM
キーをプロジェクトレベルの値または接続に入れ、ステップのヘッダーフィールドには入力しないでください。フローディフィニションはそのリテラルフィールド値を持ち、フローはエクスポート、複製、プロジェクト間で共有されます。
Configure the HTTP Step
HTTP → Send HTTP requestを追加し、5つのフィールドを入力します:
| Field | Value |
|---|---|
| Method | POST |
| URL | https://api.scrapeless.com/api/v1/scraper/request |
| Headers | x-api-token → あなたのキー |
| Body type | JSON |
| Body | 以下のオブジェクト |
json
{
"actor": "scraper.google.search",
"input": {
"q": "plumbers in Austin, TX",
"tbm": "lcl"
}
}
tbmをlclに設定すると、ビジネスがウェブページの代わりに返されます。クエリはローカルな意図を必要とします: "plumbers in Austin, TX"はローカルパックを返し、シンプルな"plumbing"はしばしば返しません。
フローの残りを配線する前に、ビルダーの外でリクエストを確認してください。シェルから同じ呼び出しを行うと、空の結果がクエリのせいかフローのせいかを教えてくれます:
bash
curl -sS -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":"plumbers in Austin, TX","tbm":"lcl"}}' \
| python3 -c 'import json, sys
data = json.load(sys.stdin)
places = (data.get("local_results") or {}).get("places") or []
print(len(places), "places")
if places:
print("raw phone:", repr(places[0]["phone"]), "| raw type:", repr(places[0]["type"]))
else:
print("no local pack in this response; top-level keys were", sorted(data))'
それは、場所のカウントと最初のレコードの生のphoneおよびtypeを表示します — 受け取られた2つのフィールドはパディングされています。elseブランチのキーを読むのは、local_resultsに直接インデックスするのではなく、フローが必要とする同じ習慣です: アドレスに入る前に、期待する形状が存在するかを確認してください。
What Comes Back
成功した実行は、local_results.placesの下に約20の場所を返します。各々はtitle、type、rating、reviews、phone、およびaddressを持っています。キャプチャは、同じクエリの20、21、22の場所に到達したため、ページサイズは固定ではなくおおよそと考えてください: 次のページのために"start": 20をinputオブジェクトに追加し、名前と電話で重複排除を行い、正確なページ境界を仮定しないでください。
tbmがlclに設定されている場合、封筒はlocal_results、metadata、pagination、およびsearch_informationです — organic_resultsはなく、related_searchesもありません。拒否された呼び出しは、結果がなくてもcodeおよびmessageを含む封筒を返します。これは、フローがHTTPステータスだけでなくlocal_resultsの存在に基づいて分岐すべき理由です。
参照パスは正確に取得する価値があります。Activepiecesでは、解析されたJSONはbodyの下に存在します:
| Reference | Result |
|---|---|
{{step_1.body.local_results.places}} |
場所の配列 |
{{step_1.body.organic_results}} |
tbmが省略された場合のウェブ結果 |
{{step_1.organic_results}} |
何もなし — エラーなし、警告なし |
最後の行は高価なものです。bodyセグメントが欠落した参照は何も解決せず、Loop on Itemsステップはゼロ回実行され、実行は成功として終了します。クエリが何も返さなかったか、参照が間違っていたかに関わらず、空の宛先テーブルは同じように見えるため、まずパスを確認してください。
{{step_1.body.local_results.places}}上でLoop on Itemsを追加して、各ビジネスが単一のセルに書き込まれた1つの塊としてではなく、各独自のアイテムとして処理されるようにします。
このフリープランで構築することで、完全なローカルパックレスポンスに達するのに十分です — Scrapelessアカウントを作成する と、プロジェクト値にキーを保持してください。
保存前の正規化
使用可能な行かどうかを決定するのは二つの振る舞いであり、二つ目はリードフローにコードが必要な理由です。
文字列はパディングされて到着します。 phone、type、および hours は先頭にスペースを持ちます — " Plumber"、" (512) 690-4935"。これは見た目の問題ではありません: デデュプリケーションキーとして使用されるパディングされた電話番号は、次の実行で同じビジネスのための二つ目のレコードを作成し、"Plumber" に対するカテゴリフィルターは何も一致しません。ITU-T番号計画勧告 は、電話番号を同定子になる前に標準形に正規化する理由です。
いくつかのフィールドは存在しますが、使用可能なものはありません。 同じキャプチャ内で、place_id、thumbnail、および lsig は20のレコードすべてで空でした。gps_coordinates は罠です: {"latitude": 0, "longitude": 0} として存在しているため、真偽チェックが通り、マッピングステップはすべてのビジネスを赤道上の同じ位置にプロットします。address から位置を取得し、両方の値がゼロでない限り座標ペアを存在しないものとして扱ってください。
phone フィールドは常に電話番号ではありません。 "plumbers in Austin, TX" に対する20のキャプチャの中で、11のレコードのみが電話のような値を持っていました。他の9つは、" Closes 6 PM " のような営業時間テキストや、"Online estimates" のようなサービスラベルを保持していました。そのフィールドをCRM列に直接マッピングすると、ほぼ半分の行が使用不可能になり、フロー内にエラーはどこにもありません。そのいくつかの営業時間文字列は、普通のスペースではなく狭い改行のないスペース(U+202F)も含んでいるため、" " での単純な分割はトリミング後も予期しない動作をします。
フィールドの名前を信頼するのではなく、フィールドを検証し、破棄するテキストを削除せずに保持してください:
リクエストと宛先の間に コード ピースを追加します。Activepiecesはボディを export const code = async (inputs) => { … } としてラップします; 内部のロジックはプレーンJavaScriptです:
javascript
// `phone` sometimes carries opening hours or a service label instead of a number,
// so the value is validated before it becomes a contact field.
const PHONE = /\(?\d{3}\)?[ -]?\d{3}-?\d{4}/;
const code = async (inputs) => {
const clean = (value) => (typeof value === 'string' ? value.trim() : value);
const places = inputs.response?.local_results?.places ?? [];
return places.map((place) => {
const contact = clean(place.phone) ?? '';
const isPhone = PHONE.test(contact);
return {
name: clean(place.title),
category: clean(place.type),
rating: place.rating ?? null,
reviews: place.reviews ?? 0,
phone: isPhone ? contact : null,
phone_field_note: isPhone ? null : contact,
address_snippet: clean(place.address),
};
});
};
const sample = {
response: {
local_results: {
places: [
{
title: 'Radiant Plumbing, Air Conditioning, & Electrical',
type: ' Plumber',
rating: 4.8,
reviews: 18000,
phone: ' (512) 690-4935',
address: '25+ years in business \u00b7 Austin, TX',
},
{
title: 'Beyond Wow Plumbing & Drains',
type: ' Plumber',
rating: 4.9,
phone: ' Closes 6\u202fPM ',
address: 'Austin, TX',
},
],
},
},
};
code(sample).then((rows) => console.log(JSON.stringify(rows, null, 2)));
{{step_1.body}} をピースの response 入力に渡します。ここで重要な二つの詳細があります。?? 0 デフォルトは、レビューのないビジネスにはreviewsキーが全くないため存在し、数値ディスティネーション列は undefined を拒否し 0 を受け入れます。phone_field_note は、数字でないときにフィールドを占めていたものを保持するので、オペレーターは行が電話番号ではなく営業時間があることを確認できます。
評価とレビュー数は、数値を保持する価値のある二つのフィールドです。それ以外はすべてテキストであり、フローがデータベースではなくスプレッドシートエクスポートで終了する場合、カンマ区切り値フォーマット仕様 が、カンマを含むビジネス名がラウンドトリップから生き残る方法を決定します。
ビジネス連絡データを責任を持って扱う
このフローはビジネス連絡の詳細を集めますので、いくつかの義務が伴います。公共の検索結果からのみ収集し、ワークフローが必要とするフィールドのみを収集してください。連絡データを保存するための合法的な基盤を保持し、オプトアウトリクエストを尊重してください。ビジネスの電話番号は個人を特定することができるためです — 一般データ保護規則 は商業的文脈においても個人データに適用され、他の法域にも同等の規則が存在します。各宛先プラットフォームのインポートされた連絡先に関する条件を尊重し、行を無限に保持するのではなく、保持期間を設定し、連絡を取っている国のマーケティング同意規則に従ってください。これらは法的アドバイスではありません; アウトリーチを実行する前に自分自身の義務を確認してください。
結論
全体のフローを構成するのは三つのピースです: アクターを呼び出すためのHTTP、フィールドをトリミングおよびデフォルト化するためのコード、ビジネスごとに1行を書き込むためのItemsのループ。監視すべき失敗モードは参照パスにあります: body を削除し、成功した実行は空のテーブルを書き込みます。
ここからは、クエリを都市のリストに置き換えることで、同じフローがテリトリー構築になります。Make統合ウォークスルー は、別のノーコードビルダーからの同じリクエストをカバーし、Difyモニタリング構築 は同じアクターのエージェント駆動バージョンを示しています。
準備はできましたか?Deep SerpApi ドキュメントを確認して、完全なパラメータセットを理解し、プランと含まれるボリュームを比較、無料プランを開始してください。
FAQ
Q: Scrapelessを使用するためにカスタムActivepiecesピースが必要ですか?
いいえ。組み込みのHTTPピースがすべてのアクターをカバーしています。APIは単一の POST と actor フィールドと input オブジェクトを取得します。カスタムピースは、リクエストを生のままで見るべきではないチーム向けに型付けされたフィールドを持つブランド化されたステップが必要な場合にのみ役立ちます。これは能力の選択ではなく、パッケージングの決定です。
Q: HTTPステップが成功したとき、なぜ私のアイテムのループステップは0回反復しますか?
解析されたレスポンスは body の下にネストされているため、{{step_1.local_results.places}} は何も返さず、{{step_1.body.local_results.places}} は配列に解決します。参照が欠けていてもエラーは発生しないため、フローは空のループで成功を報告します。クエリを調査する前に参照パスを確認してください。
Q: 1つのリクエストが返す結果の数はどれくらいで、どうやってもっと取得しますか?
ローカルパックリクエストは約20の場所を返します。1つのクエリの繰り返しキャプチャは20、21、22を返しました。次のページには "start": 20 を、次々のページには input オブジェクトに "start": 40 を追加します。ページサイズは正確に固定されていないため、オフセットを信頼するのではなく、名前と電話番号で重複排除を行い、短い最終ページをセットの終わりと見なしてください。
Q: なぜ place_id と gps_coordinates はローカル結果では使えないのですか?
place_id、thumbnail、および lsig はすべてのローカルパックレコードで空になりますので、place_id が必要なフローは20の結果をすべて破棄します。gps_coordinates は異なる挙動をし、より危険です: それは空のチェックを生き延びつつ、すべてのビジネスを同じ座標に指し示している {"latitude": 0, "longitude": 0} で満たされています。位置には address を使用し、両方の数字がゼロでないときのみ座標ペアを信頼してください。
Q: APIキーはActivepiecesフローのどこに配置するべきですか?
プロジェクトレベルの値または接続内で、ヘッダーフィールドから参照されます。フロー定義はリテラルフィールド値を持ち、プロジェクト間でエクスポートおよび複製されるので、ステップに直接入力されたキーはすべてのコピーと共に移動します。
Q: このフローはWebhookの代わりにスケジュールで実行できますか?
はい。トリガーを スケジュール に切り替えると、フローの残りは変更されず、通常はテリトリーのリフレッシュに使用されます。ローカルランキングが実際にどれくらい頻繁に動くかに合わせて実行頻度を保ってください; 毎日がほとんどのカテゴリには十分であり、遅いリズムはボリュームを予測可能に保ちます。
Q: 同じフローはビジネスではなくウェブ結果に対して機能しますか?
はい。tbm を input オブジェクトから削除すると、結果が {{step_1.body.organic_results}} の下に到着し、それぞれ title、link、および snippet を持ちます。コードピースはそのパスをマッチするように更新する必要があり、ウェブ結果にはトリミングは不要です。
Scrapelessでは、適用される法律、規制、およびWebサイトのプライバシーポリシーを厳密に遵守しながら、公開されているデータのみにアクセスします。 このブログのコンテンツは、デモンストレーションのみを目的としており、違法または侵害の活動は含まれません。 このブログまたはサードパーティのリンクからの情報の使用に対するすべての責任を保証せず、放棄します。 スクレイピング活動に従事する前に、法律顧問に相談し、ターゲットウェブサイトの利用規約を確認するか、必要な許可を取得してください。



