如何抓取Google Shopping价格:API与HTML的取舍
如果你正在做竞品价格监控,第一个问题通常是:能不能用官方API?答案是——不能用于竞品分析。Google Content API for Shopping 只返回你自己Merchant Center账户中的商品数据,它是一个商家管理工具,不是竞争情报数据源。要获取竞争对手在Google Shopping上的价格、卖家和评分,你必须解析 tbm=shop 搜索结果HTML或 /shopping/product/ 商品详情页。
这就是本指南的核心:如何抓取Google Shopping价格——从URL构造、CSS选择器、反爬绕过到住宅代理配置,给出可直接运行的Python代码和工程实践。
Google反爬技术栈:你面对的是什么
Google Shopping的搜索结果页面与常规搜索共享同一套反爬基础设施。了解这套机制是成功抓取的前提。
reCAPTCHA与sorry/index重定向
当Google检测到可疑流量时,最常见的第一步是将请求重定向到 https://www.google.com/sorry/index,页面显示一个reCAPTCHA挑战。如果你用 requests 库直接请求,你会收到一个HTTP 429状态码或302重定向。这是硬封锁的信号——当前IP已被临时拉黑。
根据Google搜索中心文档的说明,Google使用多层信号来判断自动化流量,包括请求频率、IP信誉、浏览器指纹和行为模式。实际测试中,单IP在连续发送约50-80个请求后即可能触发封锁,具体阈值因IP段信誉而异。
每IP速率限制
Google的速率限制是按IP而非按账户执行的(对于未认证的SERP请求)。这意味着:
- 同一IP短时间内大量请求 → 触发sorry/index重定向
- 数据中心IP段(如AWS、GCP)的阈值更低,因为Google已知这些是云服务器
- 住宅IP的阈值相对更高,因为它们与真实用户流量混合
这就是为什么住宅代理对Google Shopping抓取至关重要——它们使用真实ISP分配的IP,信誉评分远高于数据中心IP。
URL模式与CSS选择器详解
要抓取Google Shopping,你需要知道两个关键URL模式和一组CSS选择器。
搜索结果URL模式
Google Shopping搜索的基本URL格式为:
https://www.google.com/search?tbm=shop&q=YOUR_QUERY&gl=US&hl=en
关键参数:
tbm=shop— 切换到Shopping垂直搜索q— 搜索关键词gl— 国家代码(如US、DE、JP),影响显示的卖家和货币hl— 语言代码(如en、de、ja),影响UI语言和部分结果排序
商品详情页URL模式
点击搜索结果中的商品会跳转到商品详情页:
https://www.google.com/shopping/product/PRODUCT_ID?gl=US&hl=en
此页面包含更详细的卖家报价列表、规格参数和评论摘要。
核心CSS选择器
以下是2025-2026年验证有效的Google Shopping搜索结果选择器(注意:Google会不定期更新DOM结构,建议定期验证):
| 数据点 | CSS选择器 | 说明 |
|---|---|---|
| 结果容器 | .sh-dgr__content | 每个商品卡片的根节点 |
| 结果网格 | .sh-pr__product-results | 整个结果列表的容器 |
| 商品标题 | .sh-dgr__content h3 或 [role="heading"] | 商品名称文本 |
| 价格 | .a8Pemb | 当前价格(含货币符号) |
| 卖家名称 | .a8Pemb-O5lDed 附近文本 | 可能需要从多个节点提取 |
| 评分 | [role="img"] aria-label | 包含"Rated X out of 5"文本 |
| 商品链接 | .sh-dgr__content a[href] | 指向/shopping/product/的链接 |
实战提示:Google的CSS类名经常变化(如
.a8Pemb这类自动生成的类名)。建议同时使用多个备选选择器和结构化数据属性(如role、data-*)来提高解析鲁棒性。
为什么本地化价格需要城市级住宅代理
Google Shopping的价格和卖家列表是高度本地化的。一个美国购物者和一个德国购物者搜索同一个关键词,看到的结果可能完全不同:
- 不同的卖家(部分卖家只在特定国家运营)
- 不同的货币(USD vs EUR)
- 不同的价格(同一商品在不同市场定价不同)
- 不同的运费和库存状态
仅靠 gl 和 hl URL参数不够——Google也会根据请求IP的地理位置返回结果。如果你从一个美国数据中心IP请求 gl=DE,Google可能仍然返回部分美国卖家的结果,或者触发更严格的反爬检查。
正确做法是让代理IP的地理位置与 gl 参数一致。ProxyHat支持在用户名中指定国家和城市:
# 美国购物者视角
http://user-country-US:pass@gate.proxyhat.com:8080
# 德国柏林购物者视角
http://user-country-DE-city-berlin:pass@gate.proxyhat.com:8080
# 日本东京购物者视角
http://user-country-JP-city-tokyo:pass@gate.proxyhat.com:8080
这样,请求IP的地理位置与 gl 参数匹配,Google会返回该地区购物者看到的真实结果。查看ProxyHat支持的全球位置请访问位置列表。
Python实战:通过ProxyHat抓取Google Shopping
下面是一个完整的Python示例,使用 requests 和 BeautifulSoup 通过ProxyHat住宅代理抓取Google Shopping搜索结果。
基础请求函数
import requests
from bs4 import BeautifulSoup
import random
import time
# ProxyHat住宅代理配置
def build_proxy_url(country="US", city=None, session_id=None):
username = f"user-country-{country}"
if city:
username += f"-city-{city}"
if session_id:
username += f"-session-{session_id}"
password = "YOUR_PASSWORD"
return f"http://{username}:{password}@gate.proxyhat.com:8080"
def fetch_google_shopping(query, country="US", hl="en", city=None):
proxy_url = build_proxy_url(country=country, city=city)
proxies = {"http": proxy_url, "https": proxy_url}
params = {
"q": query,
"tbm": "shop",
"gl": country,
"hl": hl,
}
headers = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36"
),
"Accept-Language": f"{hl},{country.lower()};q=0.9",
"Accept": "text/html,application/xhtml+xml",
}
url = "https://www.google.com/search"
resp = requests.get(url, params=params, headers=headers,
proxies=proxies, timeout=30)
# 检测软封锁
if "sorry/index" in resp.url:
raise RuntimeError("检测到软封锁:被重定向到sorry/index")
if resp.status_code == 429:
raise RuntimeError("速率限制:HTTP 429")
return resp.text
解析搜索结果
def parse_shopping_results(html):
soup = BeautifulSoup(html, "html.parser")
results = []
# 查找所有商品卡片
cards = soup.select(".sh-dgr__content")
if not cards:
# 备选选择器
cards = soup.select("[data-docid]")
for card in cards:
item = {}
# 标题
title_node = card.select_one("h3") or card.select_one('[role="heading"]')
item["title"] = title_node.get_text(strip=True) if title_node else None
# 价格
price_node = card.select_one(".a8Pemb")
if price_node:
item["price"] = price_node.get_text(strip=True)
else:
# 备选:查找包含货币符号的文本
text = card.get_text()
import re
match = re.search(r'[\$€£¥]\d+[.,]\d{2}', text)
item["price"] = match.group(0) if match else None
# 卖家
seller_node = card.select_one(".a8Pemb-O5lDed")
item["seller"] = seller_node.get_text(strip=True) if seller_node else None
# 评分
rating_node = card.select_one('[role="img"][aria-label*="Rated"]')
if rating_node:
item["rating"] = rating_node.get("aria-label", "")
# 商品链接
link_node = card.select_one("a[href*='/shopping/product/']")
if link_node:
item["product_url"] = "https://www.google.com" + link_node["href"]
if item["title"] and item["price"]:
results.append(item)
return results
# 使用示例
html = fetch_google_shopping("iPhone 15 Pro 128GB", country="US")
results = parse_shopping_results(html)
for r in results[:3]:
print(f"标题: {r['title']}")
print(f"价格: {r['price']}")
print(f"卖家: {r.get('seller', 'N/A')}")
print(f"评分: {r.get('rating', 'N/A')}")
print("---")
示例输出(截断):
标题: Apple iPhone 15 Pro 128GB - Titanium Blue
价格: $999.00
卖家: Best Buy
评分: Rated 4.6 out of 5
---
标题: Apple iPhone 15 Pro 128GB Unlocked
价格: $979.99
卖家: Amazon.com
评分: Rated 4.5 out of 5
---
抓取商品详情页的卖家报价
def fetch_product_offers(product_id, country="US", city=None):
proxy_url = build_proxy_url(country=country, city=city)
proxies = {"http": proxy_url, "https": proxy_url}
url = f"https://www.google.com/shopping/product/{product_id}"
params = {"gl": country, "hl": "en"}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36",
"Accept-Language": f"en,{country.lower()};q=0.9",
}
resp = requests.get(url, params=params, headers=headers,
proxies=proxies, timeout=30)
soup = BeautifulSoup(resp.text, "html.parser")
offers = []
# 卖家报价面板
offer_rows = soup.select("[data-shp-handler]")
for row in offer_rows:
offer = {}
price_node = row.select_one(".a8Pemb")
seller_node = row.select_one("[data-shp-handler-title]")
offer["price"] = price_node.get_text(strip=True) if price_node else None
offer["seller"] = seller_node.get_text(strip=True) if seller_node else None
if offer["price"]:
offers.append(offer)
return offers
分页、批量查询与软封锁检测
分页策略
Google Shopping搜索结果通常每页显示约20-40个商品。要获取更多结果,可以使用 start 参数进行分页:
def scrape_all_pages(query, country="US", max_pages=5):
all_results = []
for page in range(max_pages):
start = page * 20 # 每页约20条
params = {
"q": query, "tbm": "shop",
"gl": country, "hl": "en",
"start": start,
}
# 每次请求使用不同的session ID实现IP轮换
session_id = f"shop-{query[:10]}-{page}"
proxy_url = build_proxy_url(country=country, session_id=session_id)
html = fetch_google_shopping(query, country=country)
page_results = parse_shopping_results(html)
if not page_results:
print(f"第{page+1}页无结果,停止分页")
break
all_results.extend(page_results)
# 随机延迟 3-8 秒
delay = random.uniform(3, 8)
time.sleep(delay)
return all_results
批量查询处理
当你需要监控数百或数千个关键词的价格时,批量处理是必须的。关键原则:
- 分批执行:每批5-10个关键词,批间暂停30-60秒
- 随机化延迟:每个请求间设置3-8秒随机延迟,模拟人类行为
- IP轮换:每个请求使用不同的session ID,让ProxyHat自动分配新IP
- 并发控制:不超过5-10个并发会话
import random
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
def batch_scrape_keywords(keywords, country="US", max_workers=5):
results = {}
def scrape_one(kw):
try:
html = fetch_google_shopping(kw, country=country)
items = parse_shopping_results(html)
time.sleep(random.uniform(3, 8))
return kw, items
except RuntimeError as e:
print(f"软封锁检测 [{kw}]: {e}")
return kw, []
# 分批处理,每批max_workers个关键词
batch_size = max_workers
for i in range(0, len(keywords), batch_size):
batch = keywords[i:i+batch_size]
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {executor.submit(scrape_one, kw): kw for kw in batch}
for future in as_completed(futures):
kw, items = future.result()
results[kw] = items
# 批间暂停
if i + batch_size < len(keywords):
pause = random.uniform(30, 60)
print(f"批间暂停 {pause:.1f} 秒...")
time.sleep(pause)
return results
软封锁检测清单
在硬CAPTCHA出现之前,Google通常会发出软封锁信号。检测这些信号可以让你在完全被封之前调整策略:
| 信号 | 检测方法 | 应对措施 |
|---|---|---|
| 重定向到sorry/index | 检查 resp.url 是否包含 "sorry" | 立即暂停,更换IP段 |
| HTTP 429 | 检查状态码 | 退避指数等待,切换session |
| 空结果页 | 解析结果数为0且页面有DOM | 可能IP被降级,换IP重试 |
| CAPTCHA页面 | 检查HTML含"captcha"关键词 | 停止当前IP所有请求 |
| 结果数量骤降 | 对比历史数据,成功率低于50% | 降低频率,扩大IP池 |
伦理、合规与法律考量
抓取Google Shopping价格数据涉及几个重要的法律和伦理边界,必须在实施前了解。
公开数据 vs 受限数据
本指南讨论的是公开可见的价格数据——任何用户在浏览器中访问Google Shopping都能看到的信息。这不同于:
- 需要登录才能查看的数据
- 通过逆向工程API获取的非公开数据
- 受版权保护的图片或详细产品描述
CFAA与GDPR风险
在美国,Computer Fraud and Abuse Act (CFAA) 历史上被用于起诉违反网站服务条款的自动化访问。虽然2022年hiQ Labs诉LinkedIn案后法院对"超出授权访问"的解读有所收窄,但绕过技术访问控制(如CAPTCHA)仍可能构成风险。
在欧盟,GDPR主要关注个人数据处理。Google Shopping的公开价格数据本身不涉及个人数据,但如果你的抓取行为收集了用户评论中的个人信息或cookie中的用户标识,则可能触发GDPR合规要求。
robots.txt与服务条款
Google的robots.txt对 /search 路径设置了 Disallow,这意味着Google从技术声明上不希望自动化爬虫访问搜索结果。你需要自行评估这一声明对你所在司法管辖区的法律影响。
当你需要大规模、持续的Google Shopping数据时,合规路线是使用Google官方的Shopping API合作伙伴或SERP API服务。这些服务通过官方渠道获取数据,承担合规责任。对于小规模、低频率的公开价格监控,住宅代理方案在技术上可行,但你需要:
- 仅抓取公开价格数据,不存储个人身份信息
- 遵守合理的请求频率,不对Google服务造成负担
- 了解你所在地区的法律框架
- 咨询法律顾问评估具体用例
ProxyHat配置与最佳实践
使用ProxyHat住宅代理抓取Google Shopping时,以下配置建议能最大化成功率:
代理类型选择
| 代理类型 | 推荐度 | 适用场景 | 成本 |
|---|---|---|---|
| 住宅代理 | ★★★★★ | Google Shopping抓取首选,IP信誉高 | 中等 |
| 移动代理 | ★★★★☆ | 极高信誉,但成本较高 | 高 |
| 数据中心代理 | ★☆☆☆☆ | 不推荐,Google易识别并封锁 | 低 |
会话管理
ProxyHat支持通过session ID控制IP粘性。对于Google Shopping抓取,推荐策略是每请求轮换(不指定session ID),让每个请求获得新IP。如果需要保持同一IP来浏览多个页面(如搜索结果→商品详情),则使用相同session ID:
# 每请求轮换IP(推荐用于搜索结果抓取)
http://user-country-US:pass@gate.proxyhat.com:8080
# 粘性会话(用于搜索→详情页的连续浏览)
http://user-country-US-session-abc123:pass@gate.proxyhat.com:8080
更多代理配置和定价信息请查看ProxyHat定价页面。如需了解更广泛的网页抓取用例,可参考网页抓取用例和SERP追踪用例。完整API文档请访问ProxyHat文档。
关键要点总结
Key Takeaways:
- Google Content API for Shopping只返回自有商品数据,竞品价格必须通过HTML解析获取
- 搜索URL格式:
google.com/search?tbm=shop&q=KEYWORD&gl=COUNTRY&hl=LANG- 核心选择器:
.sh-dgr__content(卡片)、.a8Pemb(价格)、h3(标题)- 住宅代理是必须的——数据中心IP会在约50-80个请求内被封锁
- 城市级地理定位确保本地化价格准确:
user-country-DE-city-berlin- 随机延迟3-8秒、并发不超过5-10、分批处理是稳定运行的关键
- 检测sorry/index重定向和HTTP 429作为软封锁信号
- 公开价格数据抓取需评估CFAA、GDPR和robots.txt的法律影响
常见问题
什么是抓取Google Shopping价格?
抓取Google Shopping价格是指通过程序化方式获取Google购物搜索结果页面中的商品价格、卖家、评分等数据。由于Google Shopping Content API只返回商家自己的商品数据,获取竞争对手价格需要解析tbm=shop搜索结果HTML或商品详情页。这通常需要配合住宅代理来绕过Google的反爬机制并获取不同地区的本地化价格。
为什么抓取Google Shopping价格需要使用代理?
Google对搜索结果页面实施了严格的反爬措施,包括reCAPTCHA验证、sorry/index重定向和激进的每IP速率限制。单IP高频请求会在约50-80次请求内触发封锁。住宅代理通过分配真实家庭IP地址,使请求看起来来自普通用户,从而大幅提高抓取成功率。城市级地理定位还能确保获取目标市场的本地化价格和卖家列表。
哪种代理类型最适合抓取Google Shopping?
住宅代理是抓取Google Shopping的最佳选择,因为它们使用真实ISP分配的IP地址,与普通用户流量难以区分。数据中心代理容易被Google识别并封锁。移动代理也可用但成本更高。对于需要本地化价格的场景,支持城市级定位的住宅代理(如ProxyHat的country-DE-city-berlin参数)是理想方案,能确保看到目标地区购物者看到的相同价格和卖家。
如何避免抓取Google Shopping时被封锁?
避免封锁的关键策略包括:使用住宅代理并轮换IP、设置随机化延迟(3-8秒)、分批处理查询而非并发轰炸、检测软封锁信号(如空结果页或重定向到sorry/index)、设置合理的User-Agent和Accept-Language头匹配代理地区。一旦检测到软封锁应立即暂停并切换IP段。建议将并发限制在5-10个会话以内,并监控成功率下降趋势。






