当购物者在 Google 上搜索某个产品时,购物结果是对该市场的一次实时快照:哪些零售商在销售它、价格是多少、运费如何,以及从该购物者所在位置看到的情况。对电商团队而言,这份快照能回答通常需要几十次独立抓取才能得到的问题:谁在压低我们的价格、针对哪些产品、在哪些市场、压低了多少。
Google Shopping API 将这份快照转化为可按计划查询的结构化数据。本指南将介绍团队用它来做什么、如何采集才能让数据可信,以及一小段将搜索结果转化为定价视图的代码。这是更广泛的 price intelligence 实践的一部分,而 Shopping 数据是其中最高效的输入之一。
为什么 Shopping 数据不同于抓取零售商网站
监控竞争对手价格的传统方法是抓取每个零售商的产品页面。这种方法可行,但扩展性很差:每个零售商都需要自己的采集器、自己的解析器和自己的产品匹配逻辑。Shopping 结果则颠覆了这种模式:
- 一次查询覆盖多个卖家。 单次搜索会返回 Google 为该产品列出的每个零售商的报价。
- 产品已经分好组。 同一商品的报价会聚合在一起,这大大减少了跨店铺匹配产品的麻烦。
- 它展示的是购物者视角。 结果反映了搜索所在市场和语言,包括出现哪些卖家及其排序,而这正是真正驱动点击的因素。
但代价是覆盖范围有限。Shopping 结果只显示在那里挂牌的零售商,而非市场上的每一个卖家,而且显示的是挂牌价格,可能与使用优惠券后或结账时的实际价格不同。若要做全目录追踪,仍需要零售商抓取;但若要对你的核心产品做全市场视角的把握,Shopping 数据通常更快、更省成本。我们关于构建实时竞争性价格信息流的指南介绍了如何将两者结合。
团队如何使用这些数据
| 问题 | 数据展示的内容 |
|---|---|
| 我们的定价有竞争力吗? | 每个产品在价格上的排名,以及与最低价卖家的差距 |
| 谁在压低我们的价格? | 哪些卖家的报价低于我们、频率如何、涉及哪些产品 |
| 这个产品的竞争有多激烈? | 卖家数量以及价格分布的离散程度 |
| 竞争对手是否在搞促销? | 某卖家旗下产品价格的突然下跌 |
| 各市场有何差异? | 同一产品在各国的卖家和价格 |
| 分销商是否违反了价格政策? | 授权卖家的挂牌价是否低于最低广告价 |
最后一项用途与品牌保护的关系不亚于定价本身:制造商用 Shopping 数据来发现未经授权的卖家和最低价违规行为,而无需逐一抓取每个分销商。
正确地采集数据
Shopping 结果取决于查询者的身份,因此一致的采集方式比采集量更重要。
- 固定位置和语言。 结果会因国家而异,往往也因城市而异。每次运行都使用相同的位置和语言设置,否则不同运行之间的比较会混淆不同市场的数据。
- 使用稳定的查询语句。 每次都用相同的产品词搜索,最好是产品名称加型号,这样才能返回相同的列表。
- 按与市场相匹配的频率采集。 关于在线价格的研究表明,每周采集可以捕捉到大型零售商大部分的常规价格变动,而变化快的品类和促销活动则需要每日检查。我们关于在线价格变动速度的研究涵盖了相关证据。
- 保留原始结果。 存储每次运行的完整响应及其时间和设置,以便日后可以复查某个发现。
- 比较前先对价格做归一化处理。 货币、小数格式和是否含税因市场而异。我们关于跨地区归一化价格、数字和日期的指南介绍了相关解析方法。
从自己的服务器大规模运行 Google 搜索很快就会遇到封锁和结果不一致的问题,这也是大多数团队使用专为此设计的 API 的原因。Shifter 的 SERP API 会以结构化 JSON 的形式返回 Google Shopping 结果,使用你设置的位置和语言,让工作从分析阶段开始,而不是从采集阶段开始。
从结果到定价视图
一旦每条列表的产品、卖家和价格被映射为一个简单的记录,几行代码就能生成定价团队真正需要的视图。下面的模块为一次搜索构建请求参数,并将某次运行的报价转化为按产品汇总的摘要:最低价卖家、价格中位数、价差的宽度,以及你的店铺的排名。
from collections import defaultdict
from statistics import median
def shopping_params(query, location, country, language="en", api_key="YOUR_API_KEY"):
"""One Google Shopping search, as seen by a shopper in `location`."""
return {"engine": "google_shopping", "api_key": api_key, "q": query,
"location": location, "gl": country, "hl": language}
def market_view(offers, my_seller):
"""offers: [{"product": key, "seller": name, "price": float}] from one search run.
For each product: the cheapest seller, the median price, how wide the spread
is, and where `my_seller` ranks on price.
"""
by_product = defaultdict(list)
for o in offers:
by_product[o["product"]].append(o)
report = {}
for product, rows in by_product.items():
rows.sort(key=lambda o: o["price"])
prices = [o["price"] for o in rows]
mine = next((i for i, o in enumerate(rows) if o["seller"] == my_seller), None)
report[product] = {
"sellers": len(rows),
"cheapest": f"{rows[0]['seller']} {prices[0]:.2f}",
"median": round(median(prices), 2),
"spread_pct": round(100 * (prices[-1] - prices[0]) / prices[0], 1),
"my_rank": None if mine is None else mine + 1,
"gap_to_cheapest_pct": None if mine is None
else round(100 * (rows[mine]["price"] - prices[0]) / prices[0], 1),
}
return report
对一小批示例报价运行该函数:
from shopping import market_view
offers = [ # illustrative results from one search run, mapped into the offer shape
{"product": "air fryer 6qt", "seller": "Store A", "price": 89.99},
{"product": "air fryer 6qt", "seller": "Our Store", "price": 99.00},
{"product": "air fryer 6qt", "seller": "Store C", "price": 119.95},
{"product": "blender 1.5l", "seller": "Our Store", "price": 54.00},
{"product": "blender 1.5l", "seller": "Store B", "price": 59.99},
]
for product, view in market_view(offers, my_seller="Our Store").items():
print(product, view)
air fryer 6qt {'sellers': 3, 'cheapest': 'Store A 89.99', 'median': 99.0, 'spread_pct': 33.3, 'my_rank': 2, 'gap_to_cheapest_pct': 10.0}
blender 1.5l {'sellers': 2, 'cheapest': 'Our Store 54.00', 'median': 57.0, 'spread_pct': 11.1, 'my_rank': 1, 'gap_to_cheapest_pct': 0.0}
空气炸锅是一个竞争激烈、价差较大的产品,Our Store 排名第二,比最低价卖家高出 10%。搅拌机则是一个竞争不激烈的产品,Our Store 已经是最低价。每天运行同样的报告,那些出现的变化,比如新出现一个价格更低的卖家、价差突然扩大,就是值得采取行动的信号。
将信号转化为决策
- 在你有能见度的地方调整价格。 排名第一或第二的产品已经在价格上占优;可能还有恢复利润空间的余地。排名靠后的产品,价格调整带来的流量变化最大。
- 关注价差,而不仅仅是最低价。 价差较大通常意味着这个市场有定价空间;价差较窄则意味着这是一个差异微小也很重要的大宗商品市场。
- 区分促销和真实调价。 竞争对手一周后就恢复的降价是促销;持续不变的则是新价格。我们关于追踪竞争对手促销和折扣周期的指南展示了如何区分两者。
- 扩张前先比较市场。 新国家市场中卖家数量和价格水平,能在你入驻前显示该市场的竞争激烈程度。
需要牢记的局限
- 并非每个卖家都在 Google Shopping 上挂牌。 数据展示的是可见市场,而非整个市场。
- 挂牌价格不一定是最终价格。 运费、税费、优惠券和会员价都可能改变购物者实际支付的金额。
- 相同的搜索在不同次运行中结果会有差异。 应依据多次运行中持续存在的模式采取行动,而非单次运行的排序。
- 个性化影响有限但确实存在。 使用中立设置和固定位置,以保持各次运行之间的可比性。
常见问题
什么是 Google Shopping API?
一种为你运行 Google Shopping 搜索并以结构化数据返回结果的服务:产品、提供这些产品的卖家及其价格,均基于你指定的位置和语言。
电商团队能用 Google Shopping 数据做什么?
可以同时追踪多个卖家的竞争对手价格,查看自己的价格排名,发现促销活动和新卖家,在扩张前比较各市场,并根据最低广告价检查分销商是否合规。
我应该多久采集一次 Google Shopping 结果?
对许多品类而言,每周一次就足够;对变化快的品类、促销期间以及旺季则需要每日采集。保持位置、语言和查询语句固定,以便各次运行之间可以比较。
Google Shopping 会显示每个卖家的价格吗?
不会。它只显示在那里挂牌产品的零售商,以及挂牌价格,而这可能与运费、优惠券之后或结账时的实际价格不同。在需要完整覆盖时,应将其与直接的零售商抓取结合使用。
总结
Google Shopping 结果是了解某个产品市场最高效的视角之一:众多卖家的信息已经分好组,呈现出本地购物者所看到的样子。采集时固定位置、语言和查询语句,保留原始结果,对价格做归一化处理,并将每次运行汇总为排名、价差和与最低价的差距。长期跟踪这几个数字,就能告诉电商团队该在哪里调整价格、该在哪里保持不变。如需了解更广泛的电商数据需求,请参阅我们的 电商解决方案。