Por que o seu scraper Elixir é bloqueado e como proxies residenciais + navegador em nuvem resolvem isso.
Scraping and Proxy Management Expert
Principais Conclusões:
- Elixir é construído para scraping concorrente. O runtime BEAM entrelaça milhares de processos leves em um único nó, de modo que uma coleta que se expande por centenas de URLs é executada como um simples
Task.async_stream, em vez de um pool de threads que você precisa monitorar. - Req e HTTPoison fazem a busca, Floki analisa.
Reqé o cliente HTTP moderno com todas as funcionalidades;HTTPoisoné a opção consagrada e suportada por hackney;Flokitransforma HTML bruto em uma árvore que você consulta com seletores CSS. Juntos, eles cobrem qualquer página que envie marcação renderizada. - Crawly é o framework completo para crawling. Ele agenda solicitações através de trabalhadores, lida com paginação por meio de solicitações de acompanhamento, aplica middlewares para rotação de user-agents e opções de solicitação, e envia itens analisados para uma pipeline — no estilo Scrapy, mas no BEAM.
- Proxies residenciais Scrapeless roteiam as buscas. Um único host de proxy, porta e cabeçalho de autenticação básica se conectam diretamente nas
connect_optionsdeReq, no:proxy/:proxy_authdo HTTPoison, ou no middlewareRequestOptionsdo Crawly, proporcionando a cada solicitação um IP residencial e fixando a geografia de saída. - Alvos pesados em JS e anti-bots escalam para o Scrapeless Scraping Browser. Elixir não se conecta ao Protocolo de Ferramentas de Desenvolvedor do Chrome tão bem quanto o Node ou o Python, então o navegador em nuvem é acessado de duas maneiras: solicitações HTTP através de proxies residenciais Scrapeless para a maioria renderizada, e uma pequena chamada de navegador em nuvem para a minoria renderizada do lado do cliente.
- Gratuito para começar. Novas contas Scrapeless incluem tempo de execução gratuito do Scraping Browser — inscreva-se em app.scrapeless.com.
Introdução: Por que Elixir, e onde começa a fricção
Elixir roda no BEAM, a mesma máquina virtual que manteve os switches de telecomunicações Erlang online por décadas. Sua característica definidora para scraping é a concorrência barata: gerar dez mil processos é rotina, cada um isolado, cada um capaz de manter uma única solicitação HTTP em andamento sem bloquear os outros. Uma coleta que precisaria de um framework assíncrono e ajuste cuidadoso de pool em outra linguagem é um Task.async_stream com um limite de max_concurrency no Elixir.
A história das bibliotecas é madura. Req e HTTPoison buscam páginas, Floki as analisa com seletores CSS, e Crawly envolve toda a operação — agendamento, desduplicação, paginação, e pipelines de itens — em um framework no estilo Scrapy que ainda se sente como Elixir idiomático. Para catálogos estáticos, sitemaps e páginas renderizadas no servidor, essa pilha é completa por si só.
Duas coisas quebram isso. Primeiro, reputação de IP: um endereço de data center limpo é sinalizado no momento em que um alvo executa até mesmo um gerenciador de bots básico, e nenhum esforço de ajuste de cabeçalho conserta um IP de saída bloqueado. Segundo, renderização do lado do cliente: um aplicativo de página única retorna HTTP 200 com um <div id="app"> vazio, e Floki analisa exatamente o que chegou — nada. A página parece cheia em um navegador e vazia para o scraper.
Este guia constrói a pilha do Elixir em camadas. A camada HTTP utiliza Req, HTTPoison e Crawly roteados por proxies residenciais Scrapeless em mais de 195 países. A camada de JS renderizado escala para o Scrapeless Scraping Browser, acessado do Elixir sem pedir que o BEAM se conecte diretamente ao CDP. Para a camada de proxy residencial através da qual essas buscas são roteadas, veja O que é um Proxy SSL?.
O Que Você Pode Construir
O padrão de duas camadas — bibliotecas Elixir na frente, Scrapeless por trás da escalada — cobre a maioria dos trabalhos que derrotam um scraper HTTP simples:
- Coletas de catálogo concorrentes. Sitemaps, arquivos de artigos, listagens de produtos —
Task.async_streamse expande pelo conjunto de URLs com um número limitado de trabalhadores e analisa cada página com Floki. - Monitoramento agendado com Crawly. Defina uma aranha uma vez, deixe-a percorrer listagens em uma programação e envie itens analisados para uma pipeline de validação e armazenamento.
- Snapshots geográficos específicos. Fixe o país do proxy Scrapeless para que preços, disponibilidade e barreiras de consentimento retornem o que um usuário local vê, e não o que o IP do seu servidor resolve.
- Extração resiliente atrás de gerenciadores de bots. Roteie a busca através de uma saída residencial para que um IP doméstico comum, e não um intervalo de data center, faça a solicitação.
- Ingestão RAG e LLM. Renderize páginas de editoras e documentações para texto limpo, e depois envie o conteúdo extraído para uma pipeline de embedding.
- Páginas SPA e de rolagem infinita. Escale a minoria renderizada do lado do cliente para o Scrapeless Scraping Browser, que executa o JavaScript do lado da nuvem antes de você analisar o resultado.
Por que emparelhar Elixir com Scrapeless
Elixir oferece concorrência, análise e uma estrutura de rastreamento; o Scrapeless Scraping Browser fornece a infraestrutura de saída e renderização que um cliente HTTP do lado do servidor não pode. Os dois se encaixam porque a transferência é um proxy HTTP padrão de um lado e um endpoint de navegador em nuvem documentado do outro.
- Proxies residenciais em mais de 195 países. Expostos como um único host de proxy, porta e credencial de autenticação básica — se encaixa diretamente em
Req,HTTPoisonou middlewareRequestOptionsdo Crawly. - Geolocalização por solicitação. Um código de país no nome de usuário do proxy controla a geografia de saída sem necessidade de handshake adicional, assim o mesmo código obtém visualizações dos EUA, GB, DE ou JP trocando um segmento.
- Navegador em nuvem com anti-detecção. Para páginas renderizadas no lado do cliente, o Scrapeless Scraping Browser executa um Chromium desenvolvido internamente com renderização completa de JavaScript do lado da nuvem e randomização de impressão digital por sessão, então SPAs e painéis carregados de forma preguiçosa se hidratam antes da extração.
- Uma chave API para ambos os níveis. Proxies residenciais e Scraping Browser cobram contra a mesma conta Scrapeless, assim o nível HTTP e o nível renderizado compartilham uma credencial.
- Opção de sessão fixa. Mantenha o mesmo IP residencial durante uma travessia de múltiplos passos quando um fluxo precisa de continuidade, ou faça rotação por solicitação para o resto.
O tempo de execução é gratuito para começar e escala com o uso — veja preços do Scrapeless para os níveis, e obtenha sua chave API no plano gratuito em app.scrapeless.com.
Pré-requisitos
- Elixir 1.16+ e Erlang/OTP 26+ — verifique com
elixir --version. - Uma conta Scrapeless e chave API — inscreva-se no plano gratuito em app.scrapeless.com, depois pegue sua chave em Configurações → Gerenciamento de Chave API.
- Credenciais de proxy residencial — visíveis no painel sob Proxies → Residenciais em app.scrapeless.com.
- Familiaridade básica com
mix, seletores CSS e o terminal.
Instalar: configure o projeto mix e as dependências
Crie um novo projeto e adicione as bibliotecas de raspagem. mix new estrutura a estrutura; as quatro dependências cobrem busca (req, httpoison), análise (floki) e a estrutura de rastreamento completa (crawly):
bash
mix new elixir_scraper --sup
cd elixir_scraper
Adicione as dependências a mix.exs:
elixir
# mix.exs
defp deps do
[
{:req, "~> 0.5"}, # cliente HTTP moderno (Finch/Mint por trás do palco)
{:httpoison, "~> 2.2"}, # cliente HTTP baseado em hackney, opção duradoura
{:floki, "~> 0.36"}, # analisador HTML com consultas de seletores CSS
{:crawly, "~> 0.17"} # estrutura de rastreamento completa, estilo Scrapy
]
end
Então, obtenha as dependências:
bash
mix deps.get
Você não precisa das quatro em um projeto real — req mais floki é o par mínimo de busca e análise. O guia mostra cada um para que você possa escolher o cliente que se encaixa em sua pilha.
Configurar: armazene suas credenciais Scrapeless
Exporte sua chave API e credenciais de proxy residencial como variáveis de ambiente para que permaneçam fora do controle de versão. No painel sob Proxies → Residenciais, clique em Gerar e o painel imprime uma string de conexão delimitada por dois-pontos na forma <GATEWAY>:<PORT>:<CHANNEL_ID>-proxy-country_US-r_10m-s_<SESSION_ID>:<PASSWORD>:
bash
export SCRAPELESS_API_KEY="seu_token_api_aqui"
export SCRAPELESS_CHANNEL_ID="seu_id_de_canal" # impresso no início do nome de usuário
export SCRAPELESS_PROXY_PASS="sua_senha_do_canal"
export SCRAPELESS_PROXY_GATEWAY="gw-us.scrapeless.io" # veja gateways regionais abaixo
Gateways regionais: gw-us.scrapeless.io (Américas), gw-eu.scrapeless.io (Europa), gw-ap.scrapeless.io (Ásia-Pacífico). Escolha o gateway mais próximo do seu tempo de execução para manter a latência de handshake baixa; o país de saída ainda é controlado pelo segmento de nome de usuário country_<CC>, independentemente de qual gateway você se conecte. A porta é 8789 para todos.
O nome de usuário de proxy residencial é construído a partir de quatro parâmetros:
<CHANNEL_ID>— seu identificador de canal (impresso no início do nome de usuário no painel).country_<CC>— pin de país como o código ISO de duas letras:country_US,country_GB,country_DE,country_JP, etc. (use o código mostrado na seleção de locais do painel).r_<duration>— intervalo de rotação de sessão fixa (por exemplo,r_10mmantém o mesmo IP por 10 minutos antes de rotacionar).s_<SESSION_ID>— identificador de sessão persistente; reutilize o mesmos_<id>para manter um IP em solicitações dentro da janela de rotação.
Remova os segmentos r_ e s_ para um IP residencial novo a cada solicitação; mantenha-os quando uma travessia paginada precisar do mesmo IP ao longo do processo.
Básico: buscar com Req através de um proxy residencial, analisar com Floki
Req roteia através de um proxy HTTP com a chave :connect_options, que é encaminhada para Finch e Mint por baixo. A autenticação do proxy vai em :proxy_headers como um único cabeçalho de autenticação Basic — Mint a mescla na solicitação CONNECT. O nome de usuário traz o código do país, então a linha do proxy seleciona a geografia de saída:
elixir
defmodule ElixirScraper.ReqClient do
@gateway System.get_env("SCRAPELESS_PROXY_GATEWAY") || "gw-us.scrapeless.io"
@port 8789
# Construa o nome de usuário do proxy residencial com o código do país incluso.
defp proxy_username(country) do
channel = System.fetch_env!("SCRAPELESS_CHANNEL_ID")
"#{channel}-proxy-country_#{country}"
end
defp proxy_auth_header(country) do
user = proxy_username(country)
pass = System.fetch_env!("SCRAPELESS_PROXY_PASS")
"Basic " <> Base.encode64("#{user}:#{pass}")
end
@doc "Busca uma URL através do egress residencial Scrapeless no `country`."
def fetch(url, country \\ "US") do
Req.get(url,
connect_options: [
proxy: {:http, @gateway, @port, []},
proxy_headers: [{"proxy-authorization", proxy_auth_header(country)}]
],
headers: [{"user-agent", "Mozilla/5.0 (compatible; ElixirScraper/1.0)"}]
)
end
end
Chame-o e passe o corpo diretamente para o Floki. Floki.parse_document/1 transforma a string HTML em uma árvore; Floki.find/2 consulta com seletores CSS; Floki.text/1 e Floki.attribute/2 extraem valores:
elixir
{:ok, resp} = ElixirScraper.ReqClient.fetch("https://books.toscrape.com/")
{:ok, document} = Floki.parse_document(resp.body)
titles =
document
|> Floki.find("article.product_pod h3 a")
|> Floki.attribute("title")
prices =
document
|> Floki.find("article.product_pod p.price_color")
|> Floki.text()
IO.inspect(Enum.zip(titles, prices), label: "primeira página")
Três coisas que isso fixa desde o início:
- O proxy é configurado por solicitação, não globalmente. Isso permite que um cliente faça requisições de diferentes países passando um argumento
countrydiferente. - O cabeçalho de autenticação Basic é a linha de suporte principal. Sem
proxy_headers, o túnel CONNECT para o gateway residencial é rejeitado por falta de credenciais. - Floki consulta a árvore analisada, não a string bruta. Sempre
parse_document/1primeiro, depoisfind/2— seletores são executados contra a árvore.
Avançado 1: a variação HTTPoison
HTTPoison é anterior ao Req e continua comum em bases de código existentes. É suportado pelo hackney, que expõe proxies através de duas opções de solicitação: :proxy como uma tupla {host, port} e :proxy_auth como uma tupla {user, password}. Sem codificação manual em Base64 — hackney constrói o cabeçalho:
elixir
defmodule ElixirScraper.HTTPoisonClient do
@gateway System.get_env("SCRAPELESS_PROXY_GATEWAY") || "gw-us.scrapeless.io"
@port 8789
defp proxy_username(country) do
channel = System.fetch_env!("SCRAPELESS_CHANNEL_ID")
"#{channel}-proxy-country_#{country}"
end
def fetch(url, country \\ "US") do
opts = [
proxy: {@gateway, @port},
proxy_auth: {proxy_username(country), System.fetch_env!("SCRAPELESS_PROXY_PASS")},
recv_timeout: 30_000
]
headers = [{"User-Agent", "Mozilla/5.0 (compatible; ElixirScraper/1.0)"}]
case HTTPoison.get(url, headers, opts) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} -> {:ok, body}
{:ok, %HTTPoison.Response{status_code: code}} -> {:error, {:http, code}}
{:error, reason} -> {:error, reason}
end
end
end
A parte de análise é idêntica — HTTPoison retorna uma string de corpo, e Floki faz o resto. Opte por Req para código novo (ele vem com decodificação JSON, redirecionamentos e pool de conexões embutidos) e HTTPoison quando você estiver expandindo um projeto que já é baseado nele.
Avançado 2: uma aranha Crawly com paginação e saída de proxy
Para qualquer coisa além de um punhado de URLs, Crawly substitui o loop feito à mão. Uma aranha declara suas URLs iniciais e um callback parse_item/1; Crawly agenda solicitações através de trabalhadores, segue as novas solicitações que o callback retorna (é assim que a paginação funciona) e empurra itens analisados por um pipeline.
Conecte o proxy através do middleware RequestOptions. Ele passa sua lista de palavras-chave diretamente para o buscador HTTPoison subjacente, então as mesmas opções :proxy e :proxy_auth da variante HTTPoison se aplicam a cada solicitação que a aranha faz:
elixir
# config/config.exs
import Config
config :crawly,
closespider_itemcount: 200,
concurrent_requests_per_domain: 3,
middlewares: [
Crawly.Middlewares.DomainFilter,
Crawly.Middlewares.UniqueRequest,
{Crawly.Middlewares.UserAgent,
elixir
user_agents: ["Mozilla/5.0 (compatible; ElixirScraper/1.0)"]},
{Crawly.Middlewares.RequestOptions,
[
proxy: {System.get_env("SCRAPELESS_PROXY_GATEWAY", "gw-us.scrapeless.io"), 8789},
proxy_auth:
{"#{System.fetch_env!("SCRAPELESS_CHANNEL_ID")}-proxy-country_US",
System.fetch_env!("SCRAPELESS_PROXY_PASS")},
recv_timeout: 30_000
]}
],
pipelines: [
Crawly.Pipelines.Validate,
{Crawly.Pipelines.DuplicatesFilter, item_id: :title},
Crawly.Pipelines.JSONEncoder,
{Crawly.Pipelines.WriteToFile, extension: "jl", folder: "./output"}
]
A aranha em si implementa três callbacks. parse_item/1 faz duas tarefas ao mesmo tempo: ela extrai os itens na página atual e constrói solicitações de acompanhamento para a próxima página — essa segunda lista é o que impulsiona a paginação:
elixir
defmodule BooksSpider do
use Crawly.Spider
@impl Crawly.Spider
def base_url, do: "https://books.toscrape.com/"
@impl Crawly.Spider
def init, do: [start_urls: ["https://books.toscrape.com/"]]
@impl Crawly.Spider
def parse_item(response) do
{:ok, document} = Floki.parse_document(response.body)
# Extrair um item por cartão de produto nesta página.
items =
document
|> Floki.find("article.product_pod")
|> Enum.map(fn card ->
%{
title: card |> Floki.find("h3 a") |> Floki.attribute("title") |> List.first(),
price: card |> Floki.find("p.price_color") |> Floki.text()
}
end)
# Construir a solicitação da próxima página: esta lista é como a Crawly pagina.
next_requests =
document
|> Floki.find("li.next a")
|> Floki.attribute("href")
|> Enum.map(fn href ->
href
|> Crawly.Utils.build_absolute_url(response.request_url)
|> Crawly.Utils.request_from_url()
end)
%Crawly.ParsedItem{items: items, requests: next_requests}
end
end
Execute a partir de iex -S mix:
elixir
Crawly.Engine.start_spider(BooksSpider)
Crawly percorre cada página seguindo o link li.next a que o callback retorna, escreve cada item validado e deduplicado em ./output/BooksSpider.jl, e para ao atingir closespider_itemcount. Cada solicitação passa pelo proxy residencial Scrapeless porque o middleware RequestOptions injetou as opções :proxy e :proxy_auth na solicitação antes que o fetcher fosse executado.
Obtenha sua chave de API no plano gratuito: app.scrapeless.com
Evitando bloqueios: saída residencial, pinagem geográfica e retroalimentação
Um scraper é bloqueado por razões previsíveis, e a maioria delas pode ser resolvida pela configuração que você já possui:
- Reputação do IP do datacenter. A faixa de IP de um servidor é o primeiro sinal que um gerenciador de bot verifica. Roteamento através de proxies residenciais Scrapeless faz com que a solicitação pareça uma conexão doméstica comum, que é a maior alavanca contra bloqueios de reputação de IP.
- Geografia de saída. As páginas controlam conteúdo por região — preços, estoque, muros de consentimento. Fixe o país com o segmento de nome de usuário
country_<CC>para que o resultado corresponda ao local que você pretende ler. - Concorrência que parece um ataque. Limite o paralelismo a ≤3 solicitações por host. Com
Task.async_stream, isso émax_concurrency: 3; com Crawly, éconcurrent_requests_per_domain: 3. Além disso, um pool em execução apertado é indistinguível de uma inundação. - Um user-agent padrão.
ReqeHTTPoisonenviam um UA padrão da biblioteca que é trivial de filtrar. Defina um user-agent de navegador realista (como os trechos acima fazem) ou gire uma lista através do middlewareUserAgentda Crawly. - Ritmo. Para loops que não são da Crawly, espaçar solicitações com um pequeno
Process.sleep/1entre lotes em vez de disparar todo o conjunto de uma vez. A Crawly controla o ritmo das solicitações para você através de seu agendador.
Nada disso resgata uma página cujo conteúdo chega via JavaScript após a primeira pintura — essa é a próxima seção.
Alvos pesados em JS e anti-bot: roteamento através do Navegador de Scraping Scrapeless
Req, HTTPoison e Crawly retornam todos os bytes que a origem envia. Para um aplicativo React, Vue ou Next.js, esses bytes são uma casca vazia mais uma tag de script — o conteúdo é renderizado no lado do cliente, e Floki analisa uma árvore vazia. Um cliente HTTP do lado do servidor não pode executar esse JavaScript; um navegador em nuvem pode.
Há duas maneiras de acessar o Navegador de Scraping Scrapeless a partir do Elixir, e elas se mapeiam nas duas metades de uma carga de trabalho real.
(a) Solicitações HTTP através de proxies residenciais Scrapeless — a maioria renderizada
A maioria das páginas na maioria dos sites envia HTML renderizado pelo servidor. Para essas, o nível de proxy residencial acima é a resposta completa: os clientes `Req` e `HTTPoison` já saem por meio de um IP residencial, o que elimina portas de reputação de IP e restrições geográficas sem precisar de um navegador. Mantenha este nível na maioria das páginas que retornam conteúdo diretamente — é o caminho mais barato, e a concorrência do Elixir o torna rápido.
### (b) Chamando o navegador em nuvem — a minoria renderizada no lado do cliente
O Elixir não controla o Protocolo Chrome DevTools tão limpidamente quanto o Node ou o Python, então para a minoria renderizada em JavaScript, o movimento idiomático é manter o Elixir como orquestrador e chamar o Scrapeless Scraping Browser através de um pequeno auxiliar de renderização. O Elixir inicia o auxiliar como um processo externo com `System.cmd/3`, o auxiliar se conecta ao ponto de extremidade WebSocket documentado do navegador em nuvem, executa a página e imprime o HTML renderizado de volta para o Elixir — que o analisa com Floki exatamente como antes.
O ponto de extremidade do navegador em nuvem é uma única URL WebSocket com sua chave de API e parâmetros de sessão como valores da string de consulta. Um renderizador Python mínimo (salvo como `render.py`) se conecta a ele com Playwright:
```python
# render.py — invocado pelo Elixir via System.cmd/3 para a minoria renderizada em JS.
import os
import sys
from urllib.parse import urlencode
from playwright.sync_api import sync_playwright
def scraping_browser_url(proxy_country="US", session_ttl=240):
params = urlencode({
"token": os.environ["SCRAPELESS_API_KEY"],
"sessionTTL": session_ttl,
"proxyCountry": proxy_country,
})
return f"wss://browser.scrapeless.com/api/v2/browser?{params}"
def render(url, country="US"):
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(scraping_browser_url(country))
context = browser.contexts[0] if browser.contexts else browser.new_context()
page = context.pages[0] if context.pages else context.new_page()
# Aqueça a homepage primeiro, depois navegue até a página de destino.
page.goto("https://quotes.toscrape.com/", wait_until="load")
page.goto(url, wait_until="networkidle")
html = page.content()
browser.close()
return html
if __name__ == "__main__":
sys.stdout.write(render(sys.argv[1], sys.argv[2] if len(sys.argv) > 2 else "US"))
A renderização ocorre do lado da nuvem no navegador anti-detecção da Scrapeless; a instalação local do Playwright é apenas o cliente do protocolo. Aqueçar a homepage antes da página de destino insere cookies e estado de navegação, o que produz uma renderização mais limpa em páginas que bloqueiam visitantes de primeira viagem.
Do Elixir, a chamada e a análise permanecem em uma única função. System.cmd/3 bloqueia o processo chamador até que o auxiliar retorne — é aceitável dentro de uma Task, uma vez que o BEAM mantém todos os outros processos em execução:
elixir
defmodule ElixirScraper.CloudBrowser do
@doc """
Renderiza um `url` pesado em JS através do Scrapeless Scraping Browser e
retorna o HTML pós-renderização para o Floki analisar.
"""
def render(url, country \\ "US") do
case System.cmd("python", ["render.py", url, country], stderr_to_stdout: true) do
{html, 0} -> {:ok, html}
{output, code} -> {:error, {:render_failed, code, output}}
end
end
def quotes(url) do
with {:ok, html} <- render(url),
{:ok, document} <- Floki.parse_document(html) do
texts = document |> Floki.find("span.text") |> Floki.text()
authors = document |> Floki.find("small.author") |> Floki.text()
{:ok, %{quotes: texts, authors: authors}}
end
end
end
O antes/depois é o ponto inteiro. Um simples Req.get em https://quotes.toscrape.com/js/ retorna 0 elementos de citação, porque o HTTP não pode executar o JavaScript da página. A mesma URL através de ElixirScraper.CloudBrowser.render/2 retorna o DOM totalmente renderizado com todas as 10 citações, porque o navegador em nuvem executou o JavaScript primeiro. Esse é o comportamento da plataforma, não um truque de ajuste.
Para um pipeline em camadas, busque com Req primeiro, conte os elementos que você espera e escale apenas as páginas que voltam vazias para CloudBrowser.render/2. O Task.async_stream do Elixir executa a camada HTTP em ampla escala e a camada do navegador em escala estreita, uma vez que as sessões do navegador em nuvem são mais escassas do que as solicitações HTTP — mantenha a camada do navegador com max_concurrency: 3.
Solução de Problemas
| Sintoma | Causa provável | Solução |
|---|---|---|
Floki retorna [] para um seletor que existe no navegador |
A página renderiza conteúdo no lado do cliente; o HTTP retornou um shell de aplicativo | Escale a URL para CloudBrowser.render/2; analise o HTML renderizado |
| Proxy CONNECT rejeitado / 407 do gateway | Credenciais de autenticação básica ausentes ou incorretas | Confirme se proxy_headers (Req) ou :proxy_auth (HTTPoison) transportam o nome de usuário e a senha do canal |
Floki.parse_document retorna {:error, ...} |
O corpo não é HTML (API JSON, página de redirecionamento ou vazia) | Verifique resp.status; para endpoints JSON, decodifique o corpo em vez de analisá-lo como HTML |
Mesmo conteúdo independentemente de country_<CC> |
A página não varia por região, ou o segmento do país não foi atualizado | Verifique se o segmento do nome de usuário mudou; algumas páginas não são geo-bloqueadas |
| Acesso negado ou intersticial de desafio em vez de conteúdo | Saída de datacenter ou porta de primeira visita | Roteie através de saída residencial e aqueça a página inicial do site na mesma sessão antes da página alvo |
| Crawly para após uma página | parse_item/1 não retornou solicitações de acompanhamento |
Confirme se o seletor da próxima página coincide e se Crawly.Utils.request_from_url/1 envolve cada URL absoluta |
Erros de System.cmd com :enoent |
O executável python não está no PATH |
Use o caminho completo do interpretador ou invoque através de um shell que o resolva |
Uma nota sobre a deriva de seletores: quando um site alvo reorganiza sua marcação, suas chamadas Floki.find/2 silenciosamente retornam listas vazias em vez de gerar erros. Verifique novamente e aperte os seletores contra o novo DOM sempre que um scraper que anteriormente funcionava começar a retornar vazios — trate uma lista vazia como um sinal para inspecionar, não como um resultado normal.
Conclusão: escale seu pipeline de raspagem em Elixir
O padrão de Elixir se reduz a quatro movimentos. Busque com Req ou HTTPoison (ou deixe o Crawly agendar as buscas) através de proxies residenciais da Scrapeless; analise com os seletores CSS do Floki; pagine retornando solicitações de acompanhamento de parse_item/1; e escale a minoria renderizada em JavaScript para o Scrapeless Scraping Browser, acessada de Elixir como uma chamada externa de renderização em vez de solicitar que o BEAM fale diretamente com o CDP.
A partir daqui, a mesma estrutura se compõe em sistemas maiores. Para a camada de proxies residenciais em profundidade, veja O que é um proxy SSL?. Antes de enviar: defina country_<CC> para páginas com restrição geográfica, mantenha a concorrência em ≤3 por host, configure um user-agent realista, trate seletores ausentes como anuláveis, e mantenha a camada HTTP ampla e a camada de navegador em nuvem estreita.
Pronto para construir seu pipeline de dados alimentado por IA?
Junte-se à nossa comunidade para reivindicar um plano gratuito e conectar-se com desenvolvedores que constroem pipelines de raspagem em Elixir: Discord · Telegram.
Inscreva-se em app.scrapeless.com para um runtime gratuito do Scraping Browser e adapte os padrões acima para as páginas e regiões que seu pipeline precisa. Referência completa em docs.scrapeless.com.
FAQ
P: A raspagem da web com Elixir é legal?
A linguagem é irrelevante para a legalidade. Raspagem de dados publicamente disponíveis é geralmente permitida em muitas jurisdições, mas a lei não é uniforme: revise os Termos de Serviço de cada site, evite coletar dados pessoais ou protegidos por direitos autorais que você não tem direito, e lembre-se que as regras variam por jurisdição. Quando em dúvida, procure aconselhamento jurídico para seu caso específico. A Scrapeless acessa dados publicamente disponíveis apenas.
P: Preciso de um proxy para raspagem em Elixir?
Para qualquer coisa em grande escala, sim. O IP datacenter de um servidor é uma das primeiras coisas que um gerente de bot sinaliza, e a saída residencial reduz bastante esses bloqueios. Um proxy também é necessário sempre que uma página bloqueia conteúdo por região. A Scrapeless fornece proxies residenciais em mais de 195 países — defina o segmento do nome de usuário country_<CC> e roteie Req, HTTPoison ou Crawly através do gateway, para que você não precise encontrar e rotacionar IPs sozinho.
P: Req ou HTTPoison — qual devo usar?
Para novo código, Req: ele oferece decodificação JSON, seguimento de redirecionamento e pooling de conexão através do Finch, com menos boilerplate. Escolha HTTPoison quando você estiver estendendo um projeto já construído sobre ele, ou quando quiser as opções de tuplas :proxy / :proxy_auth do hackney diretamente. Ambos analisam de forma idêntica com Floki, então a escolha é sobre a ergonomia do cliente, não a raspagem.
P: Quando eu preciso do Scrapeless Scraping Browser em vez do HTTP simples?
Quando o HTML retornado pelo seu cliente é um shell de aplicativo JavaScript sem conteúdo. O sinal: um seletor que você pode ver em um navegador real retorna uma lista vazia do Floki. Aquela página é renderizada do lado do cliente, então um cliente HTTP do lado do servidor nunca vê os dados. Roteie essas URLs através do navegador em nuvem, que executa o JavaScript antes de você analisar — e mantenha o HTTP simples nas páginas que já retornam conteúdo.
P: Por que chamar um helper de renderização em vez de controlar o navegador diretamente do Elixir?
O Elixir não possui um driver de protocolo do Chrome DevTools de primeira classe como o Node e o Python, então o padrão mais limpo mantém o Elixir como o orquestrador e delega a renderização a um pequeno helper externo invocado com System.cmd/3. O Elixir ainda controla o loop de rastreamento, os limites de concorrência, a paginação e a análise com Floki; apenas a execução do JavaScript é transferida para o navegador em nuvem. Isso mantém o código Elixir idiomático e a integração a uma única chamada externa.
P: Quantas requisições simultâneas devo executar?
Mantenha em ≤3 por host. Com Task.async_stream, defina max_concurrency: 3; com o Crawly, defina concurrent_requests_per_domain: 3. Catálogos públicos toleram um pouco mais, origens protegidas contra bots querem menos, mas 3 é um padrão seguro que evita que o pool de requisições ativas pareça um ataque. Mantenha a camada do navegador em nuvem ainda mais restrita, uma vez que as sessões renderizadas são mais escassas do que as requisições HTTP.
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.



