如何使用 TalorData 抓取 Google 地圖搜尋結果
了解如何使用 TalorData SERP API 抓取 Google 地圖搜尋結果。本技術指南涵蓋了地圖參數、Python requests 庫的使用、本地商家資訊欄位(包括評分、評論、地址、電話號碼、網站、地點 ID 和座標)、CSV 匯出以及本地 SEO 工作流程。
Google Maps results 是典型的 local-intent data。它能告訴你某個查詢下哪些商家出現、排名如何、評價強不強、聯絡方式是否完整,以及商家在本地市場中的可見度。
如果你在做 local SEO tools、lead generation workflows、market research dashboards、competitor monitors 或 AI agents,Google Maps data 可以回答這些問題:
|
問題 |
為什麼重要 |
|
“dentist near me” 會出現哪些商家? |
Local visibility |
|
指定座標附近,哪個競品排名更高? |
Local SEO tracking |
|
哪些商家 rating 和 review count 更強? |
Reputation analysis |
|
哪些 listing 有 phone 和 website? |
Lead enrichment |
|
哪些區域競爭更密集? |
Market research |
|
哪些 listing 在多個城市都出現? |
Expansion planning |
|
AI agent 應該推薦哪些本地商家? |
AI workflow context |
這篇指南會建立一個 Python workflow:把 Google Maps query 發送到 TalorData,取得 structured JSON,提取本地商家欄位,最後匯出 CSV。
整體流程:
Search query + map location
↓
TalorData SERP API
↓
Google Maps results in JSON
↓
Parse business fields
↓
Normalize place data
↓
Export to CSV or database
什麼是抓取 Google Maps search results?
這裡的 “scraping Google Maps”,指的是透過 TalorData SERP API 收集結構化 Google Maps-style search results。
它不是控制瀏覽器、手動滾動頁面,或從 Google Maps 介面抓 HTML。
我們要收集的是這些欄位:
|
Field |
Example |
|
Business name |
Blue Bottle Coffee |
|
Position |
1 |
|
Rating |
4.5 |
|
Review count |
1,240 |
|
Category |
Coffee shop |
|
Address |
123 Example St, New York |
|
Phone number |
+1 212-000-0000 |
|
Website |
https://example.com |
|
Google Maps URL |
Maps listing link |
|
Place ID / data CID |
Unique place reference |
|
Latitude / longitude |
Map coordinates |
|
Opening status |
Open now / closed |
|
Price level |
$, $$, $$$ |
TalorData Google Maps response 可用於 local business search workflows,並可在可用時返回 business name、place ID、address、coordinates、category、rating、review count、rank position、opening status、phone number、website、Google Maps URL、thumbnail 和 price level 等欄位。
什麼時候適合使用 Google Maps SERP API?
當你的問題和 local visibility 有關,就適合使用 Google Maps search results。
|
Use case |
Example |
|
Local SEO rank tracking |
在不同座標追蹤 “plumber near me” |
|
Lead generation |
收集帶 phone 和 website 的本地商家 |
|
Competitor research |
比較 rating、reviews 和 positions |
|
Market analysis |
按城市統計某品類商家 |
|
Franchise monitoring |
監控品牌在多市場的可見度 |
|
Review intelligence |
追蹤 rating 和 review count |
|
AI agents |
把本地商家選項餵給推薦流程 |
Google Maps rankings 對 location 很敏感。同一個 query,在不同座標附近可能返回不同商家。所以 location control 很重要。
核心 Google Maps 參數
先從這些參數開始。
|
Parameter |
Required |
Purpose |
Example |
|
|
Yes |
選擇 Google Maps search |
|
|
|
Search queries |
商家、品類或 local intent query |
|
|
|
建議使用 |
Latitude、longitude 和 zoom |
|
|
|
Optional |
Search mode |
|
|
|
Place lookup |
Google Maps place reference |
|
|
|
Place lookup |
Google CID-style place reference |
|
|
|
Optional |
Google domain |
|
|
|
Optional |
介面語言 |
|
|
|
Optional |
國家上下文 |
|
|
|
Optional |
Freshness control |
|
Google Maps requests 中,ll 使用 @latitude,longitude,scale 這類座標格式,Maps search type 支援 search-style 和 place-style workflow。
Search mode vs place mode
通常有兩種使用方式。
Search mode
當你需要一組商家列表時,用 search mode。
範例:
coffee shops near Manhattan
dentists in Austin
hotels near JFK Airport
plumbers in Chicago
restaurants near Times Square
Search request 需要 query 和 location context。
{
"engine": "google_maps",
"type": "search",
"q": "coffee shops",
"ll": "@40.7455096,-74.0083012,14z",
"hl": "en",
"gl": "us"
}
Place mode
當你想查某個已知商家的詳細資料時,用 place mode。
你可以使用之前 search result 中的 place_id 或 data_cid。
{
"engine": "google_maps",
"type": "place",
"place_id": "ChIJExamplePlaceId",
"hl": "en",
"gl": "us"
}
常見流程:
Search query
↓
Get local business results
↓
Extract place_id or data_cid
↓
Request place details
↓
Store richer business data
Step 1:設定 API key
將 TalorData API key 和 endpoint 存成環境變數。查看設置指南>>
export TALORDATA_API_KEY="your_api_key_here"
export TALORDATA_SERP_ENDPOINT="your_talordata_serp_endpoint_here"
Windows PowerShell:
setx TALORDATA_API_KEY "your_api_key_here"
setx TALORDATA_SERP_ENDPOINT "your_talordata_serp_endpoint_here"
安裝 Python dependencies:
pip install requests pandas
Step 2:建立可重用 Python client
這個 function 會發送 Google Maps search request 並返回 JSON。
如果你的 TalorData account 使用不同 request style,請調整 endpoint 或 authentication format。
import os
import requests
from typing import Any, Dict, Optional
TALORDATA_API_KEY = os.getenv("TALORDATA_API_KEY")
TALORDATA_SERP_ENDPOINT = os.getenv("TALORDATA_SERP_ENDPOINT")
def fetch_google_maps(
query: str,
ll: str,
hl: str = "en",
gl: str = "us",
google_domain: str = "google.com",
no_cache: bool = True,
extra_params: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""
Fetch Google Maps search results with TalorData SERP API.
ll format example:
@40.7455096,-74.0083012,14z
"""
if not TALORDATA_API_KEY:
raise RuntimeError("Missing TALORDATA_API_KEY environment variable.")
if not TALORDATA_SERP_ENDPOINT:
raise RuntimeError("Missing TALORDATA_SERP_ENDPOINT environment variable.")
payload: Dict[str, Any] = {
"engine": "google_maps",
"type": "search",
"q": query,
"ll": ll,
"hl": hl,
"gl": gl,
"google_domain": google_domain,
"no_cache": no_cache
}
if extra_params:
payload.update(extra_params)
headers = {
"Authorization": f"Bearer {TALORDATA_API_KEY}",
"Content-Type": "application/json"
}
response = requests.post(
TALORDATA_SERP_ENDPOINT,
json=payload,
headers=headers,
timeout=30
)
response.raise_for_status()
return response.json()
執行基本搜尋:
if __name__ == "__main__":
data = fetch_google_maps(
query="coffee shops",
ll="@40.7455096,-74.0083012,14z",
hl="en",
gl="us"
)
print(data)
Step 3:檢查 raw response
寫 parser 前,先檢查 JSON。
import json
print(json.dumps(data, indent=2, ensure_ascii=False)[:5000])
可以找這類 result arrays:
local_results
maps_results
place_results
results
organic_results
實際 top-level field 可能會因 response type 而變。不要太早把 parser 刻在石頭上。先看洞穴牆,再拿鑿子。
Step 4:解析 Google Maps results
下面是一個保守 parser,會檢查幾個可能的結果容器,並提取常見本地商家欄位。
from typing import Any, Dict, List
def get_first_available_list(data: Dict[str, Any], keys: List[str]) -> List[Dict[str, Any]]:
for key in keys:
value = data.get(key)
if isinstance(value, list):
return value
return []
def parse_maps_results(data: Dict[str, Any]) -> List[Dict[str, Any]]:
items = get_first_available_list(
data,
keys=[
"local_results",
"maps_results",
"place_results",
"results",
"organic_results"
]
)
rows = []
for index, item in enumerate(items, start=1):
gps = item.get("gps_coordinates") or {}
links = item.get("links") or {}
rows.append({
"position": item.get("position", index),
"title": item.get("title") or item.get("name"),
"place_id": item.get("place_id"),
"data_cid": item.get("data_cid") or item.get("cid"),
"category": item.get("type") or item.get("category"),
"rating": item.get("rating"),
"review_count": item.get("reviews") or item.get("review_count"),
"address": item.get("address"),
"phone": item.get("phone"),
"website": item.get("website") or links.get("website"),
"maps_url": item.get("link") or item.get("maps_url"),
"thumbnail": item.get("thumbnail"),
"price_level": item.get("price") or item.get("price_level"),
"open_state": item.get("open_state") or item.get("hours"),
"latitude": gps.get("latitude") or item.get("latitude"),
"longitude": gps.get("longitude") or item.get("longitude")
})
return rows
使用:
data = fetch_google_maps(
query="coffee shops",
ll="@40.7455096,-74.0083012,14z"
)
rows = parse_maps_results(data)
for row in rows[:5]:
print(row["position"], row["title"], row["rating"], row["review_count"])
Step 5:保存成 CSV
import pandas as pd
data = fetch_google_maps(
query="coffee shops",
ll="@40.7455096,-74.0083012,14z"
)
rows = parse_maps_results(data)
df = pd.DataFrame(rows)
df.to_csv("google_maps_results.csv", index=False)
print(df.head())
CSV 可能長這樣:
|
position |
title |
rating |
review_count |
category |
address |
|
1 |
Example Coffee |
4.6 |
1280 |
Coffee shop |
123 Example St |
|
2 |
Sample Cafe |
4.4 |
760 |
Cafe |
45 Sample Ave |
|
3 |
Local Roasters |
4.7 |
420 |
Coffee roasters |
88 Market Rd |
Step 6:加入 search context
沒有 context 的 Maps result,很難在之後比較。
每一列都應該保存 query、coordinate、language、country 和 timestamp。
from datetime import datetime, timezone
def add_search_context(
rows: List[Dict[str, Any]],
query: str,
ll: str,
hl: str,
gl: str
) -> List[Dict[str, Any]]:
collected_at = datetime.now(timezone.utc).isoformat()
for row in rows:
row["query"] = query
row["ll"] = ll
row["hl"] = hl
row["gl"] = gl
row["collected_at"] = collected_at
return rows
使用:
query = "coffee shops"
ll = "@40.7455096,-74.0083012,14z"
hl = "en"
gl = "us"
data = fetch_google_maps(query=query, ll=ll, hl=hl, gl=gl)
rows = parse_maps_results(data)
rows = add_search_context(rows, query=query, ll=ll, hl=hl, gl=gl)
pd.DataFrame(rows).to_csv("google_maps_snapshot.csv", index=False)
接著就能比較變化:
|
Change |
Meaning |
|
Position changed |
本地排名變化 |
|
Rating changed |
口碑變化 |
|
Review count increased |
更多顧客回饋 |
|
Business disappeared |
排名或可見度變化 |
|
New competitor appeared |
市場變化 |
|
Website added or removed |
Listing completeness 變化 |
Step 7:搜尋多個座標
對 local SEO 來說,只查一個 city-level location 通常太粗。
更好的方式,是對同一個 query 跑 coordinate grid。
locations = [
{
"name": "Manhattan West",
"ll": "@40.7455096,-74.0083012,14z"
},
{
"name": "Times Square",
"ll": "@40.758896,-73.985130,14z"
},
{
"name": "Lower Manhattan",
"ll": "@40.707491,-74.011276,14z"
}
]
all_rows = []
for location in locations:
data = fetch_google_maps(
query="coffee shops",
ll=location["ll"],
hl="en",
gl="us"
)
rows = parse_maps_results(data)
for row in rows:
row["grid_location"] = location["name"]
rows = add_search_context(
rows,
query="coffee shops",
ll=location["ll"],
hl="en",
gl="us"
)
all_rows.extend(rows)
pd.DataFrame(all_rows).to_csv("google_maps_grid_results.csv", index=False)
這可以幫你發現:
|
Signal |
Example |
|
Local rank variation |
某咖啡店在一個座標 #1,在另一個座標 #8 |
|
Competitor clusters |
某區域競品密集 |
|
Visibility gaps |
商家離開小半徑後就消失 |
|
Review advantage |
評論更強的競品更常出現 |
|
Expansion areas |
某些區域強 listing 較少 |
Step 8:取得 place-level details
Search result 給你一組列表。Place lookup 可以讓你深入看一個商家。
使用 search result 中的 place_id 或 data_cid。
def fetch_google_maps_place(
place_id: Optional[str] = None,
data_cid: Optional[str] = None,
hl: str = "en",
gl: str = "us",
google_domain: str = "google.com"
) -> Dict[str, Any]:
if not place_id and not data_cid:
raise ValueError("Provide either place_id or data_cid.")
payload: Dict[str, Any] = {
"engine": "google_maps",
"type": "place",
"hl": hl,
"gl": gl,
"google_domain": google_domain
}
if place_id:
payload["place_id"] = place_id
if data_cid:
payload["data_cid"] = data_cid
response = requests.post(
TALORDATA_SERP_ENDPOINT,
json=payload,
headers={
"Authorization": f"Bearer {TALORDATA_API_KEY}",
"Content-Type": "application/json"
},
timeout=30
)
response.raise_for_status()
return response.json()
範例:
rows = parse_maps_results(data)
first_place_id = rows[0].get("place_id")
place_data = fetch_google_maps_place(place_id=first_place_id)
print(json.dumps(place_data, indent=2, ensure_ascii=False)[:3000])
當你想在搜尋後補充特定商家詳情時,用 place mode。
Step 9:完整 script
下面是一個完整範例:搜尋 Google Maps、解析本地商家欄位、加入 context,最後匯出 CSV。
import os
import json
import requests
import pandas as pd
from datetime import datetime, timezone
from typing import Any, Dict, List, Optional
TALORDATA_API_KEY = os.getenv("TALORDATA_API_KEY")
TALORDATA_SERP_ENDPOINT = os.getenv("TALORDATA_SERP_ENDPOINT")
def fetch_google_maps(
query: str,
ll: str,
hl: str = "en",
gl: str = "us",
google_domain: str = "google.com",
no_cache: bool = True,
extra_params: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
if not TALORDATA_API_KEY:
raise RuntimeError("Missing TALORDATA_API_KEY environment variable.")
if not TALORDATA_SERP_ENDPOINT:
raise RuntimeError("Missing TALORDATA_SERP_ENDPOINT environment variable.")
payload: Dict[str, Any] = {
"engine": "google_maps",
"type": "search",
"q": query,
"ll": ll,
"hl": hl,
"gl": gl,
"google_domain": google_domain,
"no_cache": no_cache
}
if extra_params:
payload.update(extra_params)
response = requests.post(
TALORDATA_SERP_ENDPOINT,
json=payload,
headers={
"Authorization": f"Bearer {TALORDATA_API_KEY}",
"Content-Type": "application/json"
},
timeout=30
)
response.raise_for_status()
return response.json()
def get_first_available_list(data: Dict[str, Any], keys: List[str]) -> List[Dict[str, Any]]:
for key in keys:
value = data.get(key)
if isinstance(value, list):
return value
return []
def parse_maps_results(data: Dict[str, Any]) -> List[Dict[str, Any]]:
items = get_first_available_list(
data,
keys=[
"local_results",
"maps_results",
"place_results",
"results",
"organic_results"
]
)
rows = []
for index, item in enumerate(items, start=1):
gps = item.get("gps_coordinates") or {}
links = item.get("links") or {}
rows.append({
"position": item.get("position", index),
"title": item.get("title") or item.get("name"),
"place_id": item.get("place_id"),
"data_cid": item.get("data_cid") or item.get("cid"),
"category": item.get("type") or item.get("category"),
"rating": item.get("rating"),
"review_count": item.get("reviews") or item.get("review_count"),
"address": item.get("address"),
"phone": item.get("phone"),
"website": item.get("website") or links.get("website"),
"maps_url": item.get("link") or item.get("maps_url"),
"thumbnail": item.get("thumbnail"),
"price_level": item.get("price") or item.get("price_level"),
"open_state": item.get("open_state") or item.get("hours"),
"latitude": gps.get("latitude") or item.get("latitude"),
"longitude": gps.get("longitude") or item.get("longitude")
})
return rows
def add_search_context(
rows: List[Dict[str, Any]],
query: str,
ll: str,
hl: str,
gl: str
) -> List[Dict[str, Any]]:
collected_at = datetime.now(timezone.utc).isoformat()
for row in rows:
row["query"] = query
row["ll"] = ll
row["hl"] = hl
row["gl"] = gl
row["collected_at"] = collected_at
return rows
def main() -> None:
query = "coffee shops"
ll = "@40.7455096,-74.0083012,14z"
hl = "en"
gl = "us"
raw_data = fetch_google_maps(
query=query,
ll=ll,
hl=hl,
gl=gl
)
rows = parse_maps_results(raw_data)
rows = add_search_context(rows, query=query, ll=ll, hl=hl, gl=gl)
pd.DataFrame(rows).to_csv("google_maps_results.csv", index=False)
with open("raw_google_maps_response.json", "w", encoding="utf-8") as file:
json.dump(raw_data, file, ensure_ascii=False, indent=2)
print(f"Saved {len(rows)} rows to google_maps_results.csv")
print("Saved raw_google_maps_response.json")
if __name__ == "__main__":
main()
應該保存哪些欄位?
大多數 Google Maps scraping workflows 可以先用這個 schema:
{
"query": "coffee shops",
"ll": "@40.7455096,-74.0083012,14z",
"hl": "en",
"gl": "us",
"collected_at": "2026-06-30T09:00:00Z",
"business": {
"position": 1,
"title": "Example Coffee",
"place_id": "ChIJExample",
"data_cid": "123456789",
"category": "Coffee shop",
"rating": 4.6,
"review_count": 1280,
"address": "123 Example St, New York, NY",
"phone": "+1 212-000-0000",
"website": "https://example.com",
"maps_url": "https://...",
"latitude": 40.7455,
"longitude": -74.0083,
"open_state": "Open",
"price_level": "$$"
}
}
進階 workflow 可以加:
|
Field |
Use case |
|
|
UI display |
|
|
Business availability |
|
|
餐廳或服務篩選 |
|
|
需求模式分析 |
|
|
Reputation intelligence |
|
|
Review management |
|
|
Conversion tracking |
|
|
Restaurant intelligence |
|
|
Classification |
|
|
Debug 和 reprocessing |
常見錯誤
沒有使用座標搜尋
對 Maps 來說,location 不是裝飾。需要 local precision 時,請使用 ll。
比較不同 zoom level 的結果
14z 和 16z 可能返回不同結果。保存完整 ll value。
只看 rating
4.8 分但只有 12 則評論,和 4.6 分但有 2,000 則評論,不是同一件事。Rating 和 review count 要一起看。
忽略 business identity
商家名稱可能會變。去重時優先使用 place_id 或 data_cid。
不保存 raw JSON
開發階段一定要保存 raw response。後續調整 parser 會更容易。
忘記 timestamps
Maps data 會變。沒有 timestamp 的 snapshot,就像沒有標籤的化石。
結語
使用 TalorData 抓取 Google Maps search results,本質上是把 local search 變成 structured data。
先從 query、coordinate、language、country 和 Google Maps search type 開始。然後解析 business name、position、rating、reviews、category、address、phone、website、place ID、coordinates、opening status 和 Maps URL。
資料結構化後,就可以用於 local SEO、lead generation、competitor monitoring、market research、franchise tracking 和 AI agents。
Google Maps 是一個活著的本地市場。好的 API workflow,會把這個市場整理成系統能理解的資料列。