Shopify 的 /products.json 接口可以说是电商数据圈里公开的“秘密武器”。只要把它追加到任意 Shopify 店铺网址后面,就能直接拿到结构化 JSON 数据——不需要 API 密钥,不需要身份验证,也不用钻进层层嵌套的 HTML 里慢慢扒。
我在 Thunderbit Official Website 团队工作,平时花很多时间思考大家是如何从网页中提取数据的。Shopify 抓取这个话题更是经常被提起:销售团队想追踪竞品价格,电商运营想对比商品目录,采购人员想寻找新供应商。Shopify 目前有 482 万活跃商家,并占据美国电商市场大约 30% 的份额,因此可抓取的商品数据规模非常可观。
这篇指南会完整讲清楚整个流程:接口会返回什么、如何分页抓取上千个商品、如何在不被封的前提下处理速率限制,以及如何用 pandas 把 Shopify 的嵌套 JSON 整理成干净的 CSV 或 Excel 文件。我还会补充其他很少有人提到的接口(/collections.json、/meta.json),并给出一个不写代码也能用的替代方案,适合那些想直接跳过 Python 的人。
Shopify 的 /products.json 接口是什么?为什么它让抓取变得这么简单?
每个 Shopify 店铺都有一个公开接口:{store-url}/products.json,它会返回结构化的商品数据。没有 API key,没有 OAuth,也不需要任何身份认证。你只要在店铺网址后面加上 /products.json,就能拿到整个商品目录的 JSON 数组。
你现在就可以自己试试:在浏览器里打开 allbirds.com/products.json 或 zoologistperfumes.com/products.json。你会看到整洁、结构清晰的 JSON,里面包含商品标题、价格、规格、图片、标签等所有信息。
再对比一下另一种做法:解析 Shopify 的 HTML 主题页面。这些页面通常嵌套很深,不同店铺结构也不一致,而且商家一旦更新主题,你的解析逻辑就可能失效。你要面对的大概会是下面这种情况:
HTML 方式(很折腾):
<div class="product-card__info">
<h3 class="product-card__title">
<a href="/products/classic-blue-jeans">Classic Blue Jeans</a>
</h3>
<span class="price price--on-sale" data-product-price>$149.00</span>
</div>
JSON 方式(清爽直接):
{
"title": "Classic Blue Jeans",
"handle": "classic-blue-jeans",
"vendor": "Hiut Denim",
"variants": [{"price": "149.00", "sku": "HD-BLU-32", "available": true}]
}
在一致性、稳定性和解析难度上,JSON 都明显更占优势。这个接口还支持两个关键参数——?limit=(每页最多 250 个商品,默认 30)和用于分页的 ?page=——后面的代码会频繁用到它们。
这里要特别区分一下:这属于公开的店铺前台接口,不是 Shopify Admin API。Admin API 需要店主访问令牌,可以返回订单数据、库存水平和客户信息。而公开的 /products.json 只能读取商品数据,任何人都能访问。后面我会详细说明这两者的区别,因为网上很多人会把它们混为一谈。
需要注意的一点:并不是所有 Shopify 店铺都会开放这个接口。根据我的测试,大约 71% 的店铺会返回有效 JSON(例如 allbirds.com、gymshark.com、colourpop.com、kyliecosmetics.com 都可以),但有些自定义配置的店铺会返回 404(比如 hiutdenim.co.uk、bombas.com)。最简单的检查方式就是:直接在浏览器里访问 {store-url}/products.json 看看结果。
为什么要用 Python 抓取 Shopify?最常见的业务场景有哪些?
为什么值得做?因为 ROI 很高。81% 的美国零售商 现在都在用自动化价格抓取做竞品分析,而 2020 年这个比例还只有 34%。研究还显示,价格策略只要提升 1%,平均利润就能增加 11.1%。这些数据是真的值钱。
下面是我最常见到的使用场景:
| 使用场景 | 受益对象 | 可获得的数据 |
|---|---|---|
| 竞品价格监控 | 电商运营团队 | 跟踪竞品目录中的价格变化、折扣和原价对比 |
| 商品研究与采购 | 采购 / 商品团队 | 对比商品特性、规格、材质和可售情况 |
| 线索挖掘 | 销售团队 | 从店铺目录中提取供应商名称、品牌信息和联系方式 |
| 市场与品类分析 | 营销团队 | 分析商品组合、标签、集合结构和定位方式 |
| 库存与可售状态追踪 | 供应链团队 | 持续监控规格层级的库存状态(available: true/false) |
| 新品识别 | 产品团队 | 跟踪 created_at 时间戳,发现竞品新品上架 |
Python 是这类工作的天然选择。69.6% 的抓取开发者 把 Python 作为主力语言,而它的生态也非常成熟——requests 负责 HTTP 请求,pandas 负责数据处理,httpx 适合异步调用——让你可以在不到 80 行代码里,从“我有一个网址”快速走到“我有一个表格”。
完整的 products.json 字段说明:每个字段都讲清楚
很多教程只会展示 title、id 和 handle,然后就草草结束。但 Shopify 的 JSON 响应里,商品、规格、图片和选项加起来其实有 40 多个字段。写抓取代码前先知道有哪些字段,能帮你避免后面返工重抓。
这份参考表来自我在 2026 年 4 月 16 日抓取的真实 /products.json 响应。只要店铺开放了这个接口,结构基本都是一致的。
商品层级字段
| 字段 | 数据类型 | 示例值 | 业务用途 |
|---|---|---|---|
id | 整数 | 123456789 | 商品唯一 ID,用于去重 |
title | 字符串 | "Classic Blue Jeans" | 商品名称,用于目录和对比 |
handle | 字符串 | "classic-blue-jeans" | URL 标识,可拼出商品页链接:{store}/products/{handle} |
body_html | 字符串(HTML)或 null | Our best-selling... | 商品描述,用于内容分析和 SEO 研究 |
vendor | 字符串 | "Hiut Denim" | 品牌/供应商名称,用于线索挖掘或采购 |
product_type | 字符串 | "Jeans" | 品类分类,用于市场分析 |
created_at | ISO 日期时间 | "2024-01-15T10:30:00-05:00" | 跟踪商品何时上架(识别新品) |
updated_at | ISO 日期时间 | "2025-03-01T08:00:00-05:00" | 发现目录最近是否有变动 |
published_at | ISO 日期时间 | "2024-01-16T00:00:00-05:00" | 判断商品何时在前台正式展示 |
tags | 字符串数组 | ["organic", "women", "straight-leg"] | 关键词 / 标签分析,用于 SEO、分类和趋势判断 |
variants | 对象数组 | (见下方规格字段) | 每个规格对应的价格、SKU 和可售状态 |
images | 对象数组 | (见下方图片字段) | 商品图片链接,用于目录和视觉分析 |
options | 对象数组 | [{"name": "Size", "values": ["S","M","L"]}] | 了解商品配置方式(尺码、颜色、材质) |
规格层级字段(嵌套在每个商品下)
| 字段 | 数据类型 | 示例 | 用途 |
|---|---|---|---|
id | 整数 | 987654321 | 规格唯一 ID |
title | 字符串 | "32 / Blue" | 规格显示名称 |
sku | 字符串 | "HD-BLU-32" | 用于库存系统匹配 SKU |
price | 字符串 | "185.00" | 价格监控(注意:这是字符串,做计算前要转成浮点数) |
compare_at_price | 字符串或 null | "200.00" | 原价——做折扣追踪时很重要 |
available | 布尔值 | true | 库存可售状态(公开接口里唯一能看到的库存指标) |
weight | 浮点数 | 1.2 | 物流 / 运费分析 |
option1, option2, option3 | 字符串 | "32", "Blue", null | 各个选项值 |
created_at, updated_at | ISO 日期时间 | — | 规格层级的变动追踪 |
图片层级字段
| 字段 | 数据类型 | 示例 | 用途 |
|---|---|---|---|
id | 整数 | 111222333 | 图片唯一 ID |
src | 字符串(URL) | "https://cdn.shopify.com/..." | 直接下载图片的链接 |
alt | 字符串或 null | "Front view of jeans" | 图片替代文本,可用于无障碍分析 |
position | 整数 | 1 | 图片排序 |
width, height | 整数 | 2048, 2048 | 图片尺寸 |
公开接口里没有什么字段?
有一个非常重要的坑:公开的 /products.json 响应里没有 inventory_quantity。 这个字段早在 2017 年 12 月就从对外公开的 JSON 接口里移除了,原因是安全考虑。你能拿到的唯一库存指示就是每个规格上的布尔字段 available(true 或 false)。如果你想获取真实库存数量,就必须使用带认证的 Admin API,并且要有店主凭证。
在开始写抓取代码前,先看这张表,确定哪些字段对你的场景最重要。如果你是做价格监控,就需要 variants[].price、variants[].compare_at_price 和 variants[].available。如果你是做线索挖掘,那就重点关注 vendor、product_type 和 tags。按需过滤,你导出的 CSV 会干净很多。
不止 products.json:Collections、Meta 和其他 Shopify 接口
很多教程都不会讲这些接口,但如果你要做更严肃的竞品分析,它们其实非常关键。
/collections.json —— 店铺全部分类
它会返回店铺里的所有集合(分类),包含标题、handle、描述和商品数量。我在 zoologistperfumes.com、allbirds.com 和 gymshark.com 上都验证过,它们都能返回有效 JSON。
{
"collections": [
{
"id": 308387348539,
"title": "Attars",
"handle": "attars",
"published_at": "2026-03-29T12:20:32-04:00",
"products_count": 1,
"image": { "src": "https://cdn.shopify.com/..." }
}
]
}
想了解竞品是怎么组织商品目录的?这个接口最直接。
/collections/{handle}/products.json —— 按分类获取商品
这个接口会返回某个具体集合下的商品,JSON 结构和 /products.json 一样,但范围只限于一个分类。它非常适合做分类级抓取——比如你只想监控竞品的“Sale”或“New Arrivals”分类。
/meta.json —— 店铺级元数据
它会返回店铺名称、描述、货币、国家,以及最有用的 published_products_count。这个数量可以让你提前算出需要多少分页:ceil(published_products_count / 250)。不用再盲目翻页,直到碰到空响应为止。
应该用哪个接口?
| 你想要什么 | 接口 | 需要认证吗? |
|---|---|---|
| 所有商品(公开) | /products.json | 否 |
| 某个分类下的商品 | /collections/{handle}/products.json | 否 |
| 店铺元数据 + 商品数量 | /meta.json | 否 |
| 所有分类(集合) | /collections.json | 否 |
| 订单 / 销售数据(仅限自有店铺) | Admin API /orders.json | 是(API key) |
| 库存数量(仅限自有店铺) | Admin API /inventory_levels.json | 是 |
论坛里反复出现的问题——“我能抓取竞品卖了多少件吗?”——答案很短:不能。至少不能从公开接口拿到。销售数据和库存数量都需要认证后的 Admin API,也就是你必须拥有店主访问权限。公开接口只提供商品目录数据。

如何用 Python 抓取 Shopify:一步一步搭建
- 难度:初级
- 预计时间:约 15 分钟(环境安装 + 完成第一次抓取)
- 你需要准备:Python 3.11+、
pip、终端,以及一个要抓取的 Shopify 店铺网址
第 1 步:安装 Python 和所需库
先确认你已经安装了 Python 3.11 或更高版本(pandas 3.0.x 需要它)。然后安装我们要用的两个库:
pip install requests pandas
如果还要导出 Excel,再装一个:
pip install openpyxl
在脚本开头添加这些导入:
import requests
import pandas as pd
import time
import random
import json
运行脚本时不应该出现任何导入错误。如果 pandas 报版本问题,升级到 Python 3.12。
第 2 步:从 /products.json 获取商品数据
下面是一个基础函数:传入店铺网址,调用接口,并返回解析后的 JSON 数据。
def fetch_products_page(store_url, page=1, limit=250):
"""从 Shopify 店铺抓取某一页商品数据。"""
url = f"{store_url.rstrip('/')}/products.json"
params = {"limit": limit, "page": page}
headers = {
"User-Agent": "Mozilla/5.0 (compatible; ProductResearch/1.0)"
}
response = requests.get(url, params=params, headers=headers, timeout=30)
response.raise_for_status()
return response.json().get("products", [])
几个关键点:
limit=250是 Shopify 允许的每页最大值。默认是 30,所以显式设置后,所需请求次数最多能减少 8 倍。User-Agent头:一定要设置成看起来真实的值。没有 User-Agent 的请求更容易触发 Shopify 的反爬系统。timeout=30:别让单个请求无限卡住。
用一个已知店铺测试一下:
products = fetch_products_page("https://allbirds.com")
print(f"Fetched {len(products)} products")
print(f"First product: {products[0]['title']}")
你应该会看到类似这样的结果:Fetched 250 products,以及第一个商品标题。
第 3 步:处理分页,抓取全部商品
一次请求最多只能拿到 250 个商品。大多数店铺都不止这个数量(Allbirds 就有 1,420+)。所以你需要循环翻页,直到返回空结果为止。
def scrape_all_products(store_url, delay=1.0):
"""抓取 Shopify 店铺的全部商品,自动处理分页。"""
all_products = []
page = 1
while True:
print(f"Fetching page {page}...")
products = fetch_products_page(store_url, page=page, limit=250)
if not products:
print(f"No more products. Total: {len(all_products)}")
break
all_products.extend(products)
print(f" Got {len(products)} products (total so far: {len(all_products)})")
page += 1
# 温和一点:每次请求之间稍微停一下
time.sleep(delay + random.uniform(0, 0.5))
return all_products
当 products 变成空列表时,就说明已经到最后一页了。
time.sleep() 再加一点随机抖动,可以帮助你把请求频率控制在 Shopify 非正式的速率限制范围内(大约每秒 2 次请求)。
进阶提示:如果你先调用 /meta.json,就已经知道总商品数了,可以直接算出需要多少页:pages = ceil(product_count / 250)。这样就不用在最后多发一次“空请求”了。
第 4 步:解析并筛选你需要的字段
拿到所有商品的 Python 字典列表后,就可以提取你真正关心的字段了。下面这个示例会提取做价格监控最常用的一组字段:
def extract_product_data(products):
"""提取商品关键字段,并把 variants 展平。"""
rows = []
for product in products:
for variant in product.get("variants", []):
rows.append({
"product_id": product["id"],
"title": product["title"],
"handle": product["handle"],
"vendor": product.get("vendor", ""),
"product_type": product.get("product_type", ""),
"tags": ", ".join(product.get("tags", [])),
"created_at": product.get("created_at", ""),
"variant_id": variant["id"],
"variant_title": variant.get("title", ""),
"sku": variant.get("sku", ""),
"price": variant.get("price", ""),
"compare_at_price": variant.get("compare_at_price", ""),
"available": variant.get("available", ""),
"image_url": product["images"][0]["src"] if product.get("images") else ""
})
return rows
这会生成“一行对应一个规格”的数据结构,这种格式最适合做价格比较,因为像 “Classic Blue Jeans” 这种商品,可能会有 12 个规格(6 个尺码 × 2 种颜色),每个规格都有自己的价格和可售状态。
使用 pandas 把抓取到的 Shopify 数据导出为 CSV 和 Excel
其他很多 Shopify 抓取教程都会把原始 JSON 直接丢进文件里,然后就算完事了。对开发者来说还行,但对周五就要交表的电商分析师来说,这几乎没用。
问题在于:Shopify 的 JSON 是嵌套结构。一个商品里可能有十几个规格,每个规格都有自己的价格、SKU 和可售状态。要把它整理成标准的行列数据,就需要一些 pandas 处理。
把嵌套 JSON 展平成干净的表格
根据你的需求,通常有两种做法:
方案 A:每个规格一行(最适合价格监控和库存追踪)
# 使用第 4 步中的 extract_product_data 函数
products = scrape_all_products("https://allbirds.com")
rows = extract_product_data(products)
df = pd.DataFrame(rows)
print(f"DataFrame shape: {df.shape}")
print(df.head())
这样你会得到一个平坦的表格,每一行代表一个唯一的商品-规格组合。一个有 500 个商品、每个商品平均 4 个规格的店铺,最后大概会得到 2,000 行左右的 DataFrame。
方案 B:每个商品一行摘要(最适合做目录总览)
def summarize_products(products):
"""每个商品一行,统计各规格的最低 / 最高价。"""
rows = []
for product in products:
prices = [float(v["price"]) for v in product.get("variants", []) if v.get("price")]
rows.append({
"product_id": product["id"],
"title": product["title"],
"vendor": product.get("vendor", ""),
"product_type": product.get("product_type", ""),
"variant_count": len(product.get("variants", [])),
"min_price": min(prices) if prices else None,
"max_price": max(prices) if prices else None,
"any_available": any(v.get("available", False) for v in product.get("variants", [])),
"tags": ", ".join(product.get("tags", []))
})
return rows
导出为 CSV、Excel 和 Google Sheets
# 导出 CSV(使用 utf-8-sig,方便 Excel 识别特殊字符)
df.to_csv("shopify_products.csv", index=False, encoding="utf-8-sig")
# 导出 Excel(需要 openpyxl)
df.to_excel("shopify_products.xlsx", index=False, engine="openpyxl")
print("Exported to shopify_products.csv and shopify_products.xlsx")
如果是 Google Sheets,可以用 gspread 加服务账号来写入。不过说实话,对大多数场景来说,先导出 CSV 再上传到 Google Drive,通常更快也更简单。
面向生产环境的 Python 抓取:速率限制、重试和防封策略
基础脚本抓小店铺问题不大。但如果你要抓 5,000+ 商品的店铺,或者连续抓多个店铺,就会开始出问题。
了解 Shopify 的速率限制和封禁机制
Shopify 的公开 JSON 接口没有像 Admin API 那样明确公开速率限制文档,但根据实际测试,可以观察到:
- 安全速度:每个店铺大约每秒 2 个请求
- 软上限:每分钟大约 40 个请求后,可能开始触发限流
- HTTP 429:"Too Many Requests",标准的限流响应
- HTTP 430:Shopify 特有的代码,表示安全级别封禁(不只是限流)
- HTTP 403 或 CAPTCHA 跳转:部分店铺叠加了 Cloudflare 防护
来自共享云环境的请求(例如 AWS Lambda、Google Cloud Run)尤其容易触发封禁,因为这些 IP 段的滥用率通常更高。
更可靠地抓取 Shopify 的方法
下面是从“我电脑上能跑”进化到“可以上生产”的路线:
| 级别 | 技术 | 稳定性 |
|---|---|---|
| 基础 | requests.get() + ?page= | 大目录容易失效,也可能被封 |
| 中级 | requests.Session() + ?limit=250 + time.sleep(1) + 429 重试 | 适合大多数店铺 |
| 高级 | 异步 httpx + 轮换 User-Agent + 指数退避 | 可用于生产,支持 1 万+ 商品规模 |
中级方案(适合大多数用户,推荐):
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def create_session():
"""创建带自动重试逻辑的 requests 会话。"""
session = requests.Session()
retries = Retry(
total=5,
backoff_factor=1, # 休眠:0.5s、1s、2s、4s、8s
status_forcelist=[429, 430, 500, 502, 503, 504],
respect_retry_after_header=True
)
session.mount("https://", HTTPAdapter(max_retries=retries))
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
})
return session
这里的 Retry 配置会在遇到 429 时自动进行指数退避重试。backoff_factor=1 的意思是:每次重试之间的等待时间按 0.5 秒 → 1 秒 → 2 秒 → 4 秒 → 8 秒递增。复用 requests.Session() 还能启用连接池,在同一个域名上连续请求时更省开销。
User-Agent 轮换:如果你要抓多个店铺,建议在 3–5 个真实浏览器的 User-Agent 之间轮换。这不是为了“伪装”,而是为了避免每次请求都带着一模一样的请求头,看起来像机器人。
完整可运行的 Python 脚本:抓取 Shopify 并导出 CSV
下面是一份可以直接复制运行的完整脚本,把上面的所有内容都整合在了一起。实际有效代码大约 75 行左右(加上注释),我已经用它测试过 Allbirds(1,420 个商品)、ColourPop(2,000+ 个商品)和 Zoologist Perfumes(小型目录)。
import requests
import pandas as pd
import time
import random
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def create_session():
"""创建一个带速率限制重试逻辑的会话。"""
session = requests.Session()
retries = Retry(
total=5,
backoff_factor=1,
status_forcelist=[429, 430, 500, 502, 503, 504],
respect_retry_after_header=True
)
session.mount("https://", HTTPAdapter(max_retries=retries))
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/125.0.0.0 Safari/537.36"
})
return session
def scrape_shopify(store_url, delay=1.0):
"""通过 /products.json 抓取 Shopify 店铺的全部商品。"""
session = create_session()
all_products = []
page = 1
base_url = f"{store_url.rstrip('/')}/products.json"
while True:
print(f" Page {page}...", end=" ")
resp = session.get(base_url, params={"limit": 250, "page": page}, timeout=30)
resp.raise_for_status()
products = resp.json().get("products", [])
if not products:
break
all_products.extend(products)
print(f"{len(products)} products (total: {len(all_products)})")
page += 1
time.sleep(delay + random.uniform(0, 0.5))
return all_products
def flatten_to_variants(products):
"""把嵌套的商品 JSON 展平成每个规格一行。"""
rows = []
for p in products:
base = {
"product_id": p["id"],
"title": p["title"],
"handle": p["handle"],
"vendor": p.get("vendor", ""),
"product_type": p.get("product_type", ""),
"tags": ", ".join(p.get("tags", [])),
"created_at": p.get("created_at", ""),
"updated_at": p.get("updated_at", ""),
"image_url": p["images"][0]["src"] if p.get("images") else "",
}
for v in p.get("variants", []):
row = {**base}
row["variant_id"] = v["id"]
row["variant_title"] = v.get("title", "")
row["sku"] = v.get("sku", "")
row["price"] = v.get("price", "")
row["compare_at_price"] = v.get("compare_at_price", "")
row["available"] = v.get("available", "")
rows.append(row)
return rows
if __name__ == "__main__":
STORE_URL = "https://allbirds.com" # 改成你要抓取的店铺
OUTPUT_CSV = "shopify_products.csv"
OUTPUT_EXCEL = "shopify_products.xlsx"
print(f"Scraping {STORE_URL}...")
products = scrape_shopify(STORE_URL)
print(f"\nTotal products scraped: {len(products)}")
print("Flattening to variant-level rows...")
rows = flatten_to_variants(products)
df = pd.DataFrame(rows)
print(f"DataFrame: {df.shape[0]} rows x {df.shape[1]} columns")
df.to_csv(OUTPUT_CSV, index=False, encoding="utf-8-sig")
df.to_excel(OUTPUT_EXCEL, index=False, engine="openpyxl")
print(f"\nExported to {OUTPUT_CSV} and {OUTPUT_EXCEL}")
运行方式:python scrape_shopify.py。以 Allbirds 为例,整个过程大约需要 45 秒,并会生成一个包含 5,000+ 行(每个规格一行)的 CSV。终端输出大概会像这样:
Scraping https://allbirds.com...
Page 1... 250 products (total: 250)
Page 2... 250 products (total: 500)
...
Page 6... 170 products (total: 1420)
Total products scraped: 1420
Flattening to variant-level rows...
DataFrame: 5680 rows x 14 columns
Exported to shopify_products.csv and shopify_products.xlsx
不写 Python:用 Thunderbit 两步抓取 Shopify(无代码方案)
不是每个人都想安装 Python、调试导入错误,或者长期维护一份抓取脚本。对于那种明天早上就要竞品价格的人来说,Python 的成本其实有点高。
所以我们做了 Thunderbit Official Website——一个作为 Chrome 扩展运行的 AI 网页爬虫。无需代码,无需 API key,也不用配置运行环境。
Thunderbit 是怎么抓取 Shopify 店铺的?
Thunderbit 内置了一个专门的 Shopify Scraper 模板,已经为 Shopify 商品页预先做好了配置。你只需要安装 Thunderbit Chrome Extension Download Page,进入一个 Shopify 店铺,然后点击“Scrape”。模板会自动提取商品名称、描述、价格、规格详情、图片和供应商信息。
如果模板和页面结构不是完全匹配(比如自定义主题、特别的布局),Thunderbit 的 AI Suggest Fields 功能会读取页面并自动生成列名。你还可以继续自定义:重命名列、添加字段,或者直接下达指令,比如“只提取 compare_at_price 有值的商品”。
下面这些功能和 Python 脚本能做到的事情几乎一一对应:
- 子页面抓取:自动访问每个商品详情页,并补充完整描述、评论或规格详情——这和 Python 通过循环翻页实现的效果类似,但不需要写代码。
- 自动分页:自动处理点击翻页和无限滚动,无需额外配置。
- 定时抓取:可以设置周期任务(例如“每周一上午 9 点”)持续监控价格变化,不需要 cron job 或服务器。
- 免费导出:所有方案都可导出到 CSV、Excel、Google Sheets、Airtable 或 Notion。
Python 脚本 vs. Thunderbit:诚实对比
| 维度 | Python 脚本 | Thunderbit(无代码) |
|---|---|---|
| 上手时间 | 15–60 分钟(环境 + 代码) | 约 2 分钟(安装 Chrome 扩展) |
| 是否需要编程 | 需要(Python) | 不需要 |
| 可定制性 | 几乎无限 | AI 推荐字段 + 自定义提示词 |
| 分页处理 | 需要手写 | 自动完成 |
| 导出格式 | 需自行实现(CSV/Excel) | CSV、Excel、Google Sheets、Airtable、Notion(免费) |
| 定时执行 | 需要 cron + 托管环境 | 内置调度器 |
| 速率限制处理 | 需要自己写重试/退避 | 自动处理 |
| 最适合谁 | 开发者、大规模数据管道 | 商业用户、快速提取、周期性监控 |
如果你需要完全控制流程,或者要把数据接入更大的数据管道,那就用 Python。如果你要的是“快、方便、别维护代码”,那就用 Thunderbit。想更深入了解 价格比较场景下的网页抓取,我们也写过专门的文章。

抓取 Shopify 店铺的技巧与最佳实践
无论你最终用什么工具,这些建议都适用:
- 始终使用
?limit=250,把请求次数降到最低。默认每页 30 个商品,意味着同样的数据要多发 8 倍请求。 - 尊重目标站点:每次请求之间留 1–2 秒。对服务器高频连发请求不仅不礼貌,也更容易被封。
- 先检查
robots.txt:Shopify 默认的robots.txt并不会屏蔽/products.json,但有些店铺会加入自定义规则,批量抓取前最好先确认。 - 先把原始 JSON 存到本地,再做后处理。这样以后如果解析逻辑变了,不用重新抓取。像
json.dump(all_products, open("raw_data.json", "w"))这种简单操作,后面能省很多麻烦。 - 按
product.id去重:分页边界有时会出现重复商品。用df.drop_duplicates(subset=["product_id", "variant_id"])可以快速清理。 - 计算前先把
price转成 float。Shopify 返回的是字符串("185.00"),不是数字。 - 关注接口变化:虽然
/products.json多年来一直很稳定,但理论上 Shopify 仍可能限制它。如果你的抓取器突然返回 404,先手动打开店铺看看。
如果你想进一步学习如何构建更稳健的抓取器,可以看看我们关于 网页抓取工具最佳实践 的指南。
抓取 Shopify 时的法律与伦理注意事项
这一段不长,但很重要。
/products.json 接口提供的是公开商品数据——也就是任何访客在浏览商店时都能看到的信息。Shopify 的服务条款里确实有关于不能用“自动化方式”访问“服务”的表述,但这里指的是平台本身(如后台、结账流程),不是公开前台商品数据。到 2026 年 4 月为止,还没有针对 Shopify 公开数据抓取的相关诉讼案例。
已有的法律先例也支持对公开数据进行抓取:hiQ v. LinkedIn 案确认了抓取公开可访问数据并不违反 CFAA,而 Meta v. Bright Data(2024)则裁定,只有在用户登录后,服务条款里的限制才会生效。
最佳实践:
- 只抓取公开可访问的商品数据
- 不要抓取个人或客户数据
- 遵守
robots.txt和速率限制 - 如果涉及任何个人数据,需符合 GDPR/CCPA(商品目录数据不属于个人数据)
- 使用清晰的 User-Agent 标识自己
- 抓取你自己店铺的数据时,使用 Admin API 永远是没问题的
想更深入了解的话,可以看我们关于 网页抓取法律影响 的文章。
结论与核心要点
Shopify 公开的 /products.json 接口,让电商数据提取变得尽可能简单。整个流程就是:追加 /products.json → 用 Python 抓取 → 用 ?limit=250&page= 分页 → 用 pandas 展平 → 导出成 CSV 或 Excel。
这篇指南相比其他教程,补充了这些内容:
- 完整字段参考:在写第一行代码之前,就能知道具体能拿到哪些数据(商品、规格、图片共 40+ 个字段)
- 额外接口:
/collections.json和/meta.json能提供分类级情报和店铺元数据,而这些通常没人讲 - 可用于生产环境的技巧:会话复用、指数退避、User-Agent 头和
?limit=250,应对真实世界的速率限制 - 正确的 CSV/Excel 导出:使用 pandas 将规格层级数据展平,而不是只保存原始 JSON
- 无代码替代方案:适合想要速度而不是代码灵活性的用户,可直接用 Thunderbit
如果你想在不写代码的情况下做一次性或周期性的 Shopify 数据提取,可以试试 Thunderbit Chrome Extension Download Page——Shopify Scraper 模板会自动处理从分页到导出的所有事情。如果你需要自定义数据管道,或者要跨多个店铺大规模抓取,那么这篇指南里的 Python 脚本会给你完整控制权。
也欢迎关注我们的 Thunderbit YouTube Channel,观看视频教程;或者浏览我们关于 抓取亚马逊商品 和 把网站数据提取到 Excel 的相关指南,了解更多类似技巧。
使用 Thunderbit 抓取 Shopify Get Started Free
常见问题
所有 Shopify 店铺都能通过 products.json 抓取吗?
大多数 Shopify 店铺默认都会开放这个接口——在测试中,大约 71% 返回了有效 JSON。有些使用自定义配置或额外安全层(例如 Cloudflare、无头架构)的店铺,可能会返回 404 或直接拦截请求。最简单的检查方式是:在浏览器里打开 {store-url}/products.json。如果你看到了 JSON,就说明可以用。
抓取 Shopify 店铺合法吗?
公开商品数据(价格、标题、图片、描述)通常是可访问的,而且像 hiQ v. LinkedIn 这样的法律先例也支持抓取公开信息。不过,还是应该查看具体店铺的服务条款以及你所在地区的法律。不要抓取个人或客户数据,并且要遵守速率限制。
一个 Shopify 店铺最多能抓取多少商品?
总量没有硬性上限。通过 ?limit=250&page= 分页,你可以把整个目录都抓下来。对于非常大的店铺(25,000+ 商品),建议配合会话复用和延时,避免触发速率限制。/meta.json 接口还可以提前告诉你准确的商品总数,这样你就能知道大概需要多少页。
products.json 和 Shopify Admin API 有什么区别?
/products.json 是公开接口——不需要认证,任何人都能访问,只能读取商品数据。Admin API 则需要店主访问令牌,可以读取订单、库存数量、客户数据,也能写入数据。如果你需要销售数据或真实库存数量,就必须有 Admin API 访问权限(也就是你必须是店主,或者得到店主授权)。
不用 Python 也能抓取 Shopify 吗?
当然可以。Thunderbit Official Website 这类工具可以通过 Chrome 扩展直接抓取 Shopify 店铺,无需编程。它会自动处理分页,并直接导出到 CSV、Excel、Google Sheets、Airtable 或 Notion。对于喜欢其他语言的开发者来说,同样的 /products.json 接口也可以用 JavaScript、Ruby、Go 等任何能发 HTTP 请求并解析 JSON 的语言来访问。
了解更多


