API vs. HTML: O Dilema do Scraping no Etsy
Se você está lendo isto, provavelmente quer dados do Etsy para pesquisa de nichos — termos trending, preços médios, contagem de vendedores por categoria. O Etsy não oferece uma API pública para busca de listagens. A Open API v3 exige OAuth por vendedor e só retorna dados da sua própria loja. Para pesquisa de mercado, resta o caminho do HTML.
Scraping de HTML no Etsy é viável, mas exige estratégia. O site usa Cloudflare, rate limits agressivos e fingerprints de navegador. Vamos detalhar cada camada e mostrar como contorná-las com proxies residenciais e rotação inteligente — sem queimar seus IPs.
Estrutura do Etsy: O Que Existe para Raspar
Antes de escrever código, você precisa entender como o Etsy organiza seus dados. Há quatro superfícies principais:
1. Páginas de Busca (Search)
URL: https://www.etsy.com/search?q=QUERY&ref=search_bar
Parâmetros úteis:
q— termo de buscaorder—most_relevant(padrão),price_asc,price_desc,newestmin_price/max_price— filtro de preçoship_to— geotarget (código de país de 2 letras)locationQuery— ID numérico de localização
Cada página de busca retorna até 48 listagens. A paginação usa &page=2, &page=3, etc. O Etsy corta resultados em torno de 250 páginas (~12.000 itens) para buscas populares.
2. Páginas de Listagem (Listing Detail)
URL: https://www.etsy.com/listing/LISTING_ID/SLUG
Contém: título, descrição, preço, imagens, variações, avaliações, dados do vendedor, badge de vendas.
Seletor CSS útil para o preço: div[data-testid='price'] ou .wt-text-title-01 dentro do container de preço. Para o título: h1[data-testid='listing-title'].
3. Páginas de Loja (Shop)
URL: https://www.etsy.com/shop/SHOP_NAME
Contém: contagem de listagens, badge de vendas ("X Sales"), avaliações da loja, localização, data de criação.
O badge de vendas aparece como <span class="wt-text-caption">1,234 Sales</span> ou similar. É arredondado — não é um número exato, mas suficiente para pesquisa de nichos.
4. Árvore de Categorias
O Etsy organiza categorias hierarquicamente: Jewelry > Necklaces > Pendant Necklaces.
URL base: https://www.etsy.com/c/CATEGORY_SLUG
Você pode mapear a árvore de categorias raspando o footer ou o menu de navegação. Cada categoria funciona como uma busca filtrada.
Anti-Bot no Etsy: Cloudflare + Rate Limits Internos
O Etsy usa Cloudflare como WAF/CDN. Isso significa:
- Desafios JavaScript (Turnstile) em requisições suspeitas
- Fingerprinting de TLS, HTTP/2 e headers
- Bloqueio de IPs de datacenter conhecidos
Além do Cloudflare, o Etsy impõe rate limits internos:
- ~80 requisições/minuto por IP antes de receber HTTP 429
- ~200 requisições/minuto antes de um ban temporário (HTTP 403 com challenge Cloudflare)
- Após múltiplos bans, o IP pode ser bloqueado por 24–48 horas
Recomendação: Use proxies residenciais com rotação por requisição. IPs de datacenter são bloqueados rapidamente pelo Cloudflare. IPs residenciais passam pela verificação de challenge com muito mais frequência de sucesso.
Para configurar rotação de IP com ProxyHat, use o formato de URL com flag de país:
# HTTP residencial — rotação por requisição (padrão)
http://user-country-US:SUA_SENHA@gate.proxyhat.com:8080
# Sticky session (mesmo IP por ~10 min)
http://user-country-US-session-abc123:SUA_SENHA@gate.proxyhat.com:8080
Proxies Residenciais vs. Datacenter vs. Mobile para Etsy
| Tipo | Success Rate no Etsy | Velocidade | Custo | Uso Recomendado |
|---|---|---|---|---|
| Residencial | ~92–96% | Média | $$ | Busca + listagem + loja (uso geral) |
| Datacenter | ~30–50% | Alta | $ | Não recomendado para Etsy |
| Mobile | ~97–99% | Baixa | $$$ | Scraping pesado em alto volume |
Padrões de Scraping para Descoberta de Nichos
O objetivo não é copiar designs — é identificar oportunidades. Aqui estão os padrões que importam para equipes POD:
Termos de Busca em Alta (Trending)
O Etsy não tem uma página pública de "trending searches" como o Google Trends. Mas você pode:
- Raspar a API de autocomplete — retorna sugestões em tempo real
- Monitorar categorias sazonais (Halloween em setembro, Natal em outubro)
- Comparar contagens de resultados entre buscas relacionadas
A URL de autocomplete: https://www.etsy.com/search/suggestions?query=custom+name
Resposta (truncada):
{
"results": [
{"query": "custom name necklace", "type": "search_suggestion"},
{"query": "custom name bracelet", "type": "search_suggestion"},
{"query": "custom name shirt", "type": "search_suggestion"}
]
}
Contagem de Vendedores por Nicho
Na página de busca, o Etsy mostra "X results" no topo. Este número é a contagem total de listagens — não de vendedores únicos. Para obter vendedores únicos:
- Raspe as primeiras 5–10 páginas de resultados
- Extraia o nome da loja de cada listagem
- Conte valores únicos
Se um nicho tem 10.000 listagens mas apenas 50 vendedores, é um mercado concentrado. Se tem 5.000 listagens com 2.000 vendedores, é fragmentado — mais fácil de entrar.
Pontos de Preço Médio
Use o parâmetro order=price_asc e order=price_desc para mapear a distribuição de preços de um nicho. Extraia preços dos cards de busca:
Seletor CSS: span.currency-value dentro de cada div.v2-listing-card.
Exemplo em Python: Busca → Listagem → Detalhes com Proxy Residencial
Abaixo, um script completo que (1) busca um termo no Etsy, (2) extrai dados dos cards de resultado, (3) visita cada página de listagem para obter detalhes adicionais — tudo com rotação de IP via ProxyHat.
import requests
from bs4 import BeautifulSoup
import re
import time
import random
# --- Configuração ProxyHat ---
PROXY_USER = "user-country-US" # Rotação por requisição
PROXY_PASS = "SUA_SENHA"
PROXY_URL = f"http://{PROXY_USER}:{PROXY_PASS}@gate.proxyhat.com:8080"
PROXIES = {"http": PROXY_URL, "https": PROXY_URL}
HEADERS = {
"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"
),
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br",
}
SESSION = requests.Session()
SESSION.headers.update(HEADERS)
SESSION.proxies.update(PROXIES)
def search_etsy(query, page=1):
"""Raspa página de busca do Etsy e retorna listagens."""
url = "https://www.etsy.com/search"
params = {"q": query, "page": page, "order": "most_relevant"}
resp = SESSION.get(url, params=params, timeout=30)
resp.raise_for_status()
soup = BeautifulSoup(resp.text, "html.parser")
listings = []
cards = soup.select("div.v2-listing-card")
for card in cards:
link_tag = card.select_one("a.listing-link")
title_tag = card.select_one("h3")
price_tag = card.select_one("span.currency-value")
shop_tag = card.select_one("a[href*='/shop/']")
listing_url = link_tag["href"] if link_tag else None
listing_id = re.search(r"/listing/(\d+)", listing_url).group(1) if listing_url else None
title = title_tag.get_text(strip=True) if title_tag else None
price = price_tag.get_text(strip=True) if price_tag else None
shop_name = shop_tag.get_text(strip=True) if shop_tag else None
if listing_id:
listings.append({
"listing_id": listing_id,
"title": title,
"price": price,
"shop_name": shop_name,
"url": listing_url,
})
return listings
def get_listing_detail(listing_id, slug=""):
"""Raspa detalhes de uma listagem específica."""
url = f"https://www.etsy.com/listing/{listing_id}/{slug}"
resp = SESSION.get(url, timeout=30)
resp.raise_for_status()
soup = BeautifulSoup(resp.text, "html.parser")
title = soup.select_one("h1[data-testid='listing-title']")
title = title.get_text(strip=True) if title else None
price_el = soup.select_one("div[data-testid='price']")
price = price_el.get_text(strip=True) if price_el else None
# Badge de vendas do vendedor
sales_badge = soup.find(string=re.compile(r"\d+[\d,]*\s+Sales"))
sales_count = sales_badge.strip() if sales_badge else None
# Contagem de avaliações
review_count = soup.select_one("button[data-testid='reviews-header']")
reviews = review_count.get_text(strip=True) if review_count else None
return {
"listing_id": listing_id,
"title": title,
"price": price,
"seller_sales": sales_count,
"reviews_summary": reviews,
}
# --- Execução ---
niche = "custom name mug"
print(f"Buscando nicho: {niche}")
all_listings = []
for page_num in range(1, 4): # 3 páginas = até 144 listagens
listings = search_etsy(niche, page=page_num)
all_listings.extend(listings)
print(f" Página {page_num}: {len(listings)} listagens")
time.sleep(random.uniform(2, 5)) # Rate limiting manual
# Detalhes das primeiras 10 listagens
for listing in all_listings[:10]:
detail = get_listing_detail(listing["listing_id"])
print(f" {detail['title'][:50]}... | Preço: {detail['price']} | Vendas vendedor: {detail['seller_sales']}")
time.sleep(random.uniform(3, 7))
Pontos-chave do script:
- Rotação automática de IP: cada requisição passa pelo ProxyHat com rotação residencial
- Delay aleatório entre requisições (2–7s) para simular comportamento humano
- Headers realistas para reduzir chance de challenge Cloudflare
- Parse defensivo: verifica se elementos existem antes de extrair texto
Analytics de Loja: Extraindo Dados do Vendedor
Para análise competitiva, você precisa de dados por loja — não apenas por listagem. O Etsy expõe informações úteis nas páginas de shop:
Contagem de Listagens da Loja
Seletor: span[data-testid='shop-listing-count'] ou o texto "X items" no header da loja.
Badge de Vendas
O Etsy mostra badges como "1,234 Sales", "5,000+ Sales". Este número é arredondado — não é exato, mas é excelente para comparar vendedores.
XPath: //span[contains(text(), 'Sales')]
Avaliações da Loja
URL: https://www.etsy.com/shop/SHOP_NAME/reviews
Contém avaliações com texto, estrelas e data. O número total de reviews aparece no header: "X reviews".
Aqui está um script para raspar dados de uma loja:
def scrape_shop(shop_name):
"""Raspa dados de uma loja Etsy."""
url = f"https://www.etsy.com/shop/{shop_name}"
resp = SESSION.get(url, timeout=30)
resp.raise_for_status()
soup = BeautifulSoup(resp.text, "html.parser")
# Contagem de listagens
listing_count_el = soup.select_one("span[data-testid='shop-listing-count']")
listing_count = listing_count_el.get_text(strip=True) if listing_count_el else "N/A"
# Badge de vendas
sales_text = None
for el in soup.find_all(string=re.compile(r"[\d,]+\s*Sales?", re.I)):
sales_text = el.strip()
break
# Avaliação média
rating_el = soup.select_one("[data-testid='shop-rating']")
rating = rating_el.get("aria-label", "N/A") if rating_el else "N/A"
# Localização
location_el = soup.select_one("[data-testid='shop-location']")
location = location_el.get_text(strip=True) if location_el else "N/A"
return {
"shop": shop_name,
"listing_count": listing_count,
"sales_badge": sales_text,
"rating": rating,
"location": location,
}
# Exemplo: analisar vendedores de um nicho
shops_seen = set()
for listing in all_listings:
shop = listing.get("shop_name")
if shop and shop not in shops_seen:
shops_seen.add(shop)
shop_data = scrape_shop(shop)
print(f" {shop}: {shop_data['listing_count']} items | {shop_data['sales_badge']} | {shop_data['rating']}")
time.sleep(random.uniform(4, 8))
Autocomplete do Etsy: Mineração de Palavras-chave
Uma das fontes mais valiosas para Etsy niche research é a API de autocomplete. Ela retorna termos que compradores realmente buscam. Aqui está como minerá-la sistematicamente:
import string
BASE_SUGGEST_URL = "https://www.etsy.com/search/suggestions"
def get_suggestions(prefix):
"""Obtém sugestões de busca do Etsy para um prefixo."""
params = {"query": prefix}
resp = SESSION.get(BASE_SUGGEST_URL, params=params, timeout=15)
resp.raise_for_status()
data = resp.json()
return [r["query"] for r in data.get("results", [])]
# Mineração por letra — encontra todos os termos começando com "custom "
base_term = "custom "
all_suggestions = set()
for letter in string.ascii_lowercase:
prefix = f"{base_term}{letter}"
suggestions = get_suggestions(prefix)
all_suggestions.update(suggestions)
print(f" Prefixo '{prefix}': {len(suggestions)} sugestões")
time.sleep(random.uniform(1, 3))
print(f"\nTotal de termos únicos: {len(all_suggestions)}")
for term in sorted(all_suggestions):
print(f" - {term}")
Esta técnica gera centenas de termos de nicho em minutos. Combine com contagem de resultados de busca para ranquear oportunidades.
Usando cURL com ProxyHat para Testes Rápidos
Para validar se seu setup de proxy funciona antes de escrever código:
# Teste de proxy residencial — busca no Etsy
curl -x http://user-country-US:SUA_SENHA@gate.proxyhat.com:8080 \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
-H "Accept: text/html" \
"https://www.etsy.com/search?q=custom+name+mug" \
-o etsy_search.html -w "%{http_code}\n"
# Teste de autocomplete
curl -x http://user-country-US:SUA_SENHA@gate.proxyhat.com:8080 \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
"https://www.etsy.com/search/suggestions?query=custom+name" \
2>/dev/null | python3 -m json.tool
Se o primeiro retornar 200 e o segundo retornar JSON válido, seu proxy está funcionando.
Estratégias de Rotação de IP para Volume Alto
Se você precisa raspar milhares de listagens por dia, a rotação por requisição não é suficiente. Estratégias avançadas:
Sessões Sticky para Múltiplas Páginas
Use sessões sticky ao paginar dentro da mesma busca — o Etsy detecta mudanças de IP no meio de uma sessão de navegação:
# Sessão sticky — mesmo IP por ~10 minutos
PROXY_USER_STICKY = "user-country-US-session-myniche123"
PROXY_URL_STICKY = f"http://{PROXY_USER_STICKY}:SUA_SENHA@gate.proxyhat.com:8080"
Rate Limiting por IP
Mantenha cada IP abaixo de 60 requisições/minuto (margem de segurança dos ~80 do Etsy). Se você precisa de 600 req/min, use pelo menos 10 IPs simultâneos.
Geo-targeting
O Etsy personaliza resultados por localização. Se seu público é americano, use user-country-US. Para o mercado britânico, user-country-GB. Veja todas as localizações disponíveis em nossa página de localizações.
Ética: Vendedores do Etsy São Pequenos Negócios
Este é o ponto mais importante deste guia. O Etsy não é a Amazon — a maioria dos vendedores são pequenos criadores independentes. Muitos vivem do seu negócio Etsy.
O que é aceitável:
- Pesquisa de nichos — identificar categorias com demanda e pouca concorrência
- Análise de preços — entender faixas de preço para posicionar seus produtos
- Monitoramento de tendências — detectar termos de busca em alta
- Pesquisa de mercado — entender o tamanho de um nicho (contagem de vendedores, listagens)
O que NÃO é aceitável:
- Copiar designs — raspar imagens e reimprimir em produtos POD é roubo intelectual
- Copiar descrições — texto de descrição é conteúdo criativo do vendedor
- Scraping agressivo — sobrecarregar o Etsy prejudica todos os vendedores
- Re-vender dados — vender listas de vendedores ou designs raspados
Regra de ouro: Scrape para entender o mercado, não para copiar o mercado. Use os dados para encontrar lacunas e criar produtos originais que atendam a demandas não supridas.
Respeite também o robots.txt do Etsy e os Termos de Serviço. O Etsy proíbe scraping nos ToS, então esteja ciente dos riscos. Use dados apenas para pesquisa interna e nunca para redistribuição.
Considerações Técnicas Adicionais
Cloudflare Turnstile
Quando o Cloudflare detecta tráfego suspeito, ele serve um desafio Turnstile (CAPTCHA invisível). Proxies residenciais reduzem significativamente a frequência desses desafios, mas não eliminam completamente. Se você encontrar desafios:
- Reduza a taxa de requisições
- Adicione delays mais longos (8–15s)
- Considere usar um browser headless (Playwright/Puppeteer) para resolver desafios automaticamente
Renderização JavaScript
Algumas partes do Etsy dependem de JavaScript para renderizar (avaliações, imagens lazy-loaded). Se você não obtém dados esperados com requests, pode precisar de renderização JS. Nesse caso, use Playwright com proxy:
from playwright.sync_api import sync_playwright
PROXY_SERVER = "gate.proxyhat.com:8080"
PROXY_USER = "user-country-US:SUA_SENHA"
with sync_playwright() as p:
browser = p.chromium.launch(
proxy={
"server": f"http://{PROXY_SERVER}",
"username": PROXY_USER.split(":")[0],
"password": PROXY_USER.split(":")[1],
},
headless=True,
)
page = browser.new_page()
page.goto("https://www.etsy.com/search?q=custom+name+mug", wait_until="networkidle")
content = page.content()
# Parse com BeautifulSoup como antes...
browser.close()
Tratamento de Erros
Implemente retry com backoff exponencial. O Etsy retorna 429 e 403 intermitentemente:
- HTTP 429 → espere 60s, tente novamente
- HTTP 403 com Cloudflare → troque de IP (nova requisição = novo IP com ProxyHat)
- HTTP 200 mas HTML sem dados → provavelmente challenge resolvido parcialmente, tente com outro IP
Key Takeaways
- O Etsy não tem API pública de busca — scraping de HTML é o único caminho para pesquisa de mercado em escala.
- Cloudflare + rate limits tornam proxies residenciais essenciais. Datacenter proxies têm taxa de sucesso abaixo de 50%.
- A API de autocomplete é a fonte mais rápida de termos de nicho — combine com contagem de resultados para ranquear oportunidades.
- Badge de vendas e contagem de listagens por loja são métricas suficientes para análise competitiva sem precisar de dados financeiros exatos.
- Mantenha cada IP abaixo de 60 req/min para evitar bans. Use sticky sessions para paginação e rotação por requisição para buscas independentes.
- Ética importa — scrape para pesquisa, nunca para copiar designs ou descrições de vendedores independentes.
Próximos Passos
Para começar com scraping ético no Etsy usando proxies residenciais confiáveis:
- Crie uma conta no ProxyHat e escolha um plano residencial
- Teste a conectividade com o comando cURL acima
- Adapte o script Python para seu nicho-alvo
- Configure alertas para HTTP 429/403 — se a taxa de erro passar de 10%, reduza a velocidade
- Veja mais padrões de scraping em nosso guia de web scraping
Para monitoramento contínuo de SERPs e preços, confira também nosso guia de SERP tracking.






