为什么 POD 团队需要抓取 Etsy 数据
如果你在做按需印刷(Print-on-Demand)生意,Etsy 是最值得研究的平台之一。它拥有超过 700 万活跃卖家、近 1 亿件商品,覆盖从定制 T 恤到数字下载的每一个细分市场。问题是:Etsy 没有公开的数据导出 API,搜索结果页只展示 250 页,而且 Cloudflare 和内部限速让批量抓取变得棘手。
本文从 API vs HTML 的取舍开始,逐步拆解 Etsy 的页面结构、反爬机制、细分市场研究模式,并给出完整的 Python 代码。我们的原则很明确:抓取是为了研究市场,不是抄袭设计。
Etsy 的页面结构与数据入口
Etsy 没有官方公开 API(Etsy Open API 已关闭新申请),所以所有数据都得从 HTML 页面提取。理解页面结构是高效抓取的前提。
搜索结果页
搜索 URL 格式:
https://www.etsy.com/search?q=custom+dog+mug&ref=search_bar关键参数:
- q — 搜索关键词
- page — 页码(最多 250 页)
- order — 排序方式:
date_desc(最新)、price_asc(价格升序)等 - min_price / max_price — 价格区间过滤
- ship_to — 目的地国家
每个搜索结果页包含约 48 张 listing 卡片。卡片 HTML 中嵌入了 JSON-LD 结构化数据(script[type="application/ld+json"]),这是最可靠的数据来源。
商品详情页(Listing Page)
URL 格式:
https://www.etsy.com/listing/123456789/custom-dog-mug详情页的关键数据点:
- 标题与描述 — 直接在 DOM 中
- 价格 —
[data-selector="price-only"]或 CSS 类.wt-text-title-01 - 销量徽章 — "x sales" 文本,在
.wt-text-caption附近 - 评价数与星级 —
[aria-label*="out of 5 stars"] - 变体选项 — 下拉菜单中的尺寸、颜色等
- 卖家信息 — 店铺名称和链接在
[data-shop-id]属性附近
详情页同样包含 JSON-LD,里面有 offers、aggregateRating 等结构化字段。
店铺页面(Shop Page)
URL 格式:
https://www.etsy.com/shop/ShopName店铺页提供:
- 总销量 — "XX,XXX Sales" 徽章
- 在售商品数 — 列表计数
- 评价概览 — 星级分布
- 创建时间 — "On Etsy since 2019"
分类树
Etsy 的分类层级大致为:顶级分类(如 Jewelry & Accessories)→ 子分类(如 Necklaces)→ 更细分的标签。你可以从 https://www.etsy.com/c/jewelry 这样的分类页面出发,提取面包屑导航和侧边栏筛选器来构建分类树。
Etsy 的反爬机制与代理策略
Etsy 使用多层防护:
- Cloudflare — 默认开启 Bot Management,挑战请求头指纹异常的客户端
- 内部限速 — 同一 IP 短时间内请求超过约 30-50 次会触发 429 或验证码
- JavaScript 渲染 — 部分内容(如评价、价格)需要 JS 执行才能显示
- 行为检测 — 请求模式过于规律会被标记
这就是为什么住宅代理是抓取 Etsy 的首选。数据中心 IP 容易被 Cloudflare 识别并拦截,而住宅 IP 看起来像真实用户。
| 代理类型 | Etsy 兼容性 | 速度 | 成本 | 适用场景 |
|---|---|---|---|---|
| 数据中心代理 | ❌ 极易被封 | 极快 | 低 | 不推荐用于 Etsy |
| 住宅代理 | ✅ 高通过率 | 中等 | 中高 | 搜索页、详情页抓取 |
| 移动代理 | ✅ 最高通过率 | 较慢 | 高 | 高频请求、绕过严格检测 |
对于大多数 POD 研究场景,住宅代理 + 每次请求换 IP(rotating session)就足够了。如果需要维持登录态或购物车,用 sticky session。
💡 ProxyHat 的住宅代理支持按国家和城市定位,这对研究不同市场的 Etsy 定价非常有用。例如,用美国 IP 查看 Etsy 美国站的价格和运费。
细分市场研究:抓取模式与指标
POD 卖家最关心的三个指标:
1. 热门搜索词(Trending Keywords)
Etsy 搜索框的自动补全接口是一个金矿:
https://www.etsy.com/api/v3/ajax/member/search-suggestions?query=custom+dog这个接口返回与关键词相关的热门搜索建议。你可以用字母 a-z 逐个前缀请求,挖掘长尾关键词。
2. 每个细分市场的卖家数量
搜索某个关键词后,Etsy 会显示结果总数(如 "Over 100,000 results")。结合筛选后的结果数,你可以估算市场饱和度:
- 结果数多 + 卖家分散 → 竞争激烈但需求大
- 结果数少 + 卖家集中 → 可能是蓝海,但需求有限
3. 平均价格点
从搜索结果页的 JSON-LD 中批量提取价格,计算分布。价格区间告诉你:
- 低价区间($5-15)— 通常是数字下载或简单定制
- 中价区间($15-35)— 标准 POD 产品的主战场
- 高价区间($35+)— 手工定制或高端定位
Python 实战:搜索 → 列表解析 → 详情页抓取
下面是完整的 Python 脚本,使用 ProxyHat 住宅代理抓取 Etsy 搜索结果,然后逐个访问详情页。
import requests
from bs4 import BeautifulSoup
import json
import time
import random
# ProxyHat 住宅代理配置
PROXY = "http://user-country-US:PASSWORD@gate.proxyhat.com:8080"
PROXIES = {"http": PROXY, "https": PROXY}
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/124.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9",
"Accept-Language": "en-US,en;q=0.9",
}
def fetch_search(keyword, page=1):
"""抓取 Etsy 搜索结果页"""
url = "https://www.etsy.com/search"
params = {"q": keyword, "page": page}
resp = requests.get(url, params=params, headers=HEADERS,
proxies=PROXIES, timeout=30)
resp.raise_for_status()
return resp.text
def parse_search_listings(html):
"""从搜索结果页提取 listing 卡片数据"""
soup = BeautifulSoup(html, "html.parser")
listings = []
# 提取 JSON-LD 结构化数据
for script in soup.find_all("script", type="application/ld+json"):
try:
data = json.loads(script.string)
if isinstance(data, dict) and data.get("@type") == "ItemList":
for item in data.get("itemListElement", []):
prod = item.get("item", {})
listings.append({
"title": prod.get("name", ""),
"url": prod.get("url", ""),
"price": float(
prod.get("offers", {})
.get("lowPrice", 0)
),
"rating": float(
prod.get("aggregateRating", {})
.get("ratingValue", 0)
),
"review_count": int(
prod.get("aggregateRating", {})
.get("reviewCount", 0)
),
})
except (json.JSONDecodeError, TypeError):
continue
return listings
def fetch_listing_detail(listing_url):
"""抓取单个 listing 详情页"""
resp = requests.get(listing_url, headers=HEADERS,
proxies=PROXIES, timeout=30)
resp.raise_for_status()
return resp.text
def parse_listing_detail(html):
"""解析 listing 详情页,提取销量、评价等"""
soup = BeautifulSoup(html, "html.parser")
result = {}
# 销量徽章 — 查找包含 "sales" 的文本
sales_span = soup.find(string=re.compile(r"\d+\s+sales?"))
if sales_span:
result["sales"] = int(
re.search(r"(\d+)", sales_span).group(1)
)
# 评价数
review_el = soup.select_one("[aria-label*='out of 5 stars']")
if review_el:
result["stars"] = float(
re.search(r"(\d+\.?\d*)", review_el["aria-label"]).group(1)
)
return result
# === 主流程 ===
keyword = "custom dog mug"
print(f"正在抓取关键词: {keyword}")
html = fetch_search(keyword, page=1)
listings = parse_search_listings(html)
print(f"找到 {len(listings)} 个商品")
# 取前 10 个 listing 的详情
for i, listing in enumerate(listings[:10]):
print(f"[{i+1}] {listing['title'][:50]}... "
f"${listing['price']}")
detail_html = fetch_listing_detail(listing["url"])
detail = parse_listing_detail(detail_html)
listing.update(detail)
# 随机延迟,避免触发限速
time.sleep(random.uniform(2, 5))
# 输出结果摘要
avg_price = sum(l["price"] for l in listings[:10]) / 10
print(f"\n平均价格: ${avg_price:.2f}")
for l in listings[:10]:
print(f" {l.get('sales', 'N/A')} sales | "
f"${l['price']} | {l['title'][:40]}")这个脚本的流程是:搜索 → 解析 JSON-LD → 逐个访问详情页 → 提取销量和评价。每次请求都通过 ProxyHat 住宅代理发出,IP 轮换避免被封。
关键注意事项
- 延迟很重要 — 即使有代理轮换,请求间隔至少 2-5 秒
- JSON-LD 优先 — 比解析 CSS 类名更稳定,Etsy 改版时 JSON-LD 通常不变
- 错误处理 — 遇到 429 或 403 时,增加延迟并重试
店铺分析:销量、评价与竞争情报
了解一个细分市场不仅要看商品,还要看卖家。Etsy 在店铺页面暴露了几个关键数据点:
销量徽章解析
Etsy 用 "X Sales" 徽章显示店铺总销量。这个数字是近似值(如 "5,000+ Sales"),但对于市场研究来说已经足够。解析方法:
import re
from bs4 import BeautifulSoup
def parse_shop_sales(html):
soup = BeautifulSoup(html, "html.parser")
# 查找销量徽章
sales_text = soup.find(string=re.compile(r"[\d,]+\s+Sales?",
re.IGNORECASE))
if sales_text:
num_str = re.search(r"([\d,]+)", sales_text).group(1)
return int(num_str.replace(",", ""))
return None
def parse_shop_reviews(html):
soup = BeautifulSoup(html, "html.parser")
# 评价星级分布
review_summary = soup.select_one(
".wt-align-items-center .wt-mb-xs-1"
)
stars = {}
for row in soup.select("[data-review-stars]"):
star_count = row.get("data-review-stars")
count_text = row.get_text(strip=True)
try:
stars[star_count] = int(
re.search(r"(\d+)", count_text).group(1)
)
except (AttributeError, ValueError):
pass
return stars店铺研究的实用指标
- 在售商品数 — 店铺活跃度指标
- 总销量 / 在售商品数 — 平均每个商品的销量,反映产品效率
- 店铺创建时间 — "On Etsy since YYYY",结合销量可算月均增速
- 评价分布 — 5 星占比高说明产品满意度好,值得研究其设计方向
用 curl 快速验证代理连通性
在写完整脚本之前,先用 curl 测试代理是否能正常访问 Etsy:
# 测试 ProxyHat 住宅代理访问 Etsy
curl -x http://user-country-US:PASSWORD@gate.proxyhat.com:8080 \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) " \
-H "Accept: text/html" \
"https://www.etsy.com/search?q=custom+dog+mug" \
-o etsy_test.html -w "%{http_code}\n"如果返回 200,说明代理工作正常。如果返回 403 或 503,可能需要切换 IP 或增加请求间隔。
SOCKS5 代理:更高匿名度的选择
对于需要更高匿名度的场景(如频繁访问同一店铺页面),可以使用 ProxyHat 的 SOCKS5 代理:
import requests
SOCKS5_PROXY = "socks5://user-country-US:PASSWORD@gate.proxyhat.com:1080"
proxies = {"http": SOCKS5_PROXY, "https": SOCKS5_PROXY}
# 用 SOCKS5 抓取店铺页面
resp = requests.get(
"https://www.etsy.com/shop/ShopName",
headers=HEADERS,
proxies=proxies,
timeout=30
)
print(resp.status_code)SOCKS5 的优势在于它工作在更底层,不暴露 HTTP 头中的代理特征。但速度可能略慢于 HTTP 代理。
细分市场研究工作流
把上面的技术组合起来,一个完整的 Etsy 细分市场研究流程如下:
- 关键词发散 — 用搜索建议 API 和 Google Trends 找出 50-100 个候选关键词
- 搜索量估算 — 抓取每个关键词的搜索结果数,作为需求代理指标
- 竞争分析 — 抓取前 3 页的 listing,提取价格分布、评价数、卖家信息
- 店铺深挖 — 对头部卖家做店铺分析,看他们的销量、商品数、创建时间
- 机会判断 — 结合需求量、竞争度、价格区间,判断是否值得进入
🔑 这个流程的核心是:用数据验证直觉,而不是靠猜。Etsy 上的数据是公开的,只要合规抓取,你就能比 90% 的卖家更了解市场。
伦理边界:研究而非抄袭
这一点非常重要,需要单独强调:
- 不要复制设计 — Etsy 卖家大多是独立创作者,他们的设计受版权保护。抓取数据是为了了解市场趋势,不是照搬产品图或文案
- 尊重 robots.txt — 检查
https://www.etsy.com/robots.txt了解哪些路径不建议爬取 - 控制请求频率 — 不要对 Etsy 服务器造成过大负担
- 遵守 ToS — 仔细阅读 Etsy 的服务条款,了解自动化数据采集的规定
- GDPR/CCPA — 如果抓取涉及用户个人信息(如评价者姓名),需遵守相关隐私法规
合理的使用场景包括:市场趋势分析、定价策略研究、关键词优化、竞争格局了解。不合理的使用包括:批量复制产品图片、盗用文案、自动化抢购。
常见问题与故障排除
Cloudflare 挑战(403/503)
如果频繁遇到 Cloudflare 挑战页:
- 确认使用的是住宅代理,而非数据中心 IP
- 增加请求间隔到 5-10 秒
- 检查 User-Agent 和 Accept 头是否完整
- 考虑使用
requests.Session()保持 cookie 一致性
数据不完整
Etsy 的部分内容依赖 JavaScript 渲染。如果 JSON-LD 中缺少某些字段:
- 对比浏览器中查看页面源代码(Ctrl+U)和渲染后 DOM 的差异
- 对于必须 JS 渲染的数据,考虑使用 Playwright 或 Selenium
- 优先使用 JSON-LD,它是 Etsy 向搜索引擎暴露的结构化数据,最稳定
IP 被封
即使使用住宅代理,如果请求模式太规律也可能被封:
- 使用 ProxyHat 的每次请求换 IP 模式(不带 session 参数)
- 随机化请求间隔和顺序
- 限制并发数(建议不超过 3-5 个同时请求)
关键要点
Etsy 抓取要点回顾:
- Etsy 搜索结果最多 250 页,每页约 48 个 listing
- JSON-LD 是最稳定的数据提取来源,优先于 CSS 选择器
- 住宅代理是抓取 Etsy 的必要条件,数据中心 IP 会被 Cloudflare 拦截
- 搜索建议 API 是挖掘长尾关键词的利器
- 店铺销量徽章提供市场规模的粗略估计
- 始终遵守伦理边界:研究市场,不抄袭设计
- 请求间隔 2-5 秒,并发不超过 5,是安全的基本参数
如果你想快速开始,可以在 ProxyHat 定价页 选择适合你规模的住宅代理套餐。更多抓取技巧,参考我们的 网页抓取最佳实践 和 网页抓取用例 页面。






