🎯 Um navegador em nuvem personalizável e anti-detecção alimentado por Chromium desenvolvido internamente, projetado para rastreadores web e agentes de IA. 👉Experimente agora
De volta ao blog

Como Construir um Web Scraper: Um Guia em Python do Zero

Emily Chen
Emily Chen

Advanced Data Extraction Specialist

29-Jun-2026

TL;DR:

  • Um web scraper faz três coisas: busca uma página, extrai campos do HTML, repete para a próxima página. Uma vez que esses três movimentos se acertam, todo scraper que você construir depois é uma variação do mesmo loop.
  • requests mais BeautifulSoup tratam da web estática em menos de 30 linhas. No site de prática books.toscrape.com, um loop percorre todas as 50 páginas de listagem e retorna 1.000 registros de livros estruturados.
  • Paginação é apenas seguir o link "próximo" até que ele deixe de aparecer. Você lê o âncora da próxima página em cada página, resolve para uma URL absoluta e continua até que o link desapareça.
  • HTTP simples não consegue ver conteúdo renderizado por JavaScript. A página de prática quotes.toscrape.com/js/ retorna zero elementos de citações através de requests, porque a marcação é pintada do lado do cliente após o carregamento da página.
  • Scrapeless Scraping Browser renderiza essas páginas do lado da nuvem e devolve o DOM pintado. A mesma página JavaScript retorna 10 elementos de citação uma vez que um navegador em nuvem a executa, então sua análise existente com BeautifulSoup continua funcionando inalterada.
  • Gratuito para começar. Novas contas da Scrapeless incluem tempo de execução gratuito do Scraping Browser — inscreva-se em app.scrapeless.com.

Introdução: construa um scraper real, depois lide com as páginas que resistem

Todo web scraper faz as mesmas três coisas: busca uma página, extrai os campos que você deseja do HTML e passa para a próxima página. As bibliotecas mudam, os sites mudam, mas esse loop não. Aprenda uma vez em uma página que é segura para acessar, e o padrão se transfere para quase qualquer coisa que você scrape depois.

A fricção aparece mais tarde. O primeiro scraper que a maioria das pessoas escreve funciona perfeitamente em uma página HTML estática e então retorna uma casca vazia no próximo site que tentam, porque esse site renderiza seu conteúdo com JavaScript após a resposta inicial. HTTP simples baixa os bytes que o servidor envia; não executa a página. Assim, os dados que você pode ver em seu navegador não estão na resposta que seu scraper recebe.

Este guia constrói tudo do zero em Python. O nível estático usa requests e BeautifulSoup contra um sandbox de extração de catálogo de livros e um sandbox de extração de citações — dois sites que existem especificamente para serem scrapingados. Então, ele se move para o limite honesto: quando o HTTP simples retorna uma página vazia, o trabalho escala para o Scrapeless Scraping Browser, um navegador em nuvem que renderiza a página e retorna o mesmo DOM que você analisaria localmente. Se você preferir trabalhar em JavaScript, a mesma divisão estática versus dinâmica é coberta no tutorial de Cheerio e Puppeteer.


O Que Você Pode Fazer Com Isso

  • Coletar catálogos de produtos. Percorra uma listagem paginada e extraia título, preço e estado do estoque para um arquivo CSV ou JSON.
  • Acompanhar preços ao longo do tempo. Execute o mesmo scraper em um cronograma e compare os números para observar quedas.
  • Construir conjuntos de dados para análise ou IA. Transforme páginas de HTML não estruturado em linhas limpas que seu caderno ou modelo podem ler.
  • Monitorar disponibilidade. Verifique se os itens estão em estoque em várias páginas sem precisar clicar nelas manualmente.
  • Alimentar um pipeline de pesquisa. Extraia citações, artigos ou avaliações em um armazenamento estruturado para processamento posterior.
  • Acessar páginas renderizadas por JavaScript. Escale as páginas que retornam uma casca vazia através do HTTP simples para um navegador em nuvem e mantenha o mesmo código de análise.

Por Que Scrapeless Scraping Browser

O Scrapeless Scraping Browser é um navegador em nuvem personalizável e anti-detecção, projetado para rastreadores web e agentes de IA. Para um scraper feito do zero especificamente, ele resolve o único problema que requests não consegue — executar a página — e traz:

  • Renderização de JavaScript do lado da nuvem. Páginas que pintam seu conteúdo do lado do cliente retornam um DOM totalmente renderizado, então o BeautifulSoup analisa o resultado exatamente como faria em uma página estática.
  • Proxies residenciais em mais de 195 países. Defina a geografia de saída com um código de país, para que uma página retorne o mesmo conteúdo que um visitante local veria.
  • Impressão digital anti-detecção. O navegador em nuvem apresenta uma superfície de navegador consistente e semelhante a um humano em vez de uma assinatura de cliente HTTP nu.
  • Uma chave de API para tudo. O SDK Python cria um browser_ws_endpoint ao qual você se conecta com Playwright; a mesma chave cobre o tempo de execução.

Obtenha sua chave de API no plano gratuito em app.scrapeless.com.


Pré-requisitos

  • Python 3.10 ou mais recente
  • requests e beautifulsoup4 para o nível estático
  • O SDK scrapeless e playwright para o nível renderizado por JavaScript
  • Uma conta e chave API do Scrapeless — inscreva-se em app.scrapeless.com
  • Familiaridade básica com o terminal

Instalação

Instale primeiro as duas bibliotecas de camada estática:

bash Copy
pip install requests beautifulsoup4

requests busca páginas via HTTP; beautifulsoup4 analisa o HTML retornado — marcação definida pelo padrão HTML — em uma árvore que você pode consultar com seletores CSS. Esse par é suficiente para cada página estática nos Passos 1 a 4. A camada renderizada em JavaScript no Passo 5 adiciona mais dois pacotes, instalados lá.


Passo 1 — Busque uma página e confirme que você obteve HTML real

Um scraper começa com uma requisição. Envie um GET para a URL-alvo seguindo semântica HTTP, verifique o código de status e confirme que o HTML realmente contém os registros que você espera antes de escrever um único seletor.

python Copy
import requests
from bs4 import BeautifulSoup

url = "http://books.toscrape.com/"
resp = requests.get(url, headers={"User-Agent": "Mozilla/5.0 (tutorial-scraper)"}, timeout=30)
print("status:", resp.status_code, "bytes:", len(resp.text))

soup = BeautifulSoup(resp.text, "html.parser")
books = soup.select("article.product_pod")
print("cartões de livros nesta página:", len(books))

No books.toscrape.com, isso imprime status: 200 e cartões de livros nesta página: 20. O cabeçalho User-Agent identifica seu cliente; muitos servidores recusam requisições que não enviam nenhum. Se a contagem voltar como zero em um site real, esse é o sinal precoce de que a página é renderizada em JavaScript — a situação que o Passo 5 lida.


Passo 2 — Descubra o registro e depois extraia seus campos

Escolha primeiro o contêiner repetitivo, depois leia os campos dentro dele. Prefira um gancho estável — uma tag semântica, um atributo data-* ou uma classe que descreve os dados — em vez de uma classe hash que quebra na próxima reformulação. No books.toscrape.com, cada livro é um article.product_pod, e os campos se penduram em seletores filhos previsíveis.

python Copy
import requests
from bs4 import BeautifulSoup

resp = requests.get(
    "http://books.toscrape.com/",
    headers={"User-Agent": "Mozilla/5.0 (tutorial-scraper)"},
    timeout=30,
)
resp.encoding = resp.apparent_encoding  # o catálogo usa £; permite que requests o detecte
soup = BeautifulSoup(resp.text, "html.parser")

pod = soup.select_one("article.product_pod")
title = pod.h3.a["title"]
price = pod.select_one("p.price_color").get_text(strip=True)
in_stock = "Em estoque" in pod.select_one("p.instock.availability").get_text()
print(title, "|", price, "|", "em estoque" if in_stock else "fora de estoque")

Isso imprime A Light in the Attic | £51.77 | em estoque. A linha resp.encoding = resp.apparent_encoding é importante: sem ela, o símbolo da libra decodifica como mojibake, porque os cabeçalhos da resposta e a codificação real do documento discordam. Leia o campo do elemento que já o contém — o título vive no atributo title do link, então um seletor faz tanto a descoberta quanto a extração.


Uma lista raramente cabe em uma única página. O padrão confiável é ler o link da próxima página em cada página, resolvê-lo em relação à URL atual e repetir até não haver mais nenhum link próximo. Sem contador de páginas para adivinhar, sem intervalo codificado para manter.

python Copy
import requests
from urllib.parse import urljoin
from bs4 import BeautifulSoup

session = requests.Session()
session.headers.update({"User-Agent": "Mozilla/5.0 (tutorial-scraper)"})

records = []
url = "http://books.toscrape.com/catalogue/page-1.html"
pages = 0
while url:
    resp = session.get(url, timeout=30)
    resp.encoding = resp.apparent_encoding
    soup = BeautifulSoup(resp.text, "html.parser")
    for pod in soup.select("article.product_pod"):
        records.append({
            "title": pod.h3.a["title"],
            "price": pod.select_one("p.price_color").get_text(strip=True),
            "in_stock": "Em estoque" in pod.select_one("p.instock.availability").get_text(),
        })
    pages += 1
    next_link = soup.select_one("li.next > a")
    url = urljoin(url, next_link["href"]) if next_link else None

print("páginas rastreadas:", pages, "| registros coletados:", len(records))

No books.toscrape.com, isso percorre todas as 50 páginas da lista e coleta 1.000 registros. Uma requests.Session reutiliza a conexão subjacente entre as requisições, o que é mais rápido do que um novo requests.get cada vez. urljoin transforma o href relativo (como page-2.html) em uma URL absoluta para que a próxima requisição se resolva corretamente.

Obtenha sua chave API no plano gratuito: app.scrapeless.com


Passo 4 — Escreva a saída estruturada

Os dados raspados só são úteis uma vez que estão estruturados. A lista records do Passo 3 já é uma lista de dicionários com chaves consistentes, então escrevê-la em CSV ou JSON é feito em poucas linhas. Decida seu esquema de antemão — as mesmas chaves em cada linha — e campos ausentes tornam-se um None, nunca uma falha.

python Copy
import csv
import json

# records é a lista de dicionários construída no Passo 3.
with open("livros.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["título", "preço", "em_estoque"])
    writer.writeheader()
    writer.writerows(records)

with open("livros.json", "w", encoding="utf-8") as f:
    json.dump(records, f, ensure_ascii=False, indent=2)

print("escreveu", len(records), "linhas em livros.csv e livros.json")

Esse é um raspador completo e funcional: busca, descobre, extrai, pagina, armazena. Ele lidará com qualquer site HTML estático com os seletores trocados pela marcação desse site.


Onde o HTTP simples para: Páginas renderizadas em JavaScript

O raspador acima quebra no momento em que um site renderiza seu conteúdo com JavaScript. A página prática quotes.toscrape.com/js/ foi criada exatamente para esta lição — as citações são injetadas por um script após o carregamento da página, não estão presentes no HTML inicial.

python Copy
import requests
from bs4 import BeautifulSoup

resp = requests.get("https://quotes.toscrape.com/js/",
                    headers={"User-Agent": "Mozilla/5.0"}, timeout=30)
soup = BeautifulSoup(resp.text, "html.parser")
print("bytes:", len(resp.text), "| elementos de citação:", len(soup.select("div.quote")))

Isso retorna bytes: 5806 e elementos de citação: 0. A requisição foi bem-sucedida — status 200, bytes reais — mas a página analisada não tem citações, porque requests nunca executa o JavaScript que as cria. Nenhuma modificação no seletor conserta isso; os dados realmente não estão na resposta. A página deve ser executada por um navegador.


Passo 5 — Renderize a página com Scrapeless Scraping Browser

Executar um navegador completo localmente para cada página é pesado e é bloqueado rapidamente. O caminho mais limpo é um navegador na nuvem: Scrapeless cria uma sessão, renderiza a página do lado dele e devolve o DOM final — que você analisa com o mesmo código BeautifulSoup que já escreveu. Instale os dois pacotes adicionais e, em seguida, conecte-se ao Playwright através do Protocolo DevTools do Chrome.

bash Copy
pip install scrapeless playwright
playwright install chromium

playwright install chromium baixa um cliente de protocolo local uma única vez; a renderização real ainda ocorre na nuvem do Scrapeless. Exporte sua chave primeiro (export SCRAPELESS_API_KEY=seu_token_de_api_aqui), então:

python Copy
from scrapeless import Scrapeless
from scrapeless.types import ICreateBrowser
from playwright.sync_api import sync_playwright
from bs4 import BeautifulSoup

client = Scrapeless()  # lê SCRAPELESS_API_KEY do ambiente
session = client.browser.create(
    ICreateBrowser(proxy_country="US", session_ttl=180)
)

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(session.browser_ws_endpoint)
    ctx = browser.contexts[0] if browser.contexts else browser.new_context()
    page = ctx.pages[0] if ctx.pages else ctx.new_page()
    page.goto("https://quotes.toscrape.com/js/", wait_until="domcontentloaded", timeout=60_000)
    page.wait_for_timeout(4_000)  # permita que o script do lado do cliente pinte as citações
    html = page.content()
    browser.close()

soup = BeautifulSoup(html, "html.parser")
quotes = soup.select("div.quote")
print("bytes renderizados:", len(html), "| elementos de citação:", len(quotes))
print(quotes[0].select_one("small.author").get_text(strip=True))

Isso retorna bytes renderizados: 9246 e elementos de citação: 10, e imprime Albert Einstein — a mesma página que deu zero pelo HTTP simples. session.browser_ws_endpoint é uma URL wss://browser.scrapeless.com/...; connect_over_cdp do Playwright fala o Protocolo DevTools para ela, então a página roda na nuvem enquanto seu código roda localmente. wait_until="domcontentloaded" mais um breve tempo de espera fixo é mais confiável do que esperar pelo ocioso da rede em uma página dirigida por script. A SDK e a superficie da CLI estão documentadas em docs.scrapeless.com.


O Que Você Recebe de Volta

Uma vez que o navegador na nuvem retorna HTML renderizado, a análise é idêntica ao nível estático — os mesmos seletores, a mesma forma de dicionário:

json Copy
[
  {
    "texto": "O mundo como o criamos é um processo do nosso pensamento.",
    "autor": "Albert Einstein",
    "tags": ["mudança", "pensamentos profundos", "pensando", "mundo"]
  }
]
// O esquema reflete exatamente o que a análise div.quote emite. Os valores são exemplos ilustrativos.

Algumas observações honestas sobre páginas renderizadas:

  • O tempo é importante em páginas renderizadas. As citações aparecem apenas depois que o script da página é executado, então o curto intervalo após domcontentloaded é o que as faz aparecer. Muito curto e você analisa uma página vazia.
  • Os seletores ainda mudam. div.quote, span.text e small.author são a marcação deste sandbox; em um site real, verifique novamente os seletores quando a página mudar.
  • Campos ausentes são normais. Um registro pode estar faltando uma etiqueta ou um preço; trate cada campo como anulável e seu escritor continuará produzindo linhas limpas.
  • Fixe sua saída. proxy_country="US" mantém o conteúdo renderizado consistente com um visitante dos EUA; mude o código para combinar com a região que você precisa.

Conclusão: de uma página a um pipeline real

Um scraper funcional se resume a quatro etapas: buscar a página, descobrir o registro repetido, extrair seus campos e seguir a paginação até o fim. Isso cobre a web estática com requests e BeautifulSoup. O único caso que ele não pode alcançar — páginas que constroem seu conteúdo com JavaScript — é escalonado de forma limpa para o Scrapeless Scraping Browser, que renderiza a página na nuvem e devolve o mesmo DOM que seu parser já entende.

A partir daqui, escale da mesma forma que qualquer scraper de produção: mantenha seus seletores precisos e verifique-os novamente quando a marcação mudar, fixe a geografia de saída para combinar com o público que a página atende, trate campos ausentes como anuláveis e recorra ao navegador em nuvem apenas nas páginas que realmente precisam de renderização. Para a mesma divisão em JavaScript, o guia Cheerio e Puppeteer aborda a decisão estática versus dinâmica no Node.js. Compare opções de tempo de execução na página de preços quando estiver pronto para executá-lo em volume.


Pronto para construir seu pipeline de dados alimentado por IA?

Junte-se à nossa comunidade para reivindicar um plano gratuito e conecte-se com desenvolvedores que estão construindo pipelines de scraping: Discord · Telegram.

Inscreva-se em app.scrapeless.com para um tempo de execução gratuito do Scraping Browser e adapte os padrões acima aos sites, páginas e regiões que seu scraper precisa.


Perguntas Frequentes

Q: O scraping na web é legal?
Scraping de dados publicamente visíveis é amplamente permitido em muitas jurisdições, mas as regras variam por país e por site. Revise os Termos de Serviço do site de destino e o Protocolo de Exclusão de Robôs, evite dados pessoais ou restritos e consulte um advogado para qualquer coisa comercial. Os sites práticos neste guia existem especificamente para serem raspados.

Q: Preciso de um proxy para construir um scraper?
Para um scraper de baixo volume contra um site permissivo, não. Para sites que limitam a taxa por IP ou variam o conteúdo por região, sim — faça a rota através de proxies residenciais e fixe o país para que a página retorne o conteúdo que um visitante local veria. O nível do Scraping Browser na Etapa 5 inclui saída de proxy com a configuração proxy_country.

Q: Por que meu scraper retorna uma página vazia?
Quase sempre porque a página renderiza seu conteúdo com JavaScript após a resposta inicial. requests simples baixa os bytes do servidor, mas nunca executa os scripts da página, então os dados que você vê em um navegador não estão na resposta. Confirme imprimindo a contagem de elementos, como na seção "Onde o HTTP simples para", e então renderize a página com um navegador em nuvem.

Q: Como lido com um site que mostra "Acesso Negado" ou uma página de desafio?
Isso é uma verificação anti-bot na solicitação. Renderize a página através do Scrapeless Scraping Browser com a saída residencial dos EUA fixada e aqueça a sessão carregando a página inicial do site primeiro na mesma sessão do navegador antes de acessar a página alvo, para que a solicitação carregue uma superfície de navegador consistente e parecida com a humana.

Q: Meus seletores pararam de funcionar após um redesign — e agora?
A marcação muda. Reinspecione a página, prefira ganchos estáveis (tags semânticas, atributos data-*, classes que descrevem os dados) em vez de classes CSS criptografadas e atualize os seletores. Construir contra o gancho mais durável disponível mantém o scraper ativo por mais tempo entre correções.

Q: Quantas páginas posso raspar em paralelo?
Mantenha a concorrência modesta — cerca de três trabalhadores por host é um teto razoável para um único site, para que você permaneça dentro das taxas de solicitação respeitosas e evite ultrapassar os limites de taxa. As sessões de navegador em nuvem são mais escassas do que as solicitações HTTP, então limite-as mais rigorosamente do que suas buscas estáticas.

Q: Eu preciso usar um navegador para tudo?
Não, e você não deve. O nível estático com requests e BeautifulSoup é mais rápido e barato, então use-o para todas as páginas que enviam HTML renderizado. Escale para o navegador em nuvem apenas para as páginas que realmente precisam da execução de JavaScript.

Na Scorretless, acessamos apenas dados disponíveis ao público, enquanto cumprem estritamente as leis, regulamentos e políticas de privacidade do site aplicáveis. O conteúdo deste blog é apenas para fins de demonstração e não envolve atividades ilegais ou infratoras. Não temos garantias e negamos toda a responsabilidade pelo uso de informações deste blog ou links de terceiros. Antes de se envolver em qualquer atividade de raspagem, consulte seu consultor jurídico e revise os termos de serviço do site de destino ou obtenha as permissões necessárias.

Artigos mais populares

Catálogo