Saltar para o conteúdo
Comece grátis
Voltar ao blogue

Web scraping com Ruby: Faraday, Nokogiri, Selenium

Raluca PenciucÚltima atualização em 19 min read
Web scraping com Ruby: Faraday, Nokogiri, Selenium
Resumo: Comece a fazer web scraping com Ruby, descobrindo onde os dados se encontram e, em seguida, escolha a ferramenta mais simples que consiga aceder aos mesmos. Utilize o Faraday e o Nokogiri para o HTML já presente na resposta, transfira um esquema de registos validado para CSV e adicione o Selenium com o Capybara apenas para a renderização ou interação necessárias. O resultado é um scraper cuja camada de pedidos pode mudar sem invalidar a análise, a validação ou a exportação.

O Web Scraping com Ruby é o processo de solicitar conteúdo da Web, localizar valores úteis no HTML ou num documento renderizado pelo navegador e converter esses valores em registos estruturados com código Ruby. A abordagem mais fácil de manter consiste em utilizar a combinação de ferramentas mais leve capaz de aceder aos dados de forma fiável.

Isso significa, normalmente, utilizar o Faraday para o transporte HTTP e o Nokogiri para a análise. São rápidos a iniciar, fáceis de testar e suficientes sempre que a resposta do servidor contém os campos pretendidos. Se o conteúdo for criado em JavaScript ou se um fluxo de trabalho permitido exigir interação, o Selenium e o Capybara oferecem uma alternativa baseada no navegador, mas também implicam um maior consumo de recursos e mais possibilidades de falha.

Este tutorial constrói um fluxo de trabalho progressivo de web scraping em Ruby em torno de um catálogo local estável. Irá obter HTML com tempos de espera explícitos e verificações de resposta, extrair um formato de registo consistente, normalizar URLs e preços, validar linhas e exportar um CSV determinístico. Em seguida, irá adicionar novas tentativas limitadas, controlo de ritmo, testes de seletores, esperas adaptadas ao navegador, credenciais protegidas e concorrência limitada. O objetivo não é um rastreador com uma estrutura pesada. Trata-se de um pequeno scraper web em Ruby cujo comportamento permanece compreensível quando o alvo, a rede ou o método de acesso mudam.

No final, output/products.csv terá cabeçalhos previsíveis e uma linha por produto validado. O mesmo extrator aceitará código de marcação descarregado ou renderizado pelo navegador, o que torna a escolha da ferramenta reversível. Nos casos em que a gem, o navegador, a forma de empacotamento ou os detalhes legais possam mudar, verifique-os em relação à documentação oficial atual. Isso mantém o tutorial reproduzível enquanto adapta os seletores a um alvo aprovado.

Escolha a stack de scraping em Ruby mais leve (Web Scraping com Ruby)

A stack predefinida para Web Scraping com Ruby deve ser um cliente HTTP juntamente com um analisador HTML. Se o corpo da resposta já contiver os registos de que necessita, o Faraday pode tratar do transporte e o Nokogiri pode tratar da extração com muito menos sobrecarga do que um navegador. Recorra ao Selenium e ao Capybara apenas quando os dados forem apresentados após a execução de JavaScript ou quando o fluxo de trabalho exigir genuinamente cliques, deslocamento ou preenchimento de formulários.

Mantenha essas responsabilidades separadas. Uma fetch_html(url) função deve devolver HTML, enquanto parse_records(html, page_url:) não deve saber nada sobre proxies, tentativas de repetição ou navegadores. Essa separação permite que um pequeno scraper web em Ruby evolua sem transformar cada alteração na estratégia de acesso numa reescrita do analisador.

Verifique o HTML bruto, os dados incorporados e o DOM renderizado

Antes de escolher gems, localize os dados. Faça o pedido da URL com o Faraday ou curl, guarde o corpo da resposta e procure um título ou identificador conhecido. No navegador, compare a opção «Ver Código-fonte» com o painel «Elementos» e, em seguida, inspecione o painel «Rede» enquanto a página carrega.

Utilize esta ordem de diagnóstico:

  1. HTML renderizado pelo servidor: o valor encontra-se no corpo da resposta, por isso utilize o Faraday e o Nokogiri.
  2. Dados incorporados: uma tag de script contém JSON ou um estado serializado; por isso, analise essa carga diretamente quando for permitido.
  3. Resposta em segundo plano: um pedido XHR ou fetch autorizado devolve os registos, que podem ser mais claros do que a marcação renderizada.
  4. Apenas DOM renderizado: o JavaScript constrói os elementos, pelo que a utilização de um navegador sem interface gráfica pode justificar-se.

Esta sequência evita que a automação do navegador se torne a resposta automática para todos os seletores em falta.

Adapte a tarefa ao Faraday, HTTParty, Nokogiri, Selenium, Capybara ou Kimurai

Ferramenta

Melhor opção

Principal compromisso

Faraday

Pedidos HTTP configuráveis, middleware, adaptadores e tratamento explícito de erros

Configuração ligeiramente mais complexa do que um cliente de uma única chamada

HTTParty

Pequenos scripts que beneficiam de uma API de pedidos concisa

Menos separação se a lógica de pedidos crescer dentro das classes do scraper

Nokogiri

Análise rápida de HTML ou XML com seletores CSS e XPath

Não executa JavaScript

Selenium

Controlo direto do navegador para páginas renderizadas ou interativas

Mais memória, tempo de arranque e complexidade dos controladores

Capybara

Interações de nível superior com o navegador, com espera automática

Adiciona uma camada de abstração que é desnecessária para páginas estáticas

Kimurai

Uma estrutura do tipo «crawler» que vale a pena avaliar para trabalhos de scraping em Ruby de maior dimensão

Mais estrutura e componentes móveis do que este fluxo de trabalho específico necessita

Para este tutorial, o Faraday com o Nokogiri é a opção principal, enquanto o Selenium com o Capybara é a alternativa. O scraping com o HTTParty continua a ser uma alternativa razoável quando a sua equipa prefere a sua API. Uma comparação entre analisadores de HTML e XML em Ruby é útil se a sua carga de trabalho for além dos documentos HTML comuns.

Crie um scraper estático, desde o pedido até ao ficheiro CSV

Utilize um fixture local como alvo de teste estável, em vez de uma página de comércio eletrónico em constante mudança. Crie fixtures/catalog.html com dois ou mais cartões de produto e, em seguida, disponibilize-a em ruby -run -e httpd fixtures -p 8000. O URL é http://127.0.0.1:8000/catalog.html.

Utilize um único esquema ao longo de todo o exemplo: id, title, price_text, price_cents, e url. Manter o preço bruto a par do número inteiro normalizado preserva a evidência quando for necessário rever as regras de análise.

<section id="catalog">
  <article class="product-card" data-product-id="sku-101">
    <a class="product-link" href="/products/steel-mug">
      <h2 class="product-title">Steel Mug</h2>
    </a>
    <span class="product-price">$18.50</span>
  </article>
</section>

Configure um pequeno projeto Bundler

Crie um diretório, inicialize o Bundler e adicione apenas as bibliotecas utilizadas pelo caminho escolhido:

mkdir ruby_scraper && cd ruby_scraper
bundle init
mkdir -p fixtures test output
touch scraper.rb
# Gemfile
source "https://rubygems.org"

gem "faraday"
gem "faraday-follow_redirects"
gem "nokogiri"
gem "selenium-webdriver"
gem "capybara"
gem "csv"

O ficheiro Gemfile inclui explicitamente o csv para que o exemplo se mantenha estável em todas as instalações de Ruby suportadas; verifique todas as versões das gems em relação à versão de referência do Ruby utilizada pelo seu projeto.

Recuperar HTML com segurança com o Faraday

Um pedido orientado para a produção tem um User-Agent reconhecível, ligação limitada e tempos de espera de leitura, um limite de redirecionamentos deliberado e verificações que rejeitam páginas de erro antes da análise. A configuração seguinte é intencionalmente pequena:

require "faraday"
require "faraday/follow_redirects"

TARGET_URL = "http://127.0.0.1:8000/catalog.html"

HTTP = Faraday.new do |f|
  f.response :follow_redirects, limit: 3
  f.options.open_timeout = 5
  f.options.timeout = 15
  f.headers["User-Agent"] =
    "RubyCatalogCollector/1.0 (contact: developer@example.com)"
  f.adapter Faraday.default_adapter
end

def fetch_html(url)
  response = HTTP.get(url)

  unless response.success?
    raise "HTTP #{response.status} for #{url}"
  end

  content_type = response.headers["content-type"].to_s.downcase
  unless content_type.include?("text/html")
    raise "Expected HTML, received #{content_type.inspect}"
  end

  response.body
end

Substitua o valor de contacto de exemplo por um contacto operacional real se o destino exigir identificação. Não analise silenciosamente uma 403, 429, uma página de início de sessão ou um corpo de erro JSON como se fosse HTML de catálogo. Registe o estado, o anfitrião, o número da tentativa e uma breve descrição da classe de erro, mas evite divulgar credenciais ou corpos de resposta completos.

O web scraping com Ruby e Faraday é útil neste contexto, uma vez que a política de ligação permanece visível e testável. Com o HTTParty, aplique os mesmos conceitos: defina cabeçalhos, configure tempos de espera, decida como os redirecionamentos são tratados e valide tanto o estado como o tipo de conteúdo. Um guia mais aprofundado sobre cabeçalhos HTTP para web scraping pode ajudar quando um servidor varia as respostas consoante os formatos aceites ou a identidade do cliente.

Inspecione a marcação e extraia registos com o Nokogiri

Utilize o DevTools para identificar o menor contêiner que representa um registo e, em seguida, consulte os campos relativos a esse contêiner. Os seletores com escopo evitam que um preço na navegação ou no rodapé seja associado ao título de um produto. Prefira atributos duradouros, como data-product-id, nomes de elementos semânticos ou padrões de ligação estáveis em vez de classes que apenas descrevem espaçamento ou cor.

require "nokogiri"
require "uri"

class ParseError < StandardError; end

def clean_text(node)
  node&.text&.gsub(/\s+/, " ")&.strip
end

def absolute_url(base_url, href)
  return nil if href.to_s.strip.empty?

  URI.join(base_url, href).to_s
rescue URI::InvalidURIError
  nil
end

def parse_records(html, page_url:)
  document = Nokogiri::HTML(html)
  cards = document.css("#catalog .product-card")
  raise ParseError, "No product cards matched" if cards.empty?

  cards.map do |card|
    link = card.at_css("a.product-link")

    {
      id: card["data-product-id"]&.strip,
      title: clean_text(card.at_css(".product-title")),
      price_text: clean_text(card.at_css(".product-price")),
      price_cents: nil,
      url: absolute_url(page_url, link&.[]("href"))
    }
  end
end

O operador «safe-navigation» evita que um nó opcional interrompa toda a execução, mas não deve transformar dados obrigatórios em nil . A validação na etapa seguinte determina quais os campos que são obrigatórios. Se todos os cartões desaparecerem, o analisador gera um erro imediatamente, pois isso geralmente indica uma marcação alterada, HTML inesperado ou a página errada.

Os seletores CSS são normalmente mais fáceis para marcação em forma de componentes. O XPath torna-se útil quando a seleção depende de relações entre documentos, espaços de nomes mistos ou condições orientadas para texto que o CSS não consegue expressar de forma clara. Um guia comparativo entre seletores XPath e CSS é um companheiro útil quando os seletores vão além de simples descendentes e atributos.

Este é o cerne da extração de dados da Web com o Nokogiri: descarregar bytes separadamente, analisar uma única vez, delimitar o âmbito de cada registo e converter links relativos com URI.join. A mesma parse_records função pode, posteriormente, aceitar HTML capturado por um navegador.

Normalizar valores e gerar uma saída CSV determinística

Normalize apenas de acordo com um contrato de dados declarado. O analisador de preços abaixo assume um ponto como separador decimal e vírgulas opcionais como separadores de milhares. Para outras configurações regionais, adicione uma regra específica da localização em vez de adivinhar. Mantenha price_text para que possa auditar qualquer conversão.

require "bigdecimal"
require "csv"

HEADERS = %w[id title price_text price_cents url].freeze

def price_to_cents(raw)
  number = raw.to_s.match(/-?\d[\d,]*(?:\.\d{1,2})?/)&.to_s
  return nil unless number

  (BigDecimal(number.delete(",")) * 100).round.to_i
end

def prepare_records(records)
  normalized = records.map do |record|
    record.merge(price_cents: price_to_cents(record[:price_text]))
  end

  valid = normalized.select do |record|
    record[:id] && !record[:title].to_s.empty? &&
      record[:price_cents] && record[:url]
  end

  valid.uniq { |record| record[:id] }
end

def write_csv(records, path)
  CSV.open(path, "w", write_headers: true, headers: HEADERS,
           encoding: "UTF-8") do |csv|
    records.sort_by { |record| record[:id] }.each do |record|
      csv << HEADERS.map { |field| record[field.to_sym] }
    end
  end
end

A ordenação e os cabeçalhos fixos tornam as comparações repetíveis. A prevenção de duplicados deve utilizar a chave mais estável disponível, normalmente um identificador de origem ou um URL canónico, e não um título mutável. Emita contagens para registos analisados, válidos, rejeitados e duplicados, para que uma execução «bem-sucedida» não possa ocultar uma saída cada vez menor.

Reforce o scraper para execuções repetidas

Um script pontual pode falhar de forma evidente e ser executado novamente manualmente. Uma tarefa agendada necessita de limites previsíveis, contexto suficiente para diagnosticar falhas e testes que distingam um problema de rede de um seletor avariado. Para o Web Scraping com Ruby, a fiabilidade começa por tornar cada nova tentativa finita e cada resultado vazio explicável. Defina um relatório de execução com URLs solicitadas, recuperações bem-sucedidas, linhas analisadas, linhas rejeitadas, duplicados e falhas. Esses contadores revelam desvios antes que os consumidores a jusante o façam.

Lidar com falhas, novas tentativas e regulação do ritmo

Classifique as falhas antes de repetir a tentativa. Erros de DNS, reinicializações de ligação, tempos de espera, 429e as respostas selecionadas 5xx são frequentemente transitórios. A maioria das 4xx respostas são permanentes para o pedido atual e devem ser consideradas falhas sem uma enxurrada de tentativas de repetição. Erros de autenticação, URLs malformados e falhas de análise precisam de correção, não de recuo.

TRANSIENT_STATUS = [429, 500, 502, 503, 504].freeze
TRANSIENT_ERRORS = [Faraday::TimeoutError,
                    Faraday::ConnectionFailed].freeze

def retry_delay(response, attempt)
  header = response&.headers&.fetch("retry-after", nil)
  return header.to_i if header&.match?(/\A\d+\z/)

  [0.5 * (2**(attempt - 1)) + rand * 0.25, 8].min
end

def fetch_with_retry(url, max_attempts: 4)
  1.upto(max_attempts) do |attempt|
    response = nil

    begin
      response = HTTP.get(url)
      if response.success?
        type = response.headers["content-type"].to_s
        return response.body if type.include?("text/html")

        raise "Unexpected content type #{type.inspect}"
      end

      raise "Permanent HTTP #{response.status}" unless
        TRANSIENT_STATUS.include?(response.status)
    rescue *TRANSIENT_ERRORS => error
      raise if attempt == max_attempts
      warn "fetch_failed host=#{URI(url).host} attempt=#{attempt} " \
           "error=#{error.class}"
    end

    raise "Retries exhausted for #{url}" if attempt == max_attempts

    delay = retry_delay(response, attempt)
    warn "retrying host=#{URI(url).host} attempt=#{attempt} delay=#{delay.round(2)}"
    sleep(delay)
  end
end

O Retry-After campo também pode conter uma data HTTP; por isso, analise ambas as formas descritas pela Semântica HTTP (RFC 9110). Limite o atraso e o número de tentativas, adicione jitter e mantenha um ritmo de base mesmo quando as solicitações forem bem-sucedidas. A concorrência nunca deve ultrapassar os limites publicados de um site.

Registe os URLs com falha num ficheiro dedicado ou numa tabela de tarefas com data e hora, estado, contagem de tentativas e classe de erro. Ocultem os valores das consultas e os cabeçalhos. Isto produz registos úteis sem os transformar num arquivo de credenciais.

Valide os registos e teste os seletores com fixtures guardados

Guarde HTML representativo em test/fixtures, incluindo uma página normal, um campo opcional em falta e uma estrutura alterada intencionalmente. Os testes são então executados localmente sem solicitar repetidamente o destino. Atualize os fixtures deliberadamente quando a fonte mudar e analise as diferenças antes de aceitar novos seletores.

require "minitest/autorun"
require_relative "../scraper"

class ParserTest < Minitest::Test
  PAGE_URL = "https://example.test/catalog"

  def setup
    html = File.read("test/fixtures/catalog.html", encoding: "UTF-8")
    @records = prepare_records(parse_records(html, page_url: PAGE_URL))
  end

  def test_extracts_the_contract
    assert_equal 2, @records.length
    assert_equal %i[id price_cents price_text title url],
                 @records.first.keys.sort
    assert @records.all? { |record| record[:url].start_with?("https://") }
  end

  def test_empty_selector_result_is_an_error
    assert_raises(ParseError) do
      parse_records("<html><body></body></html>", page_url: PAGE_URL)
    end
  end
end

Teste também a normalização de URLs, IDs duplicados, preços inválidos e nós opcionais ausentes como casos separados. Inclua o URL da página e o nome do fixture nas falhas, para que seja fácil localizar o contrato quebrado. O comportamento importante não é meramente «o método devolveu um array». Verifique o número de registos e os campos obrigatórios, e faça com que um resultado com zero registos leve à falha da tarefa. Uma regressão do seletor deve surgir como um alerta, e não como um ficheiro CSV vazio que o código a jusante confunda com dados válidos.

Lide com páginas baseadas em JavaScript sem abusar do navegador

A automação do navegador pode resolver problemas de renderização e interação, mas acrescenta o tempo de arranque do processo, o consumo de memória, as atualizações do navegador, as preocupações com os controladores e mais falhas dependentes do tempo. Também aumenta a superfície de segurança, uma vez que os cookies, as transferências e as capturas de ecrã podem conter informações confidenciais. Em «Web Scraping with Ruby», trate um navegador sem interface gráfica como um caminho de escalonamento após o diagnóstico da localização dos dados, e não como um substituto do mesmo. É útil consultar um guia introdutório à arquitetura de navegadores sem interface gráfica antes de operar muitas sessões simultâneas.

Confirme se o JavaScript é, de facto, o obstáculo

Primeiro, procure na resposta bruta por um registo conhecido. Em seguida, inspecione as tags de script em busca de estado incorporado e observe os pedidos em segundo plano autorizados no DevTools. Se uma resposta JSON contiver os dados e a sua utilização for permitida, solicitar essa resposta é normalmente mais simples do que reproduzir interações visuais.

Quando se extrai JavaScript com Ruby, esta verificação elimina frequentemente a necessidade de renderização. O uso de um navegador justifica-se quando a execução da página gera a única representação acessível, quando uma interação necessária altera o estado ou quando um fluxo autenticado depende do comportamento do navegador. Não utilize automação para contornar controlos de acesso. Confirme que o conteúdo e a interação estão dentro das suas permissões antes de escrever o plano alternativo.

Utilize o Selenium e o Capybara com tempos de espera e limpeza

O Selenium controla o navegador, enquanto o Capybara fornece funções de localização de nível superior e comportamentos de espera. Crie uma sessão por tarefa isolada, aguarde por uma condição significativa na página e feche sempre a sessão. As configurações modernas do Selenium podem selecionar automaticamente um driver compatível, mas esse comportamento depende da versão do Selenium, do navegador, do sistema operativo e do ambiente de implementação. Verifique-o em todas as plataformas suportadas, em vez de assumir que a instalação é manual ou que a gestão é automática.

require "capybara"
require "capybara/dsl"
require "selenium-webdriver"

Capybara.register_driver :chrome_headless do |app|
  options = Selenium::WebDriver::Chrome::Options.new
  options.add_argument("--headless")
  options.add_argument("--window-size=1440,1200")

  Capybara::Selenium::Driver.new(
    app, browser: :chrome, options: options
  )
end

Capybara.default_max_wait_time = 8

def fetch_rendered_records(url)
  session = Capybara::Session.new(:chrome_headless)

  begin
    session.visit(url)

    if session.has_button?("Accept", wait: 2)
      session.click_button("Accept")
    end

    session.find("#catalog", wait: 8)

    session.all("#catalog .product-card", minimum: 1).map do |card|
      link = card.find("a.product-link")

      {
        id: card["data-product-id"],
        title: card.find(".product-title").text.strip,
        price_text: card.find(".product-price").text.strip,
        price_cents: nil,
        url: URI.join(session.current_url, link[:href]).to_s
      }
    end
  rescue StandardError
    session.save_screenshot("output/browser-failure.png")
    raise
  ensure
    session.quit
  end
end

Os localizadores do Capybara verificam repetidamente até que uma condição seja satisfeita ou o tempo de espera configurado expire. Dê preferência a esse comportamento, ou às estratégias de espera documentadas do Selenium, em vez de chamadas fixas sleep . Uma pausa forçada é aceitável como teste temporário de depuração, mas não como lógica de sincronização.

O tratamento de pop-ups deve ser restrito e opcional, uma vez que o texto dos botões e os fluxos de consentimento variam consoante o site e a região. Guarde uma captura de ecrã juntamente com o URL atual quando a extração pelo navegador falhar, mas evite incluir informações confidenciais nos nomes dos ficheiros e nas capturas de ecrã, sempre que possível. Após a extração, passe estes hashes pelo mesmo prepare_records e write_csv métodos utilizados pelo caminho estático. Esse esquema partilhado torna o web scraping do Selenium Ruby um recurso de fallback de transporte, em vez de um segundo pipeline de dados.

Dimensionar e operar o scraper de forma responsável

Escalar significa manter um resultado correto sem sobrecarregar o alvo ou o seu próprio tempo de execução. Para o Web Scraping com Ruby, um pequeno conjunto de trabalhadores, limites explícitos de pedidos e falhas observáveis são mais seguros do que iniciar um fio por URL. Defina um limite de pedidos e um limite de trabalhadores para que entradas incorretas não possam gerar tráfego ilimitado. Comece com um trabalhador, avalie e, só depois, aumente o limite quando tanto as restrições do alvo como a sua taxa de erro o permitirem.

Limite a concorrência e mantenha o transporte separado

Utilize uma fila e um número fixo de trabalhadores. Cada trabalhador deve criar a sua própria sessão de navegador se for necessária renderização, pois partilhar um controlador entre threads cria riscos de estado e de limpeza. Para a recolha HTTP, verifique se o adaptador Faraday e o objeto de ligação selecionados são seguros para o seu modelo de simultaneidade ou crie uma ligação por trabalhador.

def scrape(url, transport:)
  html = transport.call(url)
  parse_records(html, page_url: url)
end

jobs = Queue.new
urls.each { |url| jobs << url }

worker_count = [Integer(ENV.fetch("SCRAPER_WORKERS", "3")), urls.length].min
mutex = Mutex.new
records = []

workers = Array.new(worker_count) do
  Thread.new do
    while (url = jobs.pop(true) rescue nil)
      batch = scrape(url, transport: method(:fetch_with_retry))
      mutex.synchronize { records.concat(batch) }
    end
  end
end

workers.each(&:join)

O limite de trabalhadores é um teto máximo, não uma meta de desempenho. Adicione um controlo de ritmo por anfitrião e interrompa ou reduza a concorrência quando 429 as respostas, a latência ou as falhas aumentarem. Manter o transport funções separadas significa que pode substituir pedidos diretos por um proxy aprovado ou uma camada de pedidos gerida sem alterar a extração, validação ou lógica CSV do Nokogiri. Isto permite o web scraping sem ser bloqueado de forma mais eficaz do que tratar o paralelismo como velocidade gratuita.

Proteja as credenciais e respeite as restrições do site

Carregue segredos a partir de variáveis de ambiente. Dê preferência a um cabeçalho de autorização quando o serviço o suportar; se as credenciais tiverem de ser parâmetros de consulta, deixe que o cliente as codifique. Nunca registe segredos, cabeçalhos completos ou URLs que contenham credenciais.

api_key = ENV.fetch("WEBSCRAPINGAPI_API_KEY")
target_url = ENV.fetch("TARGET_URL")

client = Faraday.new(url: "https://api.webscrapingapi.com")

response = client.get("/v2", {
  api_key: api_key,
  url: target_url,
  render_js: 0
})

raise "HTTP #{response.status}" unless response.success?

html = response.body

A permissão operacional é mais abrangente do que a capacidade técnica de um analisador. Analise os termos aplicáveis, os controlos de acesso, os limites de taxa, as obrigações de privacidade e a sensibilidade dos campos recolhidos. O Protocolo de Exclusão de Robôs define diretivas padronizadas para rastreadores, mas robots.txt não constitui uma concessão universal de permissão nem uma análise jurídica completa. Recorra a um quadro de conformidade jurídica atualizado em matéria de web scraping e a aconselhamento jurídico qualificado para decisões que envolvam riscos significativos.

Resolva problemas com base nos sintomas

Sintoma

Causa provável

Primeira solução

Os seletores devolvem zero cartões

Alterações na marcação ou resposta incorreta

Compare o HTML guardado com o fixture

O HTML corresponde a uma página de início de sessão ou de desafio

Estado de acesso inesperado

Inspecione o estado, os cabeçalhos, os cookies e as permissões

Repetido 403 ou 429

Os pedidos são recusados ou demasiado agressivos

Interrompa, analise as restrições e, em seguida, reduza a frequência

Timeouts frequentes

Lentidão da rede ou excesso de simultaneidade

Reduza o número de trabalhadores e verifique o tipo de tempo limite

Erro do controlador do navegador

Incompatibilidade entre navegador, controlador ou plataforma

Verifique a configuração do Selenium nesse host

Texto ilegível

Suposição de descodificação errada

Verifique Content-Type, o conjunto de caracteres e os bytes brutos

Linhas duplicadas

Chave instável ou entradas sobrepostas

Utilizar o ID de origem ou o URL canónico

Campos subitamente em falta

Regressão do seletor ou renderização parcial

Falha na validação e recolha de provas

Pontos-chave

  • Comece todos os projetos de Web Scraping com Ruby localizando os dados em HTML bruto, no estado incorporado, em respostas em segundo plano ou no DOM renderizado antes de escolher uma biblioteca, e documente por que razão essa fonte é permitida.
  • Mantenha o transporte, a análise, a normalização, a validação e a exportação separados, para que uma alteração na camada de acesso não reescreva o pipeline nem altere o contrato de registo em percursos estáticos e do navegador.
  • Trate resultados de seletores vazios, registos rejeitados, novas tentativas, duplicados e contagens de saída como resultados observáveis da tarefa, e não como detalhes de depuração incidentais, e persista essas métricas em cada saída.
  • Utilize a automação do navegador apenas para a renderização ou interação necessárias, com esperas baseadas em condições, sessões isoladas, capturas de ecrã de falhas, limpeza garantida e limites de credenciais por sessão.
  • Limite a concorrência, regule o ritmo dos pedidos, proteja os segredos e analise as restrições do site antes de aumentar o volume; em seguida, reduza o limite quando os erros ou a latência aumentarem, antes de adicionar trabalhadores, com uma condição de paragem definida.

Perguntas frequentes

Preciso do Ruby on Rails para criar um scraper web?

Não. Um scraper pode ser um projeto simples em Ruby gerido com o Bundler e executado a partir da linha de comandos, de um agendador ou de um trabalhador em segundo plano. O Rails só é útil se a tarefa de recolha fizer parte de uma aplicação existente que necessite da sua base de dados, fila de tarefas, configuração ou interface de administração. Para um script específico, o Rails acrescenta custos de arranque e dependências sem melhorar a extração. Um script autónomo também é mais fácil de agendar sem ter de iniciar uma estrutura de aplicação.

Devo usar o Faraday ou o HTTParty para pedidos HTTP em Ruby?

Escolha o Faraday quando quiser adaptadores, middleware e um objeto de ligação explícito que possa evoluir com novas tentativas, instrumentação ou stubs de teste. Escolha o HTTParty quando uma API compacta ao nível da classe se adequar a um pequeno script e a sua equipa já a conhecer. Nenhum dos clientes torna o scraping fiável automaticamente. Os tempos limite, o tratamento de estados, os cabeçalhos, o ritmo e os testes são mais importantes do que a sintaxe superficial. Se ambos se adequarem, opte pelo cliente que a sua equipa consiga instrumentar e simular de forma consistente.

O Nokogiri consegue executar JavaScript, ou preciso do Selenium?

O Nokogiri não consegue executar JavaScript. Ele analisa a cadeia de caracteres HTML ou XML que fornecer. Só precisa do Selenium ou de outro motor de navegador quando os dados permitidos forem produzidos exclusivamente após a execução ou interação do navegador. Antes de adicionar um, inspecione o JSON incorporado e as respostas de rede autorizadas, pois essas fontes podem expor os mesmos registos com menos demoras e complexidade de infraestrutura. Mantenha a captura do navegador por trás de uma interface para que os testes do analisador possam continuar a utilizar fixtures estáticos.

Qual é o lugar do Kimurai numa stack moderna de scraping em Ruby?

O Kimurai encaixa-se como uma estrutura de nível superior a ser considerada quando um script se transforma num rastreador com vários tipos de páginas, filas, integração com o navegador e convenções de ciclo de vida. Para um pequeno trabalho de extração, o código direto do Faraday e do Nokogiri é mais fácil de inspecionar e manter. A sua conveniência ao nível da estrutura é mais importante quando a navegação e a orquestração, em vez da análise, dominam a manutenção. Confirme a compatibilidade e o comportamento atuais do Kimurai com base na sua documentação antes de o selecionar para um novo projeto.

Como posso testar um scraper em Ruby sem solicitar repetidamente o site ativo?

Utilize fixtures HTML guardados para testes de análise e, em seguida, simule a interface HTTP para o comportamento das solicitações. Bibliotecas como a WebMock podem devolver estados, cabeçalhos e corpos controlados, enquanto ferramentas de respostas gravadas podem suportar fixtures de integração revistos. Mantenha um pequeno número de verificações programadas no site ativo para detetar desvios, mas torne os testes unitários de rotina determinísticos, rápidos e independentes da disponibilidade do alvo. Revise as atualizações dos fixtures tal como faz com o código, para que uma página alterada não possa redefinir silenciosamente o contrato de dados esperado.

Conclusão

A lição central do «Web Scraping with Ruby» é que um scraper fiável começa com o diagnóstico, não com uma lista de gems. Verifique se o registo existe na resposta original, no estado incorporado, numa resposta de fundo permitida ou apenas no DOM renderizado. Para a maioria das páginas estáticas, o Faraday e o Nokogiri oferecem o caminho mais simples desde a resposta HTTP até um CSV normalizado e validado.

Os ganhos em termos de facilidade de manutenção provêm dos limites em torno desse caminho. A recuperação de dados deve impor tempos de espera e verificações de resposta. A análise deve operar sobre o HTML fornecido e produzir um esquema estável. A normalização deve preservar as evidências brutas, a validação deve rejeitar linhas incompletas e os testes de fixtures devem tornar evidente qualquer desvio nos seletores. As tentativas de repetição, o ritmo de execução, a concorrência limitada e os registos editados transformam então o script numa tarefa repetível, em vez de um ciclo otimista. Antes de o agendar, defina contagens de resultados aceitáveis e limiares de falha, para que a monitorização possa distinguir uma página legitimamente vazia de uma recolha com erros. Limites pequenos e explícitos são mais fáceis de monitorizar em produção e mais seguros de alterar do que valores predefinidos ocultos.

Utilize o Selenium e o Capybara apenas quando o JavaScript ou a interação forem verdadeiramente necessários e, quando o fizer, reutilize o mesmo esquema a jusante. Se falhas de acesso, CAPTCHAs, rotação de proxies ou manutenção na camada de pedidos se tornarem o gargalo, considere se a API Scraper da WebScrapingAPI se adequa como um transporte de HTML bruto gerido para as páginas autorizadas que recolhe. Confirme a adequação atual do produto e, em seguida, mantenha o seu código de análise em Ruby e CSV intacto. Trata-se de uma escalada prática, pois altera o transporte sem descartar o scraper que já testou.

Sobre o autor

Raluca Penciuc, Desenvolvedor Full-Stack @ WebScrapingAPI

Raluca Penciuc

Desenvolvedor Full-Stack

Raluca Penciuc é programadora Full Stack na WebScrapingAPI, onde desenvolve scrapers, aperfeiçoa estratégias de evasão e procura formas fiáveis de reduzir a deteção nos sites-alvo.

Web Scraping com AWS Lambda: Guia para Python e Java 2026
Guias

Web Scraping com AWS Lambda: Guia para Python e Java 2026

TL;DR: Resumo: A extração de dados da Web com o AWS Lambda funciona melhor quando cada invocação é curta, delimitada e pode ser repetida de forma independente. Comece com HTTP direto, AWS SAM e S3 e, só depois, adicione SQS, contentores, renderização no navegador, proxies ou uma camada de recuperação gerida, apenas quando a carga de trabalho demonstrar que precisa deles.

Suciu Dan32 min read
Ler artigo
Como utilizar o GoSpider: rastrear, limpar URLs e extrair dados
Guias

Como utilizar o GoSpider: rastrear, limpar URLs e extrair dados

TL;DR: Resumo: O GoSpider é um rastreador de linha de comandos destinado a descobrir URLs, não um extrator completo de dados estruturados. Este guia sobre como utilizar o GoSpider mostra como realizar um rastreio limitado, gerir resultados de forma organizada, fazer a transferência de dados do Colly para CSV e seguir um percurso de diagnóstico para respostas 403 ou páginas que exijam renderização em JavaScript.

Suciu Dan23 min read
Ler artigo
Como fazer o Scrape Redfin: Guia Python para Dados de Propriedade
Guias

Como fazer o Scrape Redfin: Guia Python para Dados de Propriedade

TL;DR: A Redfin expõe pontos de extremidade de API ocultos que retornam JSON estruturado para listagens de propriedades, tornando possível ignorar totalmente a análise HTML frágil. Este guia orienta-o na construção de um scraper Python que extrai dados de aluguer e venda, pesquisa por localização, monitoriza novas listagens através de sitemaps XML e exporta resultados limpos para CSV ou JSON.

Suciu Dan14 min read
Ler artigo

Comece a construir

Pronto para expandir a sua recolha de dados?

Junte-se a mais de 2.000 empresas que utilizam a WebScrapingAPI para extrair dados da Web à escala empresarial, sem quaisquer custos de infraestrutura.