In this Article
Este guia mostra como fazer scraping do eBay com Python de ponta a ponta: um scraper funcional para resultados de busca, anúncios individuais, variantes de produto e avaliações, além da configuração anti-bot e de proxies que o mantém funcionando. Você precisa de Python 3, algumas bibliotecas e cerca de 30 minutos para a versão básica. Ao final terá funções reutilizáveis de web scraping do eBay que exportam JSON e CSV limpos.
Sou Andrii Byzov, CMO fracionário AI-Native que gerencia pipelines de dados de e-commerce. O eBay é um alvo valioso, mas bem defendido, então este passo a passo sobre como fazer scraping do eBay combina o código com as partes que a maioria dos guias omite: paginação, dados ocultos de variantes e como evitar bloqueios. A versão curta de fazer scraping do eBay com segurança: envie cabeçalhos de navegador reais, rotacione proxies residenciais e respeite os limites de taxa. Para saber quais IPs usar, veja nosso guia de melhores proxies para scraping do eBay.
Fatos-chave
- O eBay tem dois tipos de página —resultados de busca e anúncios de produto— com estruturas HTML diferentes, então você escreve dois scrapers.
- As variantes (tamanho, cor, armazenamento) ficam ocultas em JSON inline, não no HTML visível: você as extrai com uma expressão regular e
json.loads(). - Cabeçalhos de navegador reais vêm primeiro: sem um
User-Agentadequado, o eBay costuma devolver 403 ou páginas vazias mesmo em requisições simples. - Os IPs de data center são sinalizados rápido no eBay; os proxies residenciais rotativos sustentam o volume e devolvem preços corretos por região.
- Os anúncios diferem por país —eBay.com, eBay.co.uk e eBay.de mostram preços e fretes diferentes— então a segmentação geográfica importa para um scraping de dados do eBay preciso.
Passo 1 — Configure seu ambiente
Crie um ambiente isolado e instale as bibliotecas que seu setup de web scraping do eBay precisa. Cada uma tem sua função: requests busca as páginas, beautifulsoup4 + lxml fazem o parse do HTML e httpx gerencia requisições assíncronas quando você escala.
# Create an isolated project folder and virtual environment
mkdir ebay-scraper && cd ebay-scraper
python3 -m venv env
source env/bin/activate # Windows: env\Scripts\activate
# requests -> send HTTP requests
# beautifulsoup4 -> parse the returned HTML
# lxml -> fast HTML parser backend for BeautifulSoup
# httpx -> async requests for scaling later
pip install requests beautifulsoup4 lxml httpx
Passo 2 — Entenda a estrutura de páginas do eBay
Antes de escrever código, entenda que o eBay tem dois tipos de página fundamentalmente diferentes, e que saber como fazer scraping do eBay de forma confiável começa por conhecer quais seletores pertencem a cada um. Cada tipo de página precisa de seletores diferentes. Abra qualquer página, pressione F12 (ou clique com o botão direito → Inspecionar) e passe o cursor sobre um elemento para ver sua classe. O mapa abaixo é o que você usará nos Passos 3 e 4.
Página de resultados de busca
Cada resultado fica em um contêiner li.s-item, com o título, o preço e o link aninhados dentro. O parâmetro de URL _pgn controla a paginação: você vai precisar dele no Passo 3.
Página de anúncio de produto
Um anúncio individual expõe o preço em .x-price-primary, o título em h1 span e as especificações do item em linhas .ux-labels-values. A descrição longa fica em um iframe#desc_ifr separado: uma subpágina que você scrapearia com sua própria requisição.
| Dado | Seletor nos resultados | Seletor no anúncio |
|---|---|---|
| Contêiner | li.s-item |
— |
| Título | .s-item__title |
h1 span |
| Preço | .s-item__price |
.x-price-primary |
| Link / imagem | a.s-item__link / .s-item__image img |
— |
| Especificações | — | .ux-labels-values |
| Descrição | — | iframe#desc_ifr |
Passo 3 — Scrapeie os resultados de busca do eBay
O primeiro scraper recebe uma consulta de busca e devolve uma lista de itens com título, preço, URL e imagem: o ponto de entrada da maioria dos projetos de web scraping do eBay. Sem o cabeçalho User-Agent correto, o eBay devolve um 403 ou uma página vazia mesmo nesta requisição simples, então os cabeçalhos entram desde o início. Você pode adaptar o mesmo padrão para scrapear anúncios do eBay a partir de qualquer termo de busca.
import requests
from bs4 import BeautifulSoup
from urllib.parse import quote_plus
HEADERS = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
"Accept-Language": "en-US,en;q=0.9"}
def scrape_search(query, proxies=None):
"""Return a list of eBay search results for a query."""
url = f"https://www.ebay.com/sch/i.html?_nkw={quote_plus(query)}"
html = requests.get(url, headers=HEADERS, proxies=proxies, timeout=30)
soup = BeautifulSoup(html.text, "lxml")
items = []
for card in soup.select("li.s-item"):
title = card.select_one(".s-item__title")
price = card.select_one(".s-item__price")
link = card.select_one("a.s-item__link")
image = card.select_one(".s-item__image-wrapper img")
if not (title and link):
continue
items.append({
"title": title.get_text(strip=True),
"price": price.get_text(strip=True) if price else None,
"url": link["href"],
"image": image.get("src") if image else None,
})
return items
print(scrape_search("smartphone")[:3])
Lidando com a paginação
Uma única página de resultados não basta. Este laço percorre o parâmetro _pgn (página 2, 3… N) e para quando a lista de resultados está vazia ou você atinge um limite de páginas: a parte que a maioria dos tutoriais de eBay scraper em Python omite.
from urllib.parse import quote_plus
def scrape_all_pages(query, max_pages=10, proxies=None):
"""Page through results via the _pgn parameter until empty or max_pages."""
all_items = []
for page in range(1, max_pages + 1):
url = (f"https://www.ebay.com/sch/i.html?_nkw={quote_plus(query)}"
f"&_pgn={page}")
html = requests.get(url, headers=HEADERS, proxies=proxies, timeout=30)
soup = BeautifulSoup(html.text, "lxml")
cards = soup.select("li.s-item")
if not cards: # no more results -> stop
break
for card in cards:
title = card.select_one(".s-item__title")
link = card.select_one("a.s-item__link")
if title and link:
all_items.append({"title": title.get_text(strip=True),
"url": link["href"]})
return all_items
Passo 4 — Scrapeie as páginas de anúncio de produto
Quando você scrapeia anúncios do eBay um a um, parse_product(url) recebe a URL de um anúncio e devolve os campos estruturados: título, preço, condição, vendedor, frete e a tabela de especificações onde cada linha é um par chave/valor. É o núcleo do scraping de dados do eBay no nível do produto.
def parse_product(url, proxies=None):
"""Scrape a single eBay listing page."""
html = requests.get(url, headers=HEADERS, proxies=proxies, timeout=30)
soup = BeautifulSoup(html.text, "lxml")
def text(sel):
el = soup.select_one(sel)
return el.get_text(strip=True) if el else None
# item specifics live in a label/value table
specifics = {}
for row in soup.select(".ux-labels-values"):
label = row.select_one(".ux-labels-values__labels")
value = row.select_one(".ux-labels-values__values")
if label and value:
specifics[label.get_text(strip=True)] = value.get_text(strip=True)
return {
"title": text("h1 span"),
"price": text(".x-price-primary"),
"condition": text(".x-item-condition-text .ux-textspans"),
"seller": text(".x-sellercard-atf__info__about-seller"),
"shipping": text(".ux-labels-values--shipping .ux-textspans"),
"item_specifics": specifics,
}
Extraindo variantes de produto (dados MSKU)
O eBay guarda as opções de variante —cor, tamanho, armazenamento— não no HTML visível, mas dentro de um objeto JSON inline (MSKU) em uma tag <script>. Você localiza esse JSON e faz o parse com json.loads(). Sua chave e formato exatos mudam com o tempo, então trate o código abaixo como ponto de partida e ajuste ao que vir no DevTools: ele devolve as opções de variante (nome/valor), a parte que a maioria dos concorrentes omite.
import re, json
def parse_variants(url, proxies=None):
"""eBay keeps multi-SKU variant options (size, colour, storage) in an inline
JSON blob rather than visible HTML. The exact key and shape change over time,
so treat the pattern below as a starting point and adjust it to the page you
inspect in DevTools. This returns the option name/value pairs, not full
price-per-combination data."""
html = requests.get(url, headers=HEADERS, proxies=proxies, timeout=30).text
# find the inline state object, then load it with a balanced-brace slice
start = html.find('"MSKU"')
if start == -1:
return []
brace = html.find("{", start)
depth, end = 0, brace
for i in range(brace, len(html)):
depth += (html[i] == "{") - (html[i] == "}")
if depth == 0:
end = i + 1
break
try:
data = json.loads(html[brace:end])
except json.JSONDecodeError:
return []
return [{"name": v.get("displayName"), "value": v.get("valueName")}
for v in data.get("menuItemMap", {}).values()]
Passo 5 — Scrapeie as avaliações de produto do eBay
Ao scrapear avaliações do eBay, observe que são avaliações de produto/catálogo em uma URL separada, não em cada página de anúncio, e que nem todo anúncio as tem. Você deriva a URL de avaliações do ID de produto/catálogo do anúncio e então extrai nota, texto, data e autor. Trate o endpoint abaixo como ilustrativo e confirme o caminho atual no DevTools.
def scrape_reviews(item_id, proxies=None):
"""Reviews live on a separate URL keyed by the item ID."""
url = f"https://www.ebay.com/urw/{item_id}/product-reviews"
html = requests.get(url, headers=HEADERS, proxies=proxies, timeout=30)
soup = BeautifulSoup(html.text, "lxml")
reviews = []
for r in soup.select(".ebay-review-section"):
rating = r.select_one(".star-rating")
body = r.select_one(".review-item-content")
date = r.select_one(".review-item-date")
author = r.select_one(".review-item-author")
reviews.append({
"rating": rating.get("aria-label") if rating else None,
"text": body.get_text(strip=True) if body else None,
"date": date.get_text(strip=True) if date else None,
"author": author.get_text(strip=True) if author else None,
})
return reviews
Passo 6 — Exporte os dados para JSON e CSV
Salve seus resultados nos dois formatos ao mesmo tempo: JSON preserva a estrutura aninhada (útil para variantes e especificações), CSV é plano e abre direto no Excel ou Google Sheets.
import json, csv
results = scrape_search("smartphone")
# Option A - JSON (keeps nested structure)
with open("ebay.json", "w", encoding="utf-8") as f:
json.dump(results, f, indent=2, ensure_ascii=False)
# Option B - CSV (flat, opens in Excel/Sheets)
if results:
with open("ebay.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=results[0].keys())
writer.writeheader()
writer.writerows(results)
Passo 7 — Contorne as medidas anti-bot do eBay
O eBay defende os anúncios com um stack em camadas: pontuação de reputação de IP, limites de taxa (HTTP 429), bloqueios suaves (uma resposta 200 com resultado vazio) e CAPTCHAs. Você não vencerá tudo com um truque, mas os cabeçalhos e IPs certos resolvem os casos comuns para o web scraping do eBay em uma escala razoável.
Configure cabeçalhos adequados
Envie um conjunto completo e realista de cabeçalhos, não apenas um User-Agent. Cada cabeçalho reduz seu risco de bloqueio: a ausência de Accept-Language ou Referer é um sinal típico de bot. Para trabalhos maiores, rotacione o User-Agent a partir de uma lista.
HEADERS = {
# identify as a real desktop browser - eBay returns 403/empty without this
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
"Accept-Language": "en-US,en;q=0.9", # match the locale you target
"Accept-Encoding": "gzip, deflate, br", # accept compressed responses
"Referer": "https://www.ebay.com/", # look like in-site navigation
"Connection": "keep-alive",
}
# rotate the User-Agent across a list for larger jobs
import random
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 Version/17.0 Safari/605.1.15",
]
HEADERS["User-Agent"] = random.choice(USER_AGENTS)
Use proxies residenciais rotativos
Os cabeçalhos não bastam em volume: o eBay pontua a reputação de IP, e os proxies de data center são sinalizados muito mais rápido que os residenciais. Use IPs residenciais rotativos para scraping de busca em grande volume (um IP novo por requisição) e sessões sticky para fluxos de anúncio ou leilão onde importa uma sessão estável. Os proxies residenciais da DataImpulse custam US$ 1/GB, são pague-conforme-o-uso e se conectam direto na chamada ao requests.
# Route requests through DataImpulse rotating residential proxies
proxies = {
"http": "http://LOGIN:[email protected]:823",
"https": "http://LOGIN:[email protected]:823",
}
html = requests.get(url, headers=HEADERS, proxies=proxies, timeout=30)
Segmentação geográfica: scrapear dados do eBay por região
Os anúncios do eBay diferem por país: eBay.com, eBay.co.uk e eBay.de devolvem preços, disponibilidade e fretes diferentes para o mesmo produto. Para ver os dados que um comprador de um país específico vê, roteie por um proxy localizado lá. A DataImpulse tem IPs residenciais em 195 países, incluindo segmentação no nível de estado nos EUA, para que seu scraping de dados do eBay reflita o mercado certo. A mesma abordagem impulsiona a comparação de preços entre regiões.
Passo 8 — Escale seu scraper do eBay
Três níveis transformam o script básico em uma forma pronta para produção de fazer scraping do eBay com Python em escala, por ordem de complexidade crescente: um atraso aleatório entre requisições para não ultrapassar os limites de taxa, lógica de retry com backoff exponencial para absorver respostas 429/403, e coleta assíncrona com httpx + asyncio para volume em paralelo.
import time, random, asyncio, httpx
# 1) Basic pacing - a random delay between requests
def polite_get(url, **kw):
time.sleep(random.uniform(1, 3))
return requests.get(url, headers=HEADERS, timeout=30, **kw)
# 2) Retry with exponential backoff on 429/403
def get_with_retry(url, retries=4, **kw):
for attempt in range(retries):
r = requests.get(url, headers=HEADERS, timeout=30, **kw)
if r.status_code not in (429, 403):
return r
time.sleep(2 ** attempt + random.random()) # 1s, 2s, 4s, 8s ...
return r
# 3) Async collection for high volume (httpx >=0.28 uses proxy=, not proxies=)
async def scrape_many(urls, proxy=None):
async with httpx.AsyncClient(headers=HEADERS, proxy=proxy, timeout=30) as client:
tasks = [client.get(u) for u in urls]
return await asyncio.gather(*tasks)
Perguntas frequentes
É legal fazer scraping do eBay?
Depende. Scrapear dados publicamente disponíveis pode ser lícito em alguns contextos, mas o Acordo de Usuário do eBay restringe o acesso automatizado —bots, scrapers, ferramentas de mineração de dados— sem permissão prévia, então o scraping pode violar seus termos e gerar risco legal. Usar proxies em si é legal. Respeite os limites de taxa, não scrapeie atrás de um login, evite dados pessoais e busque aconselhamento jurídico para uso comercial. Isto é informação geral, não aconselhamento jurídico: veja nosso guia sobre se o web scraping é legal.
Preciso de proxies para scrapear o eBay?
Para algumas requisições, não. Para qualquer volume real, sim: o eBay pontua a reputação de IP e limita a taxa com rigor, então um único IP é bloqueado rápido. Os proxies residenciais rotativos distribuem as requisições por muitos IPs e permitem segmentar países específicos para preços corretos por região.
Como scrapeio as variantes de produto do eBay?
Variantes como tamanho e cor não estão no HTML visível: o eBay as guarda em um objeto JSON inline (MSKU) dentro de uma tag script. Localize esse objeto com re.search() e faça o parse com json.loads(), como no Passo 4.
Por que meu scraper do eBay devolve resultados vazios?
Quase sempre são os cabeçalhos. Sem um User-Agent de navegador real (e idealmente Accept-Language e Referer), o eBay frequentemente devolve um 403 ou um 200 com corpo vazio. Adicione o conjunto completo de cabeçalhos do Passo 7; se persistir, você provavelmente está limitado por taxa ou sinalizado por IP, então reduza o ritmo e roteie por um proxy residencial.
Como lido com a paginação do eBay em Python?
Incremente o parâmetro de URL _pgn (&_pgn=2, 3…) em um laço e pare quando o contêiner de resultados (li.s-item) estiver vazio ou você atingir um limite de páginas, como na função de paginação do Passo 3.
Conclusão
É assim que se faz scraping do eBay com Python de ponta a ponta: scrapers de resultados e de anúncios, paginação, extração de variantes ocultas, avaliações, exportação JSON/CSV e os cabeçalhos mais os proxies residenciais rotativos que mantêm seu setup de eBay scraper em Python funcionando. Comece pequeno com o scraper de busca e adicione depois paginação, cabeçalhos anti-bot e escalonamento conforme seu volume cresce. Para os IPs certos, veja melhores proxies para scraping do eBay.
Última atualização: 24 de junho de 2026.
