Google Search Scraper API: 空のデータを返す5つのデフォルト
Lead Scraping Automation Engineer
TL;DR:
- Google Searchアクターからの
200はデータの証明ではありません:レスポンスは空のorganic_results配列を持つことがあり、リストの代わりに広告プレースホルダーを含むか、デザイン上空白のフィールドを持つことがあります。 - Difyは
401を{"code":14404,"message":"invalid access token"}で生成する二つのAPIキーのデフォルトを事前に入力します — ヘッダー名はAuthorizationにデフォルト設定され、ヘッダープレフィックスはBasicにデフォルト設定されていますが、アクターはどちらも受け入れません。 - n8nのワークフローはエラーゼロでバリデートできても実行時に失敗する可能性があります。なぜなら、バージョン2.34.4のCodeノードサンドボックスはグローバル
URLコンストラクターを公開しないからです。 - 検索ツールを与えられたエージェントは、それを呼び出さずに流暢なテキストを生成することができ、APIには一切触れていません。ツール呼び出しのカウントは、その沈黙のミスを失敗に変えます。
- ローカルパックのレコードは
place_id、gps_coordinates、thumbnailを空で返し、phone、type、hoursは先頭にスペースを含んでいます — 両方とも文書化された動作で、デバッグすべき欠陥ではありません。
Google Searchアクターが返すもの
scraper.google.searchアクターはクエリを受け取り、解析されたSERPをJSONとして返します。これはDeep SerpApiのGoogle表面で、通常はワークフロービルダーやエージェントフレームワークに接続される最初のアクターです。なぜなら、ランキング結果リストはランキング追跡とリードリサーチの両方に等しく効果的だからです。
以下の失敗は珍しいものではありません。これらは受け入れられたリクエストと使用可能なペイロードの間のギャップから来ています — この記事が例に使う四つのホストプラットフォーム(Dify、n8n、Activepieces、LangChain)から再現可能です。
リクエスト:エンドポイント、アクター、パラメータ
すべての呼び出しは二つのフィールドを持つ単一のエンドポイントへのPOSTです。actorがスクレイパーを選択し、inputがそのパラメータを持ちます:
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":"web scraping api"}}'
三つのパラメータがほとんどの作業をカバーします:
| パラメータ | 目的 |
|---|---|
q |
クエリ文字列。 |
tbm |
結果タイプ。lclはウェブ結果の代わりにローカルパックを返します。 |
start |
ペジネーションのための結果オフセット — ローカルパックでページあたり20です。 |
認証ヘッダーはx-api-tokenです。その名前は、ノーコードプラットフォームが自分のデフォルトで最も埋める可能性が高いフィールドです。401レスポンスのHTTPセマンティクス仕様は、リソース独自の認証スキームに結びついたチャレンジを期待しているため、Authorizationが合理的であると仮定するプラットフォームは誤っています — 単にこのエンドポイントに対して誤ったスキームを仮定しています。
レスポンスエンベロープ
データを読む前にエンベロープを読みます。成功したGoogle Search呼び出しは次のトップレベルキーを返します:
json
// illustrative sample — key shape only; values omitted
{
"search_information": {},
"organic_results": [],
"related_searches": [],
"pagination": {},
"metadata": {}
}
その形から二つのことが続きます。successフラグは分岐のためのものではないので、organic_resultsの存在と長さが信号です。そして、このエンベロープにはPeople-Also-Askブロックがありません — ブラウザで関連する質問を表示するクエリはここでrelated_searchesを返すため、質問配列を期待するワークフローはNoneを受け取り、空の列を記入します。
tbmがlclに設定されると、結果はlocal_results.places[]に移動しorganic_results[]の代わりにそれが返されます。パイプラインが一つのパスをハードコーディングすると、他のリクエストがされても静かに何も生成しません。
コードでレスポンスを読む
リクエストの後の主張はコピーする価値がある部分です。この例は空のリストを返す代わりに例外を投げるため、失敗はその場所で発生し、スプレッドシートの三つのステップ後ではありません:
python
import json
import os
import urllib.request
ENDPOINT = "https://api.scrapeless.com/api/v1/scraper/request"
def search(query: str) -> dict:
payload = json.dumps({"actor": "scraper.google.search", "input": {"q": query}}).encode()
request = urllib.request.Request(
ENDPOINT,
data=payload,
headers={
"Content-Type": "application/json",
"x-api-token": os.environ["SCRAPELESS_API_KEY"],
},
)
# urlopen raises HTTPError on any 4xx or 5xx, so a rejected call never reaches the parser.
with urllib.request.urlopen(request, timeout=120) as response:
return json.loads(response.read())
serp = search("web scraping api")
organic = serp.get("organic_results") or []
if not organic:
raise SystemExit(f"no organic_results in the response; envelope was {sorted(serp)}")
print(f"organic_results: {len(organic)}")
print(f"first result: {organic[0]['title']}")
print(f"envelope keys: {sorted(serp)}")
存在しないことと空であることは異なる状態であり、JSONインターチェンジフォーマット仕様は「キーが省略された」ということと「値が空の文字列である」ということを区別する助けを提供しません。パイプラインがエラーとして扱う状態を最初の挿入を書く前に決定してください。
無料プランでこれを進めることで、ここで説明されたすべての動作を見るのに十分です — Scrapelessアカウントを作成し、以下の四つのプラットフォーム全てで同じキーを使用してください。
空のデータを返す五つのデフォルト
プラットフォームが事前に入力したAPIキーのヘッダーは間違っています
Dify 1.16.1では、OpenAPIスキーマをカスタムツールとしてインポートし、API Key認証を選択すると、アクターが拒否する二つのフィールドがデフォルトで残ります。ヘッダー名はAuthorizationにデフォルト設定され、ヘッダープレフィックスはBasicにデフォルト設定されています — それにより名前を修正してもx-api-token: Basic <key>が送信されます。どちらも同じレスポンスを生成します:
json
{ "code": 14404, "message": "invalid access token" }
1つのエラーメッセージ、2つの独立した原因があり、これが診断を高コストにしています。稼働中の構成は3つすべての名前を示しています:
| フィールド | 値 |
|---|---|
| 認証タイプ | API Key |
| ヘッダー名 | x-api-token |
| ヘッダー接頭辞 | Custom |
Difyはまた、ネストされたリクエストボディオブジェクトを文字列パラメーターにフラット化するため、inputフィールドは構造化されたオブジェクトではなくテキストとして到着します。オブジェクトとJSON文字列の両方が受け入れられるため、input.qを読み取ろうとする下流ノードがあるまで、これが見逃されることはほとんどありません。
検証されるワークフローはランタイムで失敗する可能性がある
静的検証と実行はn8nのCodeノードで意見が一致しません。new URL(link).hostnameを使用してドメイン別に結果をグループ化するワークフローは、ゼロエラーで検証され、その後、URL is not definedを持つ最初のアイテムで失敗します。バージョン2.34.4のサンドボックスは、そのグローバルを公開していませんが、WHATWG URL StandardはこれをWeb APIコンストラクタとして定義し、n8n自身のURLコンストラクタなしで失敗するCodeノードの報告がその症状を記録しています。
文字列操作でホスト名を導出します:
javascript
// The Code node sandbox does not expose the global URL constructor,
// so the hostname comes from string operations.
const hostname = (link) =>
link ? link.replace(/^[a-z]+:\/\//i, '').replace(/^www\./i, '').split(/[/?#]/)[0] : '';
const results = [
{ position: 1, link: 'https://www.scrapeless.com/ja/product/deep-serp-api' },
{ position: 2, link: 'https://docs.scrapeless.com/en/deep-serp-api/quickstart/introduction/' },
];
for (const result of results) {
console.log(result.position, hostname(result.link));
}
ワークフロービルダーでの検証は、ノード内のコードではなくグラフをチェックします。したがって、緑のチェックはCodeノードが実行されるかどうかについて何も示しません。
何も解決しないステップ参照
Activepieces 0.82.0では、HTTPステップの解析されたJSONはbodyの下に存在します。その参照は{{step_1.body.organic_results}}で、{{step_1.organic_results}}は全く何も解決しません ― エラーも警告もなく、ただ空のループと成功を報告する実行があります。tbmがlclに設定されている場合、パスは{{step_1.body.local_results.places}}です。
失われた参照の失敗は、実際に空の結果セットと同じに見えるため、データ問題を探す前に参照パスを確認してください。
ツールを呼ばずに応答するエージェント
エージェントに検索ツールを与えると、それを使用しないかもしれません。小型モデルに検索ツールと取得ツールの両方を渡すと、しばしば検索を実行し、結果のスニペットから応答しながら「ページの言っていること」を説明します ― ページを取得しないことはありません。その文章は流暢であり、引用は暗黙的に提供されるため、出力内のどの情報も答えを根拠のないものとして示しません。
修正はアサーションであり、より良いプロンプトではありません。ツールコールをカウントし、ゼロを失敗として扱います:
注: このスニペットは既存のエージェントをラップしているため、実行には構築されたLangChainエージェントとモデルプロバイダキーが必要です。それが依存しているすべては標準の
agent.stream(...)出力です。
python
tool_calls = 0
for chunk in agent.stream({"messages": [("human", question)]}, stream_mode="values"):
message = chunk["messages"][-1]
tool_calls += len(getattr(message, "tool_calls", None) or [])
if tool_calls == 0:
raise SystemExit("the model answered without calling a tool; the answer is not grounded")
単一ツールの指示は小型モデルで信頼性があります。連鎖した指示 ― 検索、その後最上位の結果を取得 ― は、ツールコールが静かに失われる場所ですので、コード内でステップを分割し、モデルに1回の呼び出しだけを処理させます。
意図的に空のフィールド
いくつかの空白の値は正しいです。ローカルパックの結果では、place_id、gps_coordinates、thumbnailが空で返され、phone、type、hoursが先頭にスペースを持って到着します。どちらも欠陥ではなく、両方とも素朴なコードを破壊します: 後続スペースの不一致が重複排除キーを重複に変え、空のplace_idをエラーとして扱うと、文書化された通りに動作している動作のデバッグを送信することになります。
入力時に正規化します:
| フィールド | 挙動 | 処理 |
|---|---|---|
phone、type、hours |
先頭スペース | 保管または比較の前にトリムします。 |
place_id、gps_coordinates、thumbnail |
ローカル結果で空 | nullableとして扱います; それに基づいてレコードをゲートしないでください。 |
organic_results vs local_results.places |
tbmによる |
推測するのではなく、リクエストからパスを選択します。 |
同じ規律がカウントにも適用されます。結果の配列にはスポンサー付きスロットとレイアウトプレースホルダーがリストとともに含まれることがあるため、配列の長さは結果の数ではありません ― カウントを報告する前にレコードの独自の型フィールドでフィルタリングしてください、さもなければ下流のすべての数値は、ページが実際に提供した広告ロードを引き継ぎます。
結論
ノーコードのスクレイピングセットアップにおける高コストな失敗は、何もない緑で終了します: プラットフォームが事前に入力した認証ヘッダー、サンドボックスが省略するWeb APIのグローバル、1つのセグメントを欠く参照パス、ツールをスキップしたエージェント、または常に空白であるフィールド。それぞれに1行の修正があり、それを指摘するエラーメッセージはありません。
二つの習慣が全てを捉えます。データの前に応答封筒を読み、期待する内容を主張してください — 空でない配列、ツールの呼び出し、レコードタイプ — そうすれば、静かなミスはそれを引き起こしたステップで大きな失敗になります。n8n スクレイピングワークフローガイドとLangChain統合のウォークスルーは、これらのチェックが行われると、エンドツーエンドで接続された同じアクターを示しています。
文書化された封筒を返すSERPサーフェスに対して構築する準備はできていますか?完全なパラメータセットについては、Deep SerpApiドキュメントを確認し、プランと含まれるボリュームを見直し、無料プランを始めるようにしましょう。
FAQ
Q: なぜ私のGoogle検索アクター呼び出しは空のorganic_results配列で200を返すのですか?
空のorganic_results配列で200があるということは、リクエストが受け入れられ、解析されましたが、そのクエリシェイプに対してウェブ結果を生成しなかったことを意味します。順番に三つのことを確認してください:tbmがlclに設定されているかどうか、結果をlocal_results.places[]に移動する;クエリ自体が結果の意図を持っているか;そして、あなたのプラットフォームが封筒ではなく解析されたボディを読み取っているか。応答にはsuccessフラグがないため、配列の長さが唯一の信号です。
Q: キーが正しいとき、{"code":14404,"message":"invalid access token"}が発生する原因は何ですか?
その応答は、キーがエンドポイントが期待する形式で到着しなかったことを意味します。ヘッダーはx-api-tokenが必要で、ベアキーを持たなければなりません。Authorizationにデフォルト設定されたプラットフォームや、BasicまたはBearerを値の前に追加するプラットフォームは、エンドポイントが読み取れないヘッダーを送信します — メッセージはすべてのケースで同じですので、ヘッダー名や任意のプレフィックス設定を別々に確認してください。
Q: なぜ私のn8nコードノードはワークフローがバリデートされるとURL is not definedで失敗するのですか?
n8n 2.34.4のコードノードサンドボックスでは、グローバルURLコンストラクターが公開されず、ワークフローのバリデーションはノードコードを実行しないため、グラフはチェックを通過し、実行は最初のアイテムで失敗します。文字列操作でホスト名を解析するか、APIを提供するノードにURL処理を移動してください。
Q: エージェントが実際に検索ツールを使用したかどうかどうやって確認できますか?
ストリームされたメッセージのツール呼び出しをカウントし、カウントがゼロのときに失敗します。モデルはツールを呼び出さずに完全で自信のある回答を生成することができ、そのテキストからはそれと地に足のある回答を区別するものはありません。ツール呼び出しのカウントを、文章を検査するのではなく、厳しい要件として扱ってください。
Q: 空のplace_idおよびgps_coordinates値はバグですか?
いいえ。ローカルパックレコードはplace_id、gps_coordinates、およびthumbnailを空で返すため、これらのフィールドは設計上nullableです。レコードを保持し、存在するフィールドから位置を埋め込むことにより、行を破棄するのではなく、期待される動作周辺にエラーハンドリングを追加しないようにしてください。
Q: なぜ私のActivepiecesループはHTTPステップが成功したのにゼロ回繰り返されるのですか?
解析された応答はbodyの下にネストされているため、{{step_1.organic_results}}は何も解決せず、{{step_1.body.organic_results}}は配列に解決します。参照が欠けていると、Activepieces 0.82.0ではエラーは発生しません — ループは単に何も受け取らず、実行は成功を報告し続けるため、パスをチェックするまで空の結果セットからは区別できません。
Scrapelessでは、適用される法律、規制、およびWebサイトのプライバシーポリシーを厳密に遵守しながら、公開されているデータのみにアクセスします。 このブログのコンテンツは、デモンストレーションのみを目的としており、違法または侵害の活動は含まれません。 このブログまたはサードパーティのリンクからの情報の使用に対するすべての責任を保証せず、放棄します。 スクレイピング活動に従事する前に、法律顧問に相談し、ターゲットウェブサイトの利用規約を確認するか、必要な許可を取得してください。



