Porque Raspar o eBay é Diferente de Qualquer Outro Marketplace
Se você já tentou raspar o eBay em escala, sabe o que acontece: as primeiras 50 páginas funcionam bem, depois os CAPTCHAs aparecem, seu IP datacenter é banido e você volta ao ponto de partida. Equipas de reseller intelligence e pesquisa de mercado perdem horas — e dados — porque não entendem a relação entre a arquitetura anti-bot do eBay e a escolha correta de proxy.
Este guia é pragmático. Vamos comparar as APIs oficiais do eBay com scraping HTML, mostrar os seletores CSS reais que funcionam hoje, detalhar como escolher proxies para cada cenário, e entregar exemplos de código Python que você pode executar hoje.
APIs do eBay vs. Scraping HTML: O Trade-Off Real
O eBay oferece duas APIs principais para desenvolvedores: a Finding API (busca de itens) e a Browse API (detalhes de listagens). Ambas são úteis — até você atingir os limites.
Finding API — O que você precisa saber
- Endpoint base:
https://svcs.ebay.com/services/search/FindingService/v1 - Limite padrão: 5.000 chamadas por dia por App ID (nível básico)
- Rate limit: ~1 request/segundo por chave
- Retorna até 100 itens por página, máximo de ~10.000 resultados por consulta
- Campos de leilão limitados — sem contagem de bids em tempo real
Browse API — Para detalhes de listagem
- Endpoint:
https://api.ebay.com/buy/browse/v1/item/{item_id} - Limite: 1.500 chamadas por dia no tier gratuito
- Requer OAuth token (Client Credentials ou User Token)
- Dados estruturados: preço, condição, vendedor, imagens — mas sem HTML do anúncio completo
Quando o Scraping HTML é a Melhor Opção
- Volume alto: Quando você precisa de 100K+ listagens/dia — as APIs não escalam sem negociação comercial com o eBay
- Dados de leilão em tempo real: Contagem de bids, tempo restante, histórico — a API não entrega isso de forma completa
- Perfil de vendedor completo: Feedback detalhado, categorias de listagem, padrões de cross-listing
- eBay regionais: eBay.de, eBay.co.uk, eBay.com.au — as APIs cobrem principalmente eBay.com
- Dados de variações/SKUs: A grade de variações nos anúncios é mais completa no HTML
| Critério | Finding API | Browse API | Scraping HTML |
|---|---|---|---|
| Volume/dia | ~5K requests | ~1.5K requests | Ilimitado (com proxies) |
| Dados de leilão | Limitados | Limitados | Completos |
| Perfil vendedor | Básico | Médio | Completo |
| Cobertura regional | Principalmente .com | .com + alguns | Todos os domínios |
| Setup inicial | Médio (App ID) | Alto (OAuth) | Baixo (proxy + parser) |
| Risco de bloqueio | Nenhum | Nenhum | Alto sem proxy correto |
Estrutura HTML do eBay: Seletores que Funcionam
O eBay muda a estrutura HTML com frequência, mas alguns seletores são estáveis há anos. Aqui estão os que você precisa.
Página de Resultados de Busca
URL padrão: https://www.ebay.com/sch/i.html?_nkw={query}&_pgn={page}
- Cada item:
li.s-item— cada listagem na grade de resultados - Título:
div.s-item__title - Preço:
span.s-item__price - URL do anúncio:
a.s-item__link(atributohref) - Imagem:
img.s-item__image-img(atributosrc) - Envio:
span.s-item__shipping - Localização:
span.s-item__location - Flag Buy It Now:
span.s-item__dynamic-onsaleou texto "Buy It Now" emdiv.s-item__buy-it-now - Bids count:
span.s-item__bid-count(apenas em leilões ativos) - Tempo restante:
span.s-item__time-left
Página de Detalhe do Anúncio
URL: https://www.ebay.com/itm/{item_id}
- Título:
h1#itemTitleouh1.x-item-title__mainTitle - Preço:
div.x-price-primary span - Condição:
div.x-item-condition-text - Vendedor:
div.x-seller-info__name - Feedback do vendedor:
span.x-seller-info__feedback - Variações:
div.x-variation-selector - Descrição: iframe em
iframe#desc_ifr— conteúdo carregado separadamente
Página de Perfil do Vendedor
URL: https://www.ebay.com/usr/{username}
- Feedback score:
span.feedbackou área comdiv.str-profile__feedback - Positivo %:
div.str-profile__feedback-percentage - Categorias de listagem:
div.str-profile__categories - Membro desde:
div.str-profile__info
Seleção de Proxy para Scraping do eBay
O eBay é agressivo contra IPs datacenter. Se você usar proxies datacenter para scraping em escala, vai ser bloqueado em minutos — não em horas. Aqui está a estratégia correta.
Proxies Residenciais — A Escolha Principal
Para qualquer scraping do eBay acima de 1.000 páginas/dia, residenciais são obrigatórios. O eBay verifica ASN (Autonomous System Number) e bloqueia ranges de IPs conhecidos como datacenter. IPs residenciais parecem usuários reais.
Com ProxyHat, configure o proxy residencial com rotação por request:
# Rotação automática por request — cada request usa um IP residencial diferente
http://USERNAME:PASSWORD@gate.proxyhat.com:8080
Geo-Targeting para eBay Regionais
Os eBay regionais mostram preços, disponibilidade e vendedores diferentes conforme a localização. Para raspar eBay.de ou eBay.co.uk com resultados corretos:
# Alemanha — resultados do eBay.de
curl -x http://user-country-DE:PASSWORD@gate.proxyhat.com:8080 \
"https://www.ebay.de/sch/i.html?_nkw=iphone+15"
# Reino Unido — resultados do eBay.co.uk
curl -x http://user-country-GB:PASSWORD@gate.proxyhat.com:8080 \
"https://www.ebay.co.uk/sch/i.html?_nkw=iphone+15"
Sticky Sessions para Múltiplas Páginas
Quando você precisa navegar entre páginas de resultados mantendo a mesma sessão (evitando CAPTCHAs mid-session), use sticky sessions:
# Sessão fixa — mesmo IP por até 30 minutos
http://user-session-myebay01:PASSWORD@gate.proxyhat.com:8080
| Cenário | Tipo de Proxy | Rotação | Geo-Target |
|---|---|---|---|
| Busca pontual (<1K páginas) | Datacenter | Por request | Nenhum |
| Scraping em escala (1K-100K) | Residencial | Por request | País do target |
| eBay regional (DE, UK, AU) | Residencial | Sticky (10-30 min) | Paês específico |
| Monitoramento contínuo de preços | Residencial | Sticky | Paês do marketplace |
| Verificação de conta/login | Mobile | Sticky | Paês da conta |
Exemplo Python: Scraper de Resultados de Busca
Aqui está um exemplo funcional que busca resultados no eBay, extrai dados estruturados de cada .s-item e lida com paginação e rotação de IP.
import requests
from bs4 import BeautifulSoup
import csv
import time
import random
PROXY = "http://USERNAME: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/125.0.0.0 Safari/537.36"
),
"Accept-Language": "en-US,en;q=0.9",
}
def scrape_ebay_search(query, max_pages=5):
"""Raspa resultados de busca do eBay e retorna registros estruturados."""
results = []
base_url = "https://www.ebay.com/sch/i.html"
for page in range(1, max_pages + 1):
params = {"_nkw": query, "_pgn": page}
try:
resp = requests.get(
base_url, params=params,
headers=HEADERS, proxies=PROXIES, timeout=15
)
resp.raise_for_status()
except requests.RequestException as e:
print(f"Erro na página {page}: {e}")
break
soup = BeautifulSoup(resp.text, "html.parser")
items = soup.select("li.s-item")
if not items:
print(f"Nenhum item na página {page} — possível bloqueio ou fim de resultados.")
break
for item in items:
record = parse_s_item(item)
if record:
results.append(record)
# Delay aleatório entre páginas
time.sleep(random.uniform(2, 5))
print(f"Página {page}: {len(items)} itens extraídos")
return results
def parse_s_item(item):
"""Extrai campos de um elemento li.s-item."""
title_el = item.select_one("div.s-item__title")
if not title_el:
return None
price_el = item.select_one("span.s-item__price")
link_el = item.select_one("a.s-item__link")
shipping_el = item.select_one("span.s-item__shipping")
location_el = item.select_one("span.s-item__location")
bid_el = item.select_one("span.s-item__bid-count")
time_el = item.select_one("span.s-item__time-left")
bin_el = item.select_one("div.s-item__buy-it-now")
return {
"title": title_el.get_text(strip=True),
"price": price_el.get_text(strip=True) if price_el else None,
"url": link_el["href"] if link_el else None,
"shipping": shipping_el.get_text(strip=True) if shipping_el else None,
"location": location_el.get_text(strip=True) if location_el else None,
"bid_count": bid_el.get_text(strip=True) if bid_el else None,
"time_left": time_el.get_text(strip=True) if time_el else None,
"buy_it_now": bool(bin_el),
}
if __name__ == "__main__":
data = scrape_ebay_search("mechanical keyboard", max_pages=3)
with open("ebay_results.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=data[0].keys())
writer.writeheader()
writer.writerows(data)
print(f"Total: {len(data)} itens salvos")
Exemplo de resposta truncada de um parse_s_item:
{
"title": "Custom Mechanical Keyboard RGB Hot-Swap 87 Key",
"price": "$89.99",
"url": "https://www.ebay.com/itm/1234567890",
"shipping": "+$12.50 shipping",
"location": "Shenzhen, China",
"bid_count": null,
"time_left": null,
"buy_it_now": true
}
Lidando com Leilões: Tempo, Bids e Buy-It-Now
Leilões são o grande diferencial do eBay — e os dados mais valiosos para resellers. A página de resultados mostra informações resumidas, mas os detalhes completos estão na página do anúncio.
Campos de Leilão nos Resultados de Busca
span.s-item__bid-count— ex: "23 bids"span.s-item__time-left— ex: "1h 23m left"- Ausência de
div.s-item__buy-it-nowindica leilão puro (sem BIN)
Detalhes Completos na Página do Anúncio
Para obter o histórico de bids e tempo restante preciso, você precisa raspar a página do item:
def scrape_auction_details(item_url, session=None):
"""Raspa detalhes de leilão de uma página de anúncio do eBay."""
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/125.0.0.0 Safari/537.36"
),
}
resp = requests.get(item_url, headers=HEADERS, proxies=PROXIES, timeout=15)
soup = BeautifulSoup(resp.text, "html.parser")
auction = {
"item_id": item_url.split("/itm/")[-1].split("?")[0],
"title": None,
"current_price": None,
"bid_count": None,
"time_left_seconds": None,
"is_buy_it_now": False,
"bid_history_url": None,
}
# Título
title_el = soup.select_one("h1.x-item-title__mainTitle")
if title_el:
auction["title"] = title_el.get_text(strip=True)
# Preço atual
price_el = soup.select_one("div.x-price-primary span")
if price_el:
auction["current_price"] = price_el.get_text(strip=True)
# Contagem de bids
bid_el = soup.select_one("div.x-bid-count span")
if bid_el:
auction["bid_count"] = bid_el.get_text(strip=True)
# Tempo restante (extraído do JavaScript na página)
time_el = soup.select_one("span.x-time-left")
if time_el:
auction["time_left_seconds"] = time_el.get("data-timestamp")
# Buy It Now
bin_btn = soup.select_one("button[data-testid='x-bin-btn']")
auction["is_buy_it_now"] = bin_btn is not None
# Link para histórico de bids
bid_hist = soup.select_one("a[href*='/bfl/view/bids']")
if bid_hist:
auction["bid_history_url"] = bid_hist["href"]
return auction
Estratégia para Leilões em Tempo Real
- Polling com sticky sessions: Use
user-session-{id}:PASSWORDpara manter o mesmo IP durante o monitoramento - Intervalo de 30-60 segundos: Para leilões ativos, polling a cada 30s é suficiente — mais rápido atrai atenção
- Snapshot final: Capture o estado nos últimos 5 minutos para registrar o preço final
Analytics de Vendedores: Feedback, Categorias e Cross-Listing
Para equipas de reseller intelligence, entender o perfil do vendedor é tão importante quanto os dados do produto. Aqui está como extrair isso em escala.
def scrape_seller_profile(username):
"""Raspa o perfil público de um vendedor do eBay."""
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/125.0.0.0 Safari/537.36"
),
}
url = f"https://www.ebay.com/usr/{username}"
resp = requests.get(url, headers=HEADERS, proxies=PROXIES, timeout=15)
soup = BeautifulSoup(resp.text, "html.parser")
seller = {"username": username}
# Feedback score
fb_score = soup.select_one("span.feedback")
seller["feedback_score"] = fb_score.get_text(strip=True) if fb_score else None
# Percentual positivo
fb_pct = soup.select_one("div.str-profile__feedback-percentage")
seller["positive_pct"] = fb_pct.get_text(strip=True) if fb_pct else None
# Membro desde
info = soup.select_one("div.str-profile__info")
seller["member_since"] = info.get_text(strip=True) if info else None
# Categorias de listagem
categories = []
cat_els = soup.select("div.str-profile__categories li")
for cat in cat_els:
categories.append(cat.get_text(strip=True))
seller["categories"] = categories
# Itens ativos (contagem)
items_el = soup.select_one("div.str-profile__items-count")
seller["active_items_count"] = items_el.get_text(strip=True) if items_el else None
return seller
def detect_cross_listing(seller_data_list):
"""Detecta padrões de cross-listing entre vendedores."""
from collections import Counter
category_pairs = []
for seller in seller_data_list:
cats = seller.get("categories", [])
if len(cats) >= 2:
for i in range(len(cats)):
for j in range(i + 1, len(cats)):
category_pairs.append((cats[i], cats[j]))
return Counter(category_pairs).most_common(10)
Métricas de Vendedor que Importam
- Feedback score vs. tempo: Um vendedor com 50K feedbacks em 2 anos é muito diferente de 50K em 15 anos
- Distribuição de categorias: Vendedores com categorias muito diversas podem ser dropshippers
- Padrões de cross-listing: Se muitos vendedores listam nas mesmas combinações de categorias, há um nicho ou uma fonte comum
- Feedback recente vs. histórico: Uma queda no percentual positivo nos últimos 90 dias pode indicar problemas de qualidade
Tecnologia Anti-Bot do eBay e Como Lidar
O eBay usa múltiplas camadas de proteção:
- Verificação de ASN: IPs de datacenters conhecidos (AWS, DigitalOcean, Hetzner) são bloqueados ou recebem CAPTCHAs imediatamente
- Rate limiting por IP: Aproximadamente 100-200 requests/min por IP antes de CAPTCHA
- AKAMAI Bot Manager: Fingerprinting de browser via JavaScript — detecta headless browsers
- PerimeterX/HUMAN: Challenge em páginas de login e ações sensíveis
- Cookie-based session tracking: Múltiplos requests sem cookies de sessão são suspeitos
Mitigações Práticas
- Use proxies residenciais: Isso elimina 80% dos problemas — o ASN check é a primeira barreira
- Rotacione IPs a cada 50-100 requests: Não espere ser bloqueado para trocar
- Use headers realistas: User-Agent, Accept-Language, Accept-Encoding consistentes
- Mantenha cookies de sessão: Use
requests.Session()para manter cookies entre requests - Delays aleatórios: 2-8 segundos entre requests, nunca um intervalo fixo
- Para escala muito alta: Considere Playwright ou Puppeteer com stealth plugins — o fingerprinting do AKAMAI é sofisticado
Node.js: Exemplo Rápido com Axios
Para equipas que preferem JavaScript, aqui está um exemplo mínimo funcional:
const axios = require("axios");
const { HttpsProxyAgent } = require("https-proxy-agent");
const cheerio = require("cheerio");
const PROXY_URL = "http://USERNAME:PASSWORD@gate.proxyhat.com:8080";
const agent = new HttpsProxyAgent(PROXY_URL);
async function scrapeEbaySearch(query, page = 1) {
const url = "https://www.ebay.com/sch/i.html";
const resp = await axios.get(url, {
params: { _nkw: query, _pgn: page },
httpsAgent: agent,
headers: {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/125.0.0.0",
"Accept-Language": "en-US,en;q=0.9",
},
timeout: 15000,
});
const $ = cheerio.load(resp.data);
const items = [];
$("li.s-item").each((_, el) => {
const title = $(el).find("div.s-item__title").text().trim();
const price = $(el).find("span.s-item__price").text().trim();
const link = $(el).find("a.s-item__link").attr("href");
const bids = $(el).find("span.s-item__bid-count").text().trim();
const timeLeft = $(el).find("span.s-item__time-left").text().trim();
const isBIN = $(el).find("div.s-item__buy-it-now").length > 0;
if (title) {
items.push({ title, price, link, bids, timeLeft, isBIN });
}
});
return items;
}
scrapeEbaySearch("vintage watch", 1)
.then((items) => console.log(JSON.stringify(items.slice(0, 3), null, 2)))
.catch(console.error);
Considerações Éticas e Legais
- Respeite robots.txt: O eBay permite crawling de áreas públicas, mas verifique
https://www.ebay.com/robots.txtpara restrições - Não raspe dados pessoais: Nome, endereço, telefone de compradores — isso viola GDPR e CCPA
- Rate limits razoáveis: Mesmo com proxies, não sature os servidores do eBay
- Terms of Service: O scraping pode violar os ToS do eBay — use por sua conta e risco, e consulte um advogado se for para uso comercial em larga escala
- Dados públicos vs. privados: Preços, títulos e feedbacks são públicos; dados de transações completas não são
Conclusão e Próximos Passos
Raspar o eBay em escala exige a combinação certa de proxies residenciais, seletores CSS estáveis e estratégia de rotação. As APIs oficiais são úteis para volumes baixos e dados estruturados, mas o scraping HTML é insubstituível para leilões em tempo real, dados regionais e analytics de vendedores.
Comece com o exemplo Python acima, configure seus proxies residenciais no ProxyHat e escale gradualmente. Monitore suas taxas de sucesso — se cair abaixo de 90%, ajuste a velocidade de rotação ou os delays.
Key Takeaways
- APIs primeiro, scraping como fallback: Use Finding API para volumes baixos; scraping quando a API não cobre seus dados ou volume
- Residenciais são obrigatórios: O eBay bloqueia IPs datacenter agressivamente — proxies residenciais eliminam 80% dos bloqueios
- Geo-targeting importa: eBay.de e eBay.co.uk mostram resultados diferentes — use proxies no país correto
- .s-item é seu melhor amigo: Esse seletor é estável há anos e contém quase tudo que você precisa dos resultados de busca
- Leilões exigem sticky sessions: Para monitorar leilões em tempo real, mantenha o mesmo IP com sessões fixas
- Analytics de vendedores é ouro para resellers: Feedback, categorias e padrões de cross-listing revelam nichos e concorrentes
Para mais guias de scraping com proxies, veja nosso artigo sobre raspar resultados do Google e nosso use case de web scraping. Confira as localizações de proxy disponíveis para geo-targeting específico.






